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"
2018-11-25 07:16:18 +00:00
# include "info.h"
# include "thingdef.h"
2016-03-01 15:47:10 +00:00
// 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 ) ;
2018-11-25 07:16:18 +00:00
//==========================================================================
//
// PClassActor :: Finalize
//
// Installs the parsed states and does some sanity checking
//
//==========================================================================
void FinalizeClass ( PClass * ccls , FStateDefinitions & statedef )
{
if ( ! ccls - > IsDescendantOf ( NAME_Actor ) ) return ;
auto cls = static_cast < PClassActor * > ( ccls ) ;
try
{
statedef . FinishStates ( cls ) ;
}
catch ( CRecoverableError & )
{
statedef . MakeStateDefines ( nullptr ) ;
throw ;
}
auto def = GetDefaultByType ( cls ) ;
statedef . InstallStates ( cls , def ) ;
statedef . MakeStateDefines ( nullptr ) ;
if ( cls - > IsDescendantOf ( NAME_Inventory ) )
{
def - > flags | = MF_SPECIAL ;
}
if ( cls - > IsDescendantOf ( NAME_Weapon ) )
{
FState * ready = def - > FindState ( NAME_Ready ) ;
FState * select = def - > FindState ( NAME_Select ) ;
FState * deselect = def - > FindState ( NAME_Deselect ) ;
FState * fire = def - > FindState ( NAME_Fire ) ;
auto TypeName = cls - > TypeName ;
// Consider any weapon without any valid state abstract and don't output a warning
// This is for creating base classes for weapon groups that only set up some properties.
if ( ready | | select | | deselect | | fire )
{
if ( ! ready )
{
I_Error ( " Weapon %s doesn't define a ready state. " , TypeName . GetChars ( ) ) ;
}
if ( ! select )
{
I_Error ( " Weapon %s doesn't define a select state. " , TypeName . GetChars ( ) ) ;
}
if ( ! deselect )
{
I_Error ( " Weapon %s doesn't define a deselect state. " , TypeName . GetChars ( ) ) ;
}
if ( ! fire )
{
I_Error ( " Weapon %s doesn't define a fire state. " , TypeName . GetChars ( ) ) ;
}
}
}
}
2016-11-15 10:21:08 +00:00
//==========================================================================
//
// 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
{
2018-12-03 11:24:55 +00:00
TArray < PType * > rets ;
2016-10-15 12:36:08 +00:00
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 ;
2018-12-03 11:24:55 +00:00
if ( returntype ) rets . Push ( returntype ) ;
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
//
//==========================================================================
2019-01-23 21:37:16 +00:00
PFunction * FindClassMemberFunction ( PContainerType * selfcls , PContainerType * funccls , FName name , FScriptPosition & sc , bool * error , const VersionInfo & version , bool nodeprecated )
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 )
{
2018-11-24 07:17:30 +00:00
if ( PClass : : FindClass ( name ) ) return nullptr ; // Special case when a class's member variable hides a global class name. This should still work.
2016-10-15 18:16:44 +00:00
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 ( ) ) ;
}
2019-01-23 21:37:16 +00:00
// ZScript will skip this because it prints its own message.
else if ( ( funcsym - > Variants [ 0 ] . Flags & VARF_Deprecated ) & & funcsym - > mVersion < = version & & ! nodeprecated )
2016-10-15 18:16:44 +00:00
{
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 ;
2018-11-25 09:00:55 +00:00
auto cwtype = PClass : : FindActor ( NAME_Weapon ) ;
if ( obj - > IsDescendantOf ( cwtype ) )
2016-11-13 11:02:41 +00:00
{
2018-11-25 09:00:55 +00:00
if ( obj - > Size = = cwtype - > 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 ;
}
2018-11-25 09:09:06 +00:00
else return ; // something else derived from StateProvider. 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
}
2019-07-11 08:07:09 +00:00
void CheckDropItems ( const PClassActor * const obj )
{
const FDropItem * dropItem = obj - > ActorInfo ( ) - > DropItems ;
while ( dropItem ! = nullptr )
{
if ( dropItem - > Name ! = NAME_None )
{
const char * const dropItemName = dropItem - > Name . GetChars ( ) ;
if ( dropItemName [ 0 ] ! = ' \0 ' & & PClass : : FindClass ( dropItem - > Name ) = = nullptr )
{
Printf ( TEXTCOLOR_ORANGE " Undefined drop item class %s referenced from actor %s \n " , dropItemName , obj - > TypeName . GetChars ( ) ) ;
FScriptPosition : : WarnCounter + + ;
}
}
dropItem = dropItem - > Next ;
}
}
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
2018-11-25 09:09:06 +00:00
if ( ti - > bDecorateClass & & ti - > IsDescendantOf ( NAME_StateProvider ) )
2016-11-13 11:02:41 +00:00
{
// 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 ;
}
2019-07-11 08:07:09 +00:00
CheckDropItems ( ti ) ;
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
}