2016-03-01 15:47:10 +00:00
/*
* * thingdef . cpp
* *
* * Actor definitions
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2002 - 2008 Christoph Oelckers
* * Copyright 2004 - 2008 Randy Heit
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* * 4. When not used as part of ZDoom or a ZDoom derivative , this code will be
* * covered by the terms of the GNU General Public License as published by
* * the Free Software Foundation ; either version 2 of the License , or ( at
* * your option ) any later version .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# include "gi.h"
# include "actor.h"
# include "r_defs.h"
# include "a_pickups.h"
# include "cmdlib.h"
# include "p_lnspec.h"
# include "decallib.h"
# include "p_local.h"
2017-01-18 17:46:24 +00:00
# include "a_weapons.h"
2016-03-01 15:47:10 +00:00
# include "p_conversation.h"
# include "v_text.h"
2017-02-08 11:24:08 +00:00
# include "backend/codegen.h"
2016-03-01 15:47:10 +00:00
# include "stats.h"
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void InitThingdef ( ) ;
// STATIC FUNCTION PROTOTYPES --------------------------------------------
2016-11-15 10:21:08 +00:00
static TMap < FState * , FScriptPosition > StateSourceLines ;
static FScriptPosition unknownstatesource ( " unknown file " , 0 ) ;
//==========================================================================
//
// Saves the state's source lines for error messages during postprocessing
//
//==========================================================================
void SaveStateSourceLines ( FState * firststate , TArray < FScriptPosition > & positions )
{
for ( unsigned i = 0 ; i < positions . Size ( ) ; i + + )
{
StateSourceLines [ firststate + i ] = positions [ i ] ;
}
}
FScriptPosition & GetStateSource ( FState * state )
{
auto check = StateSourceLines . CheckKey ( state ) ;
return check ? * check : unknownstatesource ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
2016-10-12 22:53:59 +00:00
// SetImplicitArgs
//
// Adds the parameters implied by the function flags.
2016-03-01 15:47:10 +00:00
//
//==========================================================================
2017-04-12 15:21:13 +00:00
void SetImplicitArgs ( TArray < PType * > * args , TArray < uint32_t > * argflags , TArray < FName > * argnames , PContainerType * cls , uint32_t funcflags , int useflags )
2016-03-01 15:47:10 +00:00
{
2016-10-12 22:53:59 +00:00
// Must be called before adding any other arguments.
2016-10-15 12:36:08 +00:00
assert ( args = = nullptr | | args - > Size ( ) = = 0 ) ;
assert ( argflags = = nullptr | | argflags - > Size ( ) = = 0 ) ;
2016-03-01 15:47:10 +00:00
2016-10-12 22:53:59 +00:00
if ( funcflags & VARF_Method )
2016-03-01 15:47:10 +00:00
{
2016-10-12 22:53:59 +00:00
// implied self pointer
2017-03-04 11:43:07 +00:00
if ( args ! = nullptr ) args - > Push ( NewPointer ( cls , ! ! ( funcflags & VARF_ReadOnly ) ) ) ;
2016-10-20 23:12:54 +00:00
if ( argflags ! = nullptr ) argflags - > Push ( VARF_Implicit | VARF_ReadOnly ) ;
2016-10-15 12:36:08 +00:00
if ( argnames ! = nullptr ) argnames - > Push ( NAME_self ) ;
2016-03-01 15:47:10 +00:00
}
2016-10-12 22:53:59 +00:00
if ( funcflags & VARF_Action )
2016-03-01 15:47:10 +00:00
{
2017-03-04 11:43:07 +00:00
assert ( ! ( funcflags & VARF_ReadOnly ) ) ;
2016-10-12 22:53:59 +00:00
// implied caller and callingstate pointers
2016-10-15 12:36:08 +00:00
if ( args ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-11-15 10:21:08 +00:00
// Special treatment for weapons and CustomInventory flagged functions: 'self' is not the defining class but the actual user of the item, so this pointer must be of type 'Actor'
if ( useflags & ( SUF_WEAPON | SUF_ITEM ) )
2016-10-15 12:36:08 +00:00
{
2016-10-15 15:40:27 +00:00
args - > Insert ( 0 , NewPointer ( RUNTIME_CLASS ( AActor ) ) ) ; // this must go in before the real pointer to the containing class.
2016-10-15 12:36:08 +00:00
}
2016-10-15 13:10:48 +00:00
else
2016-10-15 12:36:08 +00:00
{
2016-10-15 15:40:27 +00:00
args - > Push ( NewPointer ( cls ) ) ;
2016-10-15 12:36:08 +00:00
}
2016-11-19 00:23:56 +00:00
args - > Push ( NewPointer ( NewStruct ( " FStateParamInfo " , nullptr ) ) ) ;
2016-03-01 15:47:10 +00:00
}
2016-10-15 12:36:08 +00:00
if ( argflags ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-10-20 23:12:54 +00:00
argflags - > Push ( VARF_Implicit | VARF_ReadOnly ) ;
argflags - > Push ( VARF_Implicit | VARF_ReadOnly ) ;
2016-03-01 15:47:10 +00:00
}
2016-10-15 12:36:08 +00:00
if ( argnames ! = nullptr )
{
argnames - > Push ( NAME_invoker ) ;
argnames - > Push ( NAME_stateinfo ) ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-10-15 12:36:08 +00:00
//==========================================================================
//
// CreateAnonymousFunction
//
// Creates a function symbol for an anonymous function
// This contains actual info about the implied variables which is needed
// during code generation.
//
//==========================================================================
2017-04-12 15:21:13 +00:00
PFunction * CreateAnonymousFunction ( PContainerType * containingclass , PType * returntype , int flags )
2016-10-15 12:36:08 +00:00
{
TArray < PType * > rets ( 1 ) ;
TArray < PType * > args ;
TArray < uint32_t > argflags ;
TArray < FName > argnames ;
2016-11-15 10:21:08 +00:00
// Functions that only get flagged for actors do not need the additional two context parameters.
int fflags = ( flags & ( SUF_OVERLAY | SUF_WEAPON | SUF_ITEM ) ) ? VARF_Action | VARF_Method : VARF_Method ;
2017-02-18 04:27:28 +00:00
// [ZZ] give anonymous functions the scope of their class
// (just give them VARF_Play, whatever)
fflags | = VARF_Play ;
2016-10-15 12:36:08 +00:00
rets [ 0 ] = returntype ! = nullptr ? returntype : TypeError ; // Use TypeError as placeholder if we do not know the return type yet.
2016-11-15 10:21:08 +00:00
SetImplicitArgs ( & args , & argflags , & argnames , containingclass , fflags , flags ) ;
2016-10-15 12:36:08 +00:00
2017-04-14 11:31:58 +00:00
PFunction * sym = Create < PFunction > ( containingclass , NAME_None ) ; // anonymous functions do not have names.
2016-11-15 20:38:12 +00:00
sym - > AddVariant ( NewPrototype ( rets , args ) , argflags , argnames , nullptr , fflags , flags ) ;
2016-10-15 12:36:08 +00:00
return sym ;
}
2016-10-15 18:16:44 +00:00
//==========================================================================
//
// FindClassMemberFunction
//
// Looks for a name in a class's symbol table and outputs appropriate messages
//
//==========================================================================
2017-04-12 15:21:13 +00:00
PFunction * FindClassMemberFunction ( PContainerType * selfcls , PContainerType * funccls , FName name , FScriptPosition & sc , bool * error )
2016-10-15 18:16:44 +00:00
{
// Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead.
if ( name = = NAME_ACS_NamedExecuteWithResult ) return nullptr ;
PSymbolTable * symtable ;
auto symbol = selfcls - > Symbols . FindSymbolInTable ( name , symtable ) ;
auto funcsym = dyn_cast < PFunction > ( symbol ) ;
if ( symbol ! = nullptr )
{
2017-04-13 13:13:14 +00:00
auto cls_ctx = PType : : toClass ( funccls ) ;
auto cls_target = funcsym ? PType : : toClass ( funcsym - > OwningClass ) : nullptr ;
2016-10-15 18:16:44 +00:00
if ( funcsym = = nullptr )
{
sc . Message ( MSG_ERROR , " %s is not a member function of %s " , name . GetChars ( ) , selfcls - > TypeName . GetChars ( ) ) ;
}
2017-02-16 00:31:20 +00:00
else if ( ( funcsym - > Variants [ 0 ] . Flags & VARF_Private ) & & symtable ! = & funccls - > Symbols )
2016-10-15 18:16:44 +00:00
{
// private access is only allowed if the symbol table belongs to the class in which the current function is being defined.
sc . Message ( MSG_ERROR , " %s is declared private and not accessible " , symbol - > SymbolName . GetChars ( ) ) ;
}
2017-04-12 20:46:49 +00:00
else if ( ( funcsym - > Variants [ 0 ] . Flags & VARF_Protected ) & & symtable ! = & funccls - > Symbols & & ( ! cls_ctx | | ! cls_target | | ! cls_ctx - > Descriptor - > IsDescendantOf ( cls_target - > Descriptor ) ) )
2017-03-04 22:52:43 +00:00
{
sc . Message ( MSG_ERROR , " %s is declared protected and not accessible " , symbol - > SymbolName . GetChars ( ) ) ;
}
2016-10-15 18:16:44 +00:00
else if ( funcsym - > Variants [ 0 ] . Flags & VARF_Deprecated )
{
sc . Message ( MSG_WARNING , " Call to deprecated function %s " , symbol - > SymbolName . GetChars ( ) ) ;
}
}
// return nullptr if the name cannot be found in the symbol table so that the calling code can do other checks.
return funcsym ;
}
2016-10-26 12:04:49 +00:00
//==========================================================================
//
// CreateDamageFunction
//
// creates a damage function from the given expression
//
//==========================================================================
2017-03-05 16:58:55 +00:00
void CreateDamageFunction ( PNamespace * OutNamespace , const VersionInfo & ver , PClassActor * info , AActor * defaults , FxExpression * id , bool fromDecorate , int lumpnum )
2016-10-26 12:04:49 +00:00
{
if ( id = = nullptr )
{
defaults - > DamageFunc = nullptr ;
}
else
{
auto dmg = new FxReturnStatement ( new FxIntCast ( id , true ) , id - > ScriptPosition ) ;
2017-04-12 20:46:49 +00:00
auto funcsym = CreateAnonymousFunction ( info - > VMType , TypeSInt32 , 0 ) ;
2017-03-05 16:58:55 +00:00
defaults - > DamageFunc = FunctionBuildList . AddFunction ( OutNamespace , ver , funcsym , dmg , FStringf ( " %s.DamageFunction " , info - > TypeName . GetChars ( ) ) , fromDecorate , - 1 , 0 , lumpnum ) ;
2016-10-26 12:04:49 +00:00
}
}
2016-11-13 11:02:41 +00:00
//==========================================================================
//
// CheckForUnsafeStates
//
// Performs a quick analysis to find potentially bad states.
// This is not perfect because it cannot track jumps by function.
// For such cases a runtime check in the relevant places is also present.
//
//==========================================================================
2016-11-15 10:21:08 +00:00
static void CheckForUnsafeStates ( PClassActor * obj )
2016-11-13 11:02:41 +00:00
{
static ENamedName weaponstates [ ] = { NAME_Ready , NAME_Deselect , NAME_Select , NAME_Fire , NAME_AltFire , NAME_Hold , NAME_AltHold , NAME_Flash , NAME_AltFlash , NAME_None } ;
static ENamedName pickupstates [ ] = { NAME_Pickup , NAME_Drop , NAME_Use , NAME_None } ;
TMap < FState * , bool > checked ;
ENamedName * test ;
2017-02-08 14:47:22 +00:00
if ( obj - > IsDescendantOf ( NAME_Weapon ) )
2016-11-13 11:02:41 +00:00
{
2016-11-15 10:21:08 +00:00
if ( obj - > Size = = RUNTIME_CLASS ( AWeapon ) - > Size ) return ; // This class cannot have user variables.
2016-11-13 11:02:41 +00:00
test = weaponstates ;
}
2017-01-19 18:14:22 +00:00
else
2016-11-13 11:02:41 +00:00
{
2017-01-19 18:14:22 +00:00
auto citype = PClass : : FindActor ( NAME_CustomInventory ) ;
if ( obj - > IsDescendantOf ( citype ) )
{
if ( obj - > Size = = citype - > Size ) return ; // This class cannot have user variables.
test = pickupstates ;
}
else return ; // something else derived from AStateProvider. We do not know what this may be.
2016-11-13 11:02:41 +00:00
}
for ( ; * test ! = NAME_None ; test + + )
{
FState * state = obj - > FindState ( * test ) ;
while ( state ! = nullptr & & checked . CheckKey ( state ) = = nullptr ) // have we checked this state already. If yes, we can stop checking the current chain.
{
checked [ state ] = true ;
if ( state - > ActionFunc & & state - > ActionFunc - > Unsafe )
{
// If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash.
2017-04-11 19:48:41 +00:00
GetStateSource ( state ) . Message ( MSG_ERROR , TEXTCOLOR_RED " Unsafe state call in state %s which accesses user variables, reached by %s.%s. \n " ,
2017-04-13 07:17:11 +00:00
FState : : StaticGetStateName ( state ) . GetChars ( ) , obj - > TypeName . GetChars ( ) , FName ( * test ) . GetChars ( ) ) ;
2016-11-13 11:02:41 +00:00
}
state = state - > NextState ;
}
}
2016-11-15 10:21:08 +00:00
}
//==========================================================================
//
// CheckStates
//
// Checks if states link to ones with proper restrictions
// Checks that all base labels refer a string with proper restrictions.
// For these cases a runtime check in the relevant places is also present.
//
//==========================================================================
static void CheckLabel ( PClassActor * obj , FStateLabel * slb , int useflag , FName statename , const char * descript )
{
auto state = slb - > State ;
if ( state ! = nullptr )
{
if ( ! ( state - > UseFlags & useflag ) )
{
2017-04-11 19:48:41 +00:00
GetStateSource ( state ) . Message ( MSG_ERROR , TEXTCOLOR_RED " %s references state %s as %s state, but this state is not flagged for use as %s. \n " ,
2017-04-13 07:17:11 +00:00
obj - > TypeName . GetChars ( ) , FState : : StaticGetStateName ( state ) . GetChars ( ) , statename . GetChars ( ) , descript ) ;
2016-11-15 10:21:08 +00:00
}
}
if ( slb - > Children ! = nullptr )
{
for ( int i = 0 ; i < slb - > Children - > NumLabels ; i + + )
{
auto state = slb - > Children - > Labels [ i ] . State ;
CheckLabel ( obj , & slb - > Children - > Labels [ i ] , useflag , statename , descript ) ;
}
}
}
static void CheckStateLabels ( PClassActor * obj , ENamedName * test , int useflag , const char * descript )
{
2017-04-11 22:07:41 +00:00
FStateLabels * labels = obj - > GetStateLabels ( ) ;
2016-11-15 10:21:08 +00:00
for ( ; * test ! = NAME_None ; test + + )
{
auto label = labels - > FindLabel ( * test ) ;
if ( label ! = nullptr )
{
CheckLabel ( obj , label , useflag , * test , descript ) ;
}
}
}
static void CheckStates ( PClassActor * obj )
{
static ENamedName actorstates [ ] = { NAME_Spawn , NAME_See , NAME_Melee , NAME_Missile , NAME_Pain , NAME_Death , NAME_Wound , NAME_Raise , NAME_Yes , NAME_No , NAME_Greetings , NAME_None } ;
static ENamedName weaponstates [ ] = { NAME_Ready , NAME_Deselect , NAME_Select , NAME_Fire , NAME_AltFire , NAME_Hold , NAME_AltHold , NAME_Flash , NAME_AltFlash , NAME_None } ;
static ENamedName pickupstates [ ] = { NAME_Pickup , NAME_Drop , NAME_Use , NAME_None } ;
TMap < FState * , bool > checked ;
CheckStateLabels ( obj , actorstates , SUF_ACTOR , " actor sprites " ) ;
2017-02-08 14:47:22 +00:00
if ( obj - > IsDescendantOf ( NAME_Weapon ) )
2016-11-15 10:21:08 +00:00
{
CheckStateLabels ( obj , weaponstates , SUF_WEAPON , " weapon sprites " ) ;
}
2017-02-08 14:47:22 +00:00
else if ( obj - > IsDescendantOf ( NAME_CustomInventory ) )
2016-11-15 10:21:08 +00:00
{
CheckStateLabels ( obj , pickupstates , SUF_ITEM , " CustomInventory state chain " ) ;
}
2017-04-11 22:07:41 +00:00
for ( unsigned i = 0 ; i < obj - > GetStateCount ( ) ; i + + )
2016-11-15 10:21:08 +00:00
{
2017-04-11 22:07:41 +00:00
auto state = obj - > GetStates ( ) + i ;
2016-11-15 10:21:08 +00:00
if ( state - > NextState & & ( state - > UseFlags & state - > NextState - > UseFlags ) ! = state - > UseFlags )
{
2017-04-11 20:44:35 +00:00
GetStateSource ( state ) . Message ( MSG_ERROR , TEXTCOLOR_RED " State %s links to a state with incompatible restrictions. \n " ,
2017-04-13 07:17:11 +00:00
FState : : StaticGetStateName ( state ) . GetChars ( ) ) ;
2016-11-15 10:21:08 +00:00
}
}
2016-11-13 11:02:41 +00:00
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// LoadActors
//
// Called from FActor::StaticInit()
//
//==========================================================================
2016-10-07 23:05:27 +00:00
void ParseScripts ( ) ;
2016-10-12 22:53:59 +00:00
void ParseAllDecorate ( ) ;
2017-04-12 20:46:49 +00:00
void SynthesizeFlagFields ( ) ;
2016-03-01 15:47:10 +00:00
2016-11-30 23:05:23 +00:00
void LoadActors ( )
2016-03-01 15:47:10 +00:00
{
cycle_t timer ;
timer . Reset ( ) ; timer . Clock ( ) ;
2016-10-12 22:53:59 +00:00
FScriptPosition : : ResetErrorCounter ( ) ;
2016-03-01 15:47:10 +00:00
InitThingdef ( ) ;
2016-10-26 11:22:36 +00:00
FScriptPosition : : StrictErrors = true ;
2016-10-07 23:05:27 +00:00
ParseScripts ( ) ;
2016-10-15 10:15:25 +00:00
2016-10-26 11:22:36 +00:00
FScriptPosition : : StrictErrors = false ;
2016-10-12 22:53:59 +00:00
ParseAllDecorate ( ) ;
2017-04-12 20:46:49 +00:00
SynthesizeFlagFields ( ) ;
2016-10-12 22:53:59 +00:00
FunctionBuildList . Build ( ) ;
2016-10-12 18:42:41 +00:00
2016-03-01 15:47:10 +00:00
if ( FScriptPosition : : ErrorCounter > 0 )
{
I_Error ( " %d errors while parsing DECORATE scripts " , FScriptPosition : : ErrorCounter ) ;
}
2016-11-15 10:21:08 +00:00
FScriptPosition : : ResetErrorCounter ( ) ;
2016-10-12 22:53:59 +00:00
2016-11-30 23:05:23 +00:00
for ( int i = PClassActor : : AllActorClasses . Size ( ) - 1 ; i > = 0 ; i - - )
2016-10-12 22:53:59 +00:00
{
2016-11-30 18:48:52 +00:00
auto ti = PClassActor : : AllActorClasses [ i ] ;
2016-10-12 22:53:59 +00:00
if ( ti - > Size = = TentativeClass )
{
2017-04-12 20:46:49 +00:00
if ( ti - > bOptional )
2016-11-23 21:34:17 +00:00
{
Printf ( TEXTCOLOR_ORANGE " Class %s referenced but not defined \n " , ti - > TypeName . GetChars ( ) ) ;
FScriptPosition : : WarnCounter + + ;
2017-02-08 18:52:33 +00:00
// the class must be rendered harmless so that it won't cause problems.
ti - > ParentClass = RUNTIME_CLASS ( AActor ) ;
ti - > Size = sizeof ( AActor ) ;
2016-11-23 21:34:17 +00:00
}
else
{
Printf ( TEXTCOLOR_RED " Class %s referenced but not defined \n " , ti - > TypeName . GetChars ( ) ) ;
FScriptPosition : : ErrorCounter + + ;
}
2016-10-12 22:53:59 +00:00
continue ;
}
if ( GetDefaultByType ( ti ) = = nullptr )
{
Printf ( TEXTCOLOR_RED " No ActorInfo defined for class '%s' \n " , ti - > TypeName . GetChars ( ) ) ;
2016-11-15 10:21:08 +00:00
FScriptPosition : : ErrorCounter + + ;
2016-10-12 22:53:59 +00:00
continue ;
}
2016-11-13 11:02:41 +00:00
2016-11-15 10:21:08 +00:00
CheckStates ( ti ) ;
2016-11-30 23:05:23 +00:00
2016-11-13 11:02:41 +00:00
if ( ti - > bDecorateClass & & ti - > IsDescendantOf ( RUNTIME_CLASS ( AStateProvider ) ) )
{
// either a DECORATE based weapon or CustomInventory.
// These are subject to relaxed rules for user variables in states.
// Although there is a runtime check for bogus states, let's do a quick analysis if any of the known entry points
// hits an unsafe state. If we can find something here it can be handled wuth a compile error rather than a runtime error.
2016-11-15 10:21:08 +00:00
CheckForUnsafeStates ( ti ) ;
2016-11-13 11:02:41 +00:00
}
2016-11-30 23:05:23 +00:00
2016-12-26 23:32:54 +00:00
// ensure that all actor bouncers have PASSMOBJ set.
auto defaults = GetDefaultByType ( ti ) ;
if ( defaults - > BounceFlags & ( BOUNCE_Actors | BOUNCE_AllActors ) )
{
// PASSMOBJ is irrelevant for normal missiles, but not for bouncers.
defaults - > flags2 | = MF2_PASSMOBJ ;
}
2016-10-12 22:53:59 +00:00
}
2016-11-15 10:21:08 +00:00
if ( FScriptPosition : : ErrorCounter > 0 )
2016-10-12 22:53:59 +00:00
{
2016-11-15 10:21:08 +00:00
I_Error ( " %d errors during actor postprocessing " , FScriptPosition : : ErrorCounter ) ;
2016-10-12 22:53:59 +00:00
}
2016-03-01 15:47:10 +00:00
timer . Unclock ( ) ;
2016-11-15 10:21:08 +00:00
if ( ! batchrun ) Printf ( " script parsing took %.2f ms \n " , timer . TimeMS ( ) ) ;
2016-10-12 18:42:41 +00:00
2017-01-13 12:51:47 +00:00
// Now we may call the scripted OnDestroy method.
PClass : : bVMOperational = true ;
2016-11-15 10:21:08 +00:00
StateSourceLines . Clear ( ) ;
2016-03-01 15:47:10 +00:00
}