From d753d417525a6e7073fed17d11a67d1c35aa2443 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Oct 2008 17:38:00 +0000 Subject: [PATCH] - Added NULL checks to all places where class names are passed as DECORATE parameters. - All DECORATE parameters are passed as expressions now. This change allows for compile time checks of all class names being used in DECORATE so many incorrect definitions may output warnings now. - Changed DECORATE sound and color parameters to use expressions. - Changed: S_StopChannel now resets the actor's sound flags. The previous bug made me think that delaying this until FMod calls the end of sound callback may simply be too late. SVN r1276 (trunk) --- docs/rh-log.txt | 18 + src/CMakeLists.txt | 1 - src/g_heretic/a_hereticweaps.cpp | 1 + src/g_strife/a_strifeweapons.cpp | 48 +- src/info.cpp | 6 +- src/p_enemy.cpp | 9 +- src/p_enemy_a_lookex.cpp | 4 +- src/p_states.cpp | 47 -- src/s_sound.cpp | 7 + src/sc_man.cpp | 89 ++++ src/sc_man.h | 40 ++ src/thingdef/thingdef.cpp | 485 +++--------------- src/thingdef/thingdef.h | 100 ++-- src/thingdef/thingdef_codeptr.cpp | 63 ++- src/thingdef/thingdef_exp.cpp | 5 - src/thingdef/thingdef_exp.h | 190 ++++--- src/thingdef/thingdef_expression.cpp | 588 ++++++++++++++++++---- src/thingdef/thingdef_main.cpp | 166 ------ src/thingdef/thingdef_parse.cpp | 504 +++++++++++++++++-- src/thingdef/thingdef_states.cpp | 51 +- src/thingdef/thingdef_type.h | 19 +- wadsrc/static/actors/chex/chexweapons.txt | 7 +- zdoom.vcproj | 4 - 23 files changed, 1476 insertions(+), 976 deletions(-) delete mode 100644 src/thingdef/thingdef_main.cpp diff --git a/docs/rh-log.txt b/docs/rh-log.txt index a1d895130..8e40c6f3d 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,8 +1,26 @@ +October 25, 2008 (Changes by Graf Zahl) +- Added NULL checks to all places where class names are passed as DECORATE + parameters. +- All DECORATE parameters are passed as expressions now. This change allows + for compile time checks of all class names being used in DECORATE so many + incorrect definitions may output warnings now. + October 23, 2008 (Changes by Graf Zahl) +- Changed: S_StopChannel now resets the actor's sound flags. The previous bug + made me think that delaying this until FMod calls the end of sound callback + may simply be too late. - Fixed: The high level sound code must not rely on FMod immediately returning the sound channel data when a sound is being stopped. This caused an endless loop when changing levels with Strife's Flamethrower active. +October 22, 2008 (Changes by Graf Zahl) +- Changed DECORATE sound and color parameters to use expressions. + +October 21, 2008 (Changes by Graf Zahl) +- Added a proper function parser to the expression evaluator and converted + sin/cos and action specials to use it. The old evaluator is gone now. +- fixed some GCC problems with autosegs. + October 20, 2008 - Game time is now frozen during screen wipes. This obsoletes the DEM_WIPEON and DEM_WIPEOFF commands. Fixes multimap demos desyncing when played back diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db5afb226..ff69157ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -627,7 +627,6 @@ add_executable( zdoom WIN32 thingdef/thingdef_data.cpp thingdef/thingdef_exp.cpp thingdef/thingdef_expression.cpp - thingdef/thingdef_main.cpp thingdef/thingdef_parse.cpp thingdef/thingdef_properties.cpp thingdef/thingdef_states.cpp diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index c7aa8b9e0..c0988aa45 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -81,6 +81,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack) if (!weapon->DepleteAmmo (weapon->bAltFire)) return; } + if (puff == NULL) puff = PClass::FindClass(NAME_BulletPuff); // just to be sure angle = self->angle; angle += pr_sap.Random2() << 18; slope = P_AimLineAttack (self, angle, MELEERANGE, &linetarget); diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 72b49e900..757863fde 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -654,33 +654,33 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade) if (!weapon->DepleteAmmo (weapon->bAltFire)) return; - // Make it flash - FState *jumpto = P_GetState(weapon, NULL, flash); + P_SetPsprite (player, ps_flash, flash); - P_SetPsprite (player, ps_flash, jumpto); - - self->z += 32*FRACUNIT; - grenade = P_SpawnSubMissile (self, grenadetype, self); - self->z -= 32*FRACUNIT; - if (grenade == NULL) - return; - - if (grenade->SeeSound != 0) + if (grenadetype != NULL) { - S_Sound (grenade, CHAN_VOICE, grenade->SeeSound, 1, ATTN_NORM); + self->z += 32*FRACUNIT; + grenade = P_SpawnSubMissile (self, grenadetype, self); + self->z -= 32*FRACUNIT; + if (grenade == NULL) + return; + + if (grenade->SeeSound != 0) + { + S_Sound (grenade, CHAN_VOICE, grenade->SeeSound, 1, ATTN_NORM); + } + + grenade->momz = FixedMul (finetangent[FINEANGLES/4-(self->pitch>>ANGLETOFINESHIFT)], grenade->Speed) + 8*FRACUNIT; + + an = self->angle >> ANGLETOFINESHIFT; + tworadii = self->radius + grenade->radius; + grenade->x += FixedMul (finecosine[an], tworadii); + grenade->y += FixedMul (finesine[an], tworadii); + + an = self->angle + Angle; + an >>= ANGLETOFINESHIFT; + grenade->x += FixedMul (finecosine[an], 15*FRACUNIT); + grenade->y += FixedMul (finesine[an], 15*FRACUNIT); } - - grenade->momz = FixedMul (finetangent[FINEANGLES/4-(self->pitch>>ANGLETOFINESHIFT)], grenade->Speed) + 8*FRACUNIT; - - an = self->angle >> ANGLETOFINESHIFT; - tworadii = self->radius + grenade->radius; - grenade->x += FixedMul (finecosine[an], tworadii); - grenade->y += FixedMul (finesine[an], tworadii); - - an = self->angle + Angle; - an >>= ANGLETOFINESHIFT; - grenade->x += FixedMul (finecosine[an], 15*FRACUNIT); - grenade->y += FixedMul (finesine[an], 15*FRACUNIT); } // The Almighty Sigil! ------------------------------------------------------ diff --git a/src/info.cpp b/src/info.cpp index 2773501b0..f0c320a6b 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -49,7 +49,7 @@ #include "templates.h" #include "cmdlib.h" -extern void LoadDecorations (); +extern void LoadActors (); //========================================================================== // @@ -102,8 +102,8 @@ void FActorInfo::StaticInit () sprites.Push (temp); } - Printf ("LoadDecorations: Load external actors.\n"); - LoadDecorations (); + Printf ("LoadActors: Load actor definitions.\n"); + LoadActors (); } //========================================================================== diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 8584f4e23..6b18d4cce 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2232,15 +2232,12 @@ enum ChaseFlags DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) { ACTION_PARAM_START(3); - ACTION_PARAM_STATE(i_melee, 0); - ACTION_PARAM_STATE(i_missile, 1); + ACTION_PARAM_STATE(melee, 0); + ACTION_PARAM_STATE(missile, 1); ACTION_PARAM_INT(flags, 2); - if (i_melee != INT_MIN) + if (melee != (FState*)-1) { - FState *melee = P_GetState(self, CallingState, i_melee); - FState *missile = P_GetState(self, CallingState, i_missile); - if (flags & CHF_RESURRECT && P_CheckForResurrection(self, false)) return; A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), diff --git a/src/p_enemy_a_lookex.cpp b/src/p_enemy_a_lookex.cpp index 2e580eb45..f8ba31b86 100644 --- a/src/p_enemy_a_lookex.cpp +++ b/src/p_enemy_a_lookex.cpp @@ -719,9 +719,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LookEx) ACTION_PARAM_FIXED(maxseedist, 2); ACTION_PARAM_FIXED(maxheardist, 3); ACTION_PARAM_ANGLE(fov, 4); - ACTION_PARAM_STATE(i_state, 5); - - FState *seestate = P_GetState(self, CallingState, i_state); + ACTION_PARAM_STATE(seestate, 5); AActor *targ = NULL; // Shuts up gcc fixed_t dist; diff --git a/src/p_states.cpp b/src/p_states.cpp index 13d7055a9..251c6033b 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -53,8 +53,6 @@ #define NULL_STATE_INDEX 127 -TArray JumpParameters; - //========================================================================== // // @@ -210,51 +208,6 @@ bool AActor::HasSpecialDeathStates () const return false; } -//========================================================================== -// -// Resolves a label parameter -// -//========================================================================== - -FState *P_GetState(AActor *self, FState *CallingState, int offset) -{ - if (offset == 0 || offset == INT_MIN) - { - return NULL; // 0 means 'no state' - } - else if (offset>0) - { - if (CallingState == NULL) return NULL; - return CallingState + offset; - } - else if (self != NULL) - { - FName *params = &JumpParameters[-offset]; - - FName classname = params[0]; - const PClass *cls; - cls = classname==NAME_None? RUNTIME_TYPE(self) : PClass::FindClass(classname); - if (cls==NULL || cls->ActorInfo==NULL) return NULL; // shouldn't happen - - int numnames = (int)params[1]; - FState *jumpto = cls->ActorInfo->FindState(numnames, ¶ms[2]); - if (jumpto == NULL) - { - const char *dot=""; - Printf("Jump target '"); - if (classname != NAME_None) Printf("%s::", classname.GetChars()); - for (int i=0;iGetClass()->TypeName.GetChars()); - } - return jumpto; - } - else return NULL; -} - //========================================================================== // // Creates a list of names from a string. Dots are used as separator diff --git a/src/s_sound.cpp b/src/s_sound.cpp index ddfb286a0..6799119b5 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1910,6 +1910,13 @@ void S_StopChannel(FSoundChan *chan) { chan->ChanFlags |= CHAN_FORGETTABLE; } + + if (chan->SourceType == SOURCE_Actor && chan->Actor != NULL) + { + chan->Actor->SoundChans &= ~(1 << chan->EntChannel); + chan->Actor = NULL; + } + GSnd->StopChannel(chan); } else diff --git a/src/sc_man.cpp b/src/sc_man.cpp index d63c15043..1f6dd9171 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -21,6 +21,7 @@ #include "cmdlib.h" #include "m_misc.h" #include "templates.h" +#include "doomstat.h" // MACROS ------------------------------------------------------------------ @@ -1023,3 +1024,91 @@ void FScanner::CheckOpen() I_FatalError ("SC_ call before SC_Open()."); } } + +//========================================================================== +// +// a class that remembers a parser position +// +//========================================================================== + +FScriptPosition::FScriptPosition(const FScriptPosition &other) +{ + FileName = other.FileName; + ScriptLine = other.ScriptLine; +} + +FScriptPosition::FScriptPosition(FString fname, int line) +{ + FileName = fname; + ScriptLine = line; +} + +FScriptPosition::FScriptPosition(FScanner &sc) +{ + FileName = sc.ScriptName; + ScriptLine = sc.GetMessageLine(); +} + +FScriptPosition &FScriptPosition::operator=(const FScriptPosition &other) +{ + FileName = other.FileName; + ScriptLine = other.ScriptLine; + return *this; +} + +//========================================================================== +// +// FScriptPosition::Message +// +//========================================================================== + +void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...) const +{ + FString composed; + + if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && !developer) return; + + if (message == NULL) + { + composed = "Bad syntax."; + } + else + { + va_list arglist; + va_start (arglist, message); + composed.VFormat (message, arglist); + va_end (arglist); + } + const char *type = ""; + int level = PRINT_HIGH; + + switch (severity) + { + default: + return; + + case MSG_WARNING: + type = "warning"; + break; + + case MSG_ERROR: + type = "error"; + break; + + case MSG_DEBUG: + type = "message"; + break; + + case MSG_DEBUGLOG: + case MSG_LOG: + type = "message"; + level = PRINT_LOG; + break; + + case MSG_FATAL: + I_Error ("Script error, \"%s\" line %d:\n%s\n", + FileName.GetChars(), ScriptLine, composed.GetChars()); + } +} + + diff --git a/src/sc_man.h b/src/sc_man.h index f688b41d4..665b31bf8 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -215,4 +215,44 @@ enum TK_LastToken }; + +//========================================================================== +// +// +// +//========================================================================== + +enum +{ + MSG_WARNING, + MSG_FATAL, + MSG_ERROR, + MSG_DEBUG, + MSG_LOG, + MSG_DEBUGLOG +}; + +//========================================================================== +// +// a class that remembers a parser position +// +//========================================================================== + +struct FScriptPosition +{ + FString FileName; + int ScriptLine; + + FScriptPosition() + { + ScriptLine=0; + } + FScriptPosition(const FScriptPosition &other); + FScriptPosition(FString fname, int line); + FScriptPosition(FScanner &sc); + FScriptPosition &operator=(const FScriptPosition &other); + void Message(int severity, const char *message,...) const; +}; + + #endif //__SC_MAN_H__ diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index eba724cb1..1fcba1f98 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -4,8 +4,8 @@ ** Actor definitions ** **--------------------------------------------------------------------------- -** Copyright 2002-2007 Christoph Oelckers -** Copyright 2004-2007 Randy Heit +** Copyright 2002-2008 Christoph Oelckers +** Copyright 2004-2008 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -56,302 +56,29 @@ #include "m_random.h" #include "i_system.h" #include "p_local.h" -#include "v_palette.h" #include "doomerrors.h" #include "a_hexenglobal.h" #include "a_weaponpiece.h" #include "p_conversation.h" #include "v_text.h" #include "thingdef.h" +#include "thingdef_exp.h" #include "a_sharedglobal.h" +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +void InitThingdef(); +void ParseDecorate (FScanner &sc); +// STATIC FUNCTION PROTOTYPES -------------------------------------------- const PClass *QuestItemClasses[31]; - -//========================================================================== -// -// ParseParameter -// -// Parses aparameter - either a default in a function declaration -// or an argument in a function call. -// -//========================================================================== - -int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant) -{ - int v; - - switch(type) - { - case 'S': - case 's': // Sound name - sc.MustGetString(); - return S_FindSound(sc.String); - - case 'M': - case 'm': // Actor name - case 'T': - case 't': // String - sc.SetEscape(true); - sc.MustGetString(); - sc.SetEscape(false); - return (int)(sc.String[0] ? FName(sc.String) : NAME_None); - - case 'C': - case 'c': // Color - sc.MustGetString (); - if (sc.Compare("none")) - { - return -1; - } - else if (sc.Compare("")) - { - return 0; - } - else - { - int c = V_GetColor (NULL, sc.String); - // 0 needs to be the default so we have to mark the color. - return MAKEARGB(1, RPART(c), GPART(c), BPART(c)); - } - - case 'L': - case 'l': - { - if (JumpParameters.Size()==0) JumpParameters.Push(NAME_None); - - v = -(int)JumpParameters.Size(); - // This forces quotation marks around the state name. - sc.MustGetToken(TK_StringConst); - if (sc.String[0] == 0 || sc.Compare("None")) - { - return 0; - } - if (sc.Compare("*")) - { - if (constant) return INT_MIN; - else sc.ScriptError("Invalid state name '*'"); - } - FString statestring = sc.String; // ParseStateString(sc); - const PClass *stype=NULL; - int scope = statestring.IndexOf("::"); - if (scope >= 0) - { - FName scopename = FName(statestring, scope, false); - if (scopename == NAME_Super) - { - // Super refers to the direct superclass - scopename = cls->ParentClass->TypeName; - } - JumpParameters.Push(scopename); - statestring = statestring.Right(statestring.Len()-scope-2); - - stype = PClass::FindClass (scopename); - if (stype == NULL) - { - sc.ScriptError ("%s is an unknown class.", scopename.GetChars()); - } - if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor))) - { - sc.ScriptError ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars()); - } - if (!stype->IsAncestorOf (cls)) - { - sc.ScriptError ("%s is not derived from %s so cannot access its states.", - cls->TypeName.GetChars(), stype->TypeName.GetChars()); - } - } - else - { - // No class name is stored. This allows 'virtual' jumps to - // labels in subclasses. - // It also means that the validity of the given state cannot - // be checked here. - JumpParameters.Push(NAME_None); - } - - TArray &names = MakeStateNameList(statestring); - - if (stype != NULL) - { - if (!stype->ActorInfo->FindState(names.Size(), &names[0])) - { - sc.ScriptError("Jump to unknown state '%s' in class '%s'", - statestring.GetChars(), stype->TypeName.GetChars()); - } - } - JumpParameters.Push((ENamedName)names.Size()); - for(unsigned i=0;i DefaultParams; - bool hasdefaults = false; - - if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) - { - sc.ScriptError ("action functions can only be imported by internal class and actor definitions!"); - } - - sc.MustGetToken(TK_Native); - sc.MustGetToken(TK_Identifier); - funcname = sc.String; - afd = FindFunction(sc.String); - if (afd == NULL) - { - sc.ScriptError ("The function '%s' has not been exported from the executable.", sc.String); - } - sc.MustGetToken('('); - if (!sc.CheckToken(')')) - { - while (sc.TokenType != ')') - { - int flags = 0; - char type = '@'; - - // Retrieve flags before type name - for (;;) - { - if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) - { - } - else - { - break; - } - } - // Read the variable type - sc.MustGetAnyToken(); - switch (sc.TokenType) - { - case TK_Bool: - case TK_Int: - case TK_Float: - type = 'x'; - break; - - case TK_Sound: type = 's'; break; - case TK_String: type = 't'; break; - case TK_Name: type = 't'; break; - case TK_State: type = 'l'; break; - case TK_Color: type = 'c'; break; - case TK_Class: - sc.MustGetToken('<'); - sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care - sc.MustGetToken('>'); - type = 'm'; - break; - case TK_Ellipsis: - type = '+'; - sc.MustGetToken(')'); - sc.UnGet(); - break; - default: - sc.ScriptError ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); - break; - } - // Read the optional variable name - if (!sc.CheckToken(',') && !sc.CheckToken(')')) - { - sc.MustGetToken(TK_Identifier); - } - else - { - sc.UnGet(); - } - - int def; - if (sc.CheckToken('=')) - { - hasdefaults = true; - flags|=OPTIONAL; - def = ParseParameter(sc, cls, type, true); - } - else - { - def = 0; - } - DefaultParams.Push(def); - - if (!(flags & OPTIONAL) && type != '+') - { - type -= 'a' - 'A'; - } - args += type; - sc.MustGetAnyToken(); - if (sc.TokenType != ',' && sc.TokenType != ')') - { - sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); - } - } - } - sc.MustGetToken(';'); - PSymbolActionFunction *sym = new PSymbolActionFunction(funcname); - sym->Arguments = args; - sym->Function = afd->Function; - if (hasdefaults) - { - sym->defaultparameterindex = StateParameters.Size(); - for(unsigned int i = 0; i < DefaultParams.Size(); i++) - StateParameters.Push(DefaultParams[i]); - } - else - { - sym->defaultparameterindex = -1; - } - if (cls->Symbols.AddSymbol (sym) == NULL) - { - delete sym; - sc.ScriptError ("'%s' is already defined in class '%s'.", - funcname.GetChars(), cls->TypeName.GetChars()); - } -} +PSymbolTable GlobalSymbols; //========================================================================== // // Starts a new actor definition // //========================================================================== -static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replaceName, +FActorInfo *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, FName replaceName, int DoomEdNum, bool native) { const PClass *replacee = NULL; @@ -366,15 +93,15 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac if (parent == NULL) { - I_Error ("Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars()); + sc.Message(MSG_FATAL, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars()); } else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor))) { - I_Error ("Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars()); + sc.Message(MSG_FATAL, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars()); } else if (parent->ActorInfo == NULL) { - I_Error ("uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars()); + sc.Message(MSG_FATAL, "uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars()); } } @@ -386,11 +113,11 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac if (replacee == NULL) { - I_Error ("Replaced type '%s' not found in %s", replaceName.GetChars(), typeName.GetChars()); + sc.Message(MSG_FATAL, "Replaced type '%s' not found in %s", replaceName.GetChars(), typeName.GetChars()); } else if (replacee->ActorInfo == NULL) { - I_Error ("Replaced type '%s' is not an actor in %s", replaceName.GetChars(), typeName.GetChars()); + sc.Message(MSG_FATAL, "Replaced type '%s' is not an actor in %s", replaceName.GetChars(), typeName.GetChars()); } } @@ -399,15 +126,15 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac ti = (PClass*)PClass::FindClass(typeName); if (ti == NULL) { - I_Error("Unknown native class '%s'", typeName.GetChars()); + sc.Message(MSG_FATAL, "Unknown native class '%s'", typeName.GetChars()); } else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass()) { - I_Error("Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars()); + sc.Message(MSG_FATAL, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars()); } else if (ti->ActorInfo != NULL) { - I_Error("Redefinition of internal class '%s'", typeName.GetChars()); + sc.Message(MSG_FATAL, "Redefinition of internal class '%s'", typeName.GetChars()); } ti->InitializeActorInfo(); info = ti->ActorInfo; @@ -444,145 +171,43 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac //========================================================================== // -// Starts a new actor definition +// Finalizes an actor definition // //========================================================================== -static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag) + +void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag) { - FName typeName; - FName parentName; - FName replaceName; - bool native = false; - int DoomEdNum = -1; - - // Get actor name - sc.MustGetString(); - - char *colon = strchr(sc.String, ':'); - if (colon != NULL) - { - *colon++ = 0; - } - - typeName = sc.String; - - // Do some tweaking so that a definition like 'Actor:Parent' which is read as a single token is recognized as well - // without having resort to C-mode (which disallows periods in actor names.) - if (colon == NULL) - { - sc.MustGetString (); - if (sc.String[0]==':') - { - colon = sc.String + 1; - } - } - - if (colon != NULL) - { - if (colon[0] == 0) - { - sc.MustGetString (); - colon = sc.String; - } - } - - if (colon == NULL) - { - sc.UnGet(); - } - - parentName = colon; - - // Check for "replaces" - if (sc.CheckString ("replaces")) - { - // Get actor name - sc.MustGetString (); - replaceName = sc.String; - } - - // Now, after the actor names have been parsed, it is time to switch to C-mode - // for the rest of the actor definition. - sc.SetCMode (true); - if (sc.CheckNumber()) - { - if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number; - else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]"); - } - - if (sc.CheckString("native")) - { - native = true; - } + AActor *defaults = (AActor*)info->Class->Defaults; try { - FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native); - ResetBaggage (bag, info->Class->ParentClass); - bag->Info = info; - bag->Lumpnum = sc.LumpNum; -#ifdef _DEBUG - bag->ClassName = typeName; -#endif - return info; + bag.statedef.FinishStates (info, defaults, bag.StateArray); } catch (CRecoverableError &err) { - sc.ScriptError("%s", err.GetMessage()); - return NULL; + sc.Message(MSG_FATAL, "%s", err.GetMessage()); } -} - -//========================================================================== -// -// Reads an actor definition -// -//========================================================================== -void ParseActor(FScanner &sc) -{ - FActorInfo * info=NULL; - Baggage bag; - - info = ParseActorHeader(sc, &bag); - sc.MustGetToken('{'); - while (sc.MustGetAnyToken(), sc.TokenType != '}') + bag.statedef.InstallStates (info, defaults); + bag.StateArray.Clear (); + if (bag.DropItemSet) { - switch (sc.TokenType) + if (bag.DropItemList == NULL) { - case TK_Action: - ParseActionDef (sc, info->Class); - break; - - case TK_Const: - ParseConstant (sc, &info->Class->Symbols, info->Class); - break; - - case TK_Enum: - ParseEnum (sc, &info->Class->Symbols, info->Class); - break; - - case TK_Native: - ParseVariable (sc, &info->Class->Symbols, info->Class); - break; - - case TK_Identifier: - // other identifier related checks here - case TK_Projectile: // special case: both keyword and property name - ParseActorProperty(sc, bag); - break; - - case '+': - case '-': - ParseActorFlag(sc, bag, sc.TokenType); - break; - - default: - sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars()); - break; + if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0) + { + info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0); + } + } + else + { + info->Class->Meta.SetMetaInt (ACMETA_DropItems, + StoreDropItemChain(bag.DropItemList)); } } - FinishActor(sc, info, bag); - sc.SetCMode (false); + if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory))) + { + defaults->flags |= MF_SPECIAL; + } } //========================================================================== @@ -617,10 +242,12 @@ static int ResolvePointer(const PClass **pPtr, const PClass *owner, const PClass } -void FinishThingdef() +static void FinishThingdef() { unsigned int i; - int errorcount=0; + int errorcount; + + errorcount = StateParams.ResolveAll(); for (i = 0;i < PClass::m_Types.Size(); i++) { @@ -747,3 +374,27 @@ void FinishThingdef() } + + +//========================================================================== +// +// LoadActors +// +// Called from FActor::StaticInit() +// +//========================================================================== + +void LoadActors () +{ + int lastlump, lump; + + InitThingdef(); + lastlump = 0; + while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1) + { + FScanner sc(lump); + ParseDecorate (sc); + } + FinishThingdef(); +} + diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 15a8132c5..f029bcadc 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -3,6 +3,8 @@ #include "doomtype.h" #include "info.h" +#include "s_sound.h" +#include "sc_man.h" class FScanner; @@ -55,9 +57,7 @@ public: // State parser // //========================================================================== - -extern TArray StateParameters; -extern TArray JumpParameters; +class FxExpression; struct FStateLabels; @@ -112,6 +112,38 @@ public: }; +//========================================================================== +// +// +// +//========================================================================== + +struct FStateExpression +{ + FxExpression *expr; + const PClass *owner; + bool constant; + bool cloned; +}; + +class FStateExpressions +{ + TArray expressions; + +public: + ~FStateExpressions(); + int Add(FxExpression *x, const PClass *o, bool c); + int Reserve(int num, const PClass *cls); + void Set(int num, FxExpression *x); + void Copy(int dest, int src, int cnt); + int ResolveAll(); + FxExpression *Get(int no); + int Size() { return expressions.Size(); } +}; + +extern FStateExpressions StateParams; + + //========================================================================== // // Extra info maintained while defining an actor. @@ -161,7 +193,6 @@ AFuncDesc * FindFunction(const char * string); -FState *P_GetState(AActor *self, FState *CallingState, int offset); int ParseStates(FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag); PSymbolActionFunction *FindGlobalActionFunction(const char *name); @@ -172,23 +203,13 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name); // //========================================================================== -void ParseActorProperty(FScanner &sc, Baggage &bag); +FActorInfo *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, FName replaceName, + int DoomEdNum, bool native); + void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod); -void ParseActorFlag (FScanner &sc, Baggage &bag, int mod); -void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag); +void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag); +FxExpression *ParseParameter(FScanner &sc, PClass *cls, char type, bool constant); -void ParseConstant (FScanner &sc, PSymbolTable *symt, PClass *cls); -void ParseVariable (FScanner &sc, PSymbolTable *symt, PClass *cls); -void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls); -int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant); - - -int ParseExpression (FScanner &sc, bool _not, PClass *cls); - -bool IsExpressionConst(int id); -int EvalExpressionI (int id, AActor *self); -double EvalExpressionF (int id, AActor *self); -fixed_t EvalExpressionFix (int id, AActor *self); enum { @@ -360,40 +381,45 @@ struct StateCallData MSVC_ASEG AFuncDesc *infoptr_##cls##_##name GCC_ASEG = &info_##cls##_##name; \ void AFP_##name (AActor *self, FState *CallingState, int ParameterIndex, StateCallData *statecall) -#define DECLARE_PARAMINFO FState *CallingState, int ParameterIndex, StateCallData *statecall -#define PUSH_PARAMINFO CallingState, ParameterIndex, statecall +#define DECLARE_PARAMINFO AActor *self, FState *CallingState, int ParameterIndex, StateCallData *statecall +#define PUSH_PARAMINFO self, CallingState, ParameterIndex, statecall #define CALL_ACTION(name,self) AF_##name(self, NULL, 0, NULL) + +int EvalExpressionI (DWORD x, AActor *self); +int EvalExpressionCol (DWORD x, AActor *self); +FSoundID EvalExpressionSnd (DWORD x, AActor *self); +double EvalExpressionF (DWORD x, AActor *self); +fixed_t EvalExpressionFix (DWORD x, AActor *self); +FState *EvalExpressionState (DWORD x, AActor *self); +const PClass *EvalExpressionClass (DWORD x, AActor *self); +FName EvalExpressionName (DWORD x, AActor *self); + #define ACTION_PARAM_START(count) -#define ACTION_PARAM_CONST(var, i) \ - int var = StateParameters[ParameterIndex+i]; #define ACTION_PARAM_INT(var, i) \ - int var = EvalExpressionI(StateParameters[ParameterIndex+i], self); + int var = EvalExpressionI(ParameterIndex+i, self); #define ACTION_PARAM_BOOL(var,i) \ - bool var = !!EvalExpressionI(StateParameters[ParameterIndex+i], self); + bool var = !!EvalExpressionI(ParameterIndex+i, self); #define ACTION_PARAM_FIXED(var,i) \ - fixed_t var = EvalExpressionFix(StateParameters[ParameterIndex+i], self); + fixed_t var = EvalExpressionFix(ParameterIndex+i, self); #define ACTION_PARAM_FLOAT(var,i) \ - float var = EvalExpressionF(StateParameters[ParameterIndex+i], self); + float var = EvalExpressionF(ParameterIndex+i, self); #define ACTION_PARAM_CLASS(var,i) \ - const PClass *var = PClass::FindClass(ENamedName(StateParameters[ParameterIndex+i])); + const PClass *var = EvalExpressionClass(ParameterIndex+i, self); #define ACTION_PARAM_STATE(var,i) \ - int var = StateParameters[ParameterIndex+i]; + FState *var = EvalExpressionState(ParameterIndex+i, self); #define ACTION_PARAM_COLOR(var,i) \ - PalEntry var = StateParameters[ParameterIndex+i]; + PalEntry var = EvalExpressionCol(ParameterIndex+i, self); #define ACTION_PARAM_SOUND(var,i) \ - FSoundID var = StateParameters[ParameterIndex+i]; + FSoundID var = EvalExpressionSnd(ParameterIndex+i, self); #define ACTION_PARAM_STRING(var,i) \ - const char *var = FName(ENamedName(StateParameters[ParameterIndex+i])); + const char *var = EvalExpressionName(ParameterIndex+i, self); #define ACTION_PARAM_NAME(var,i) \ - FName var = ENamedName(StateParameters[ParameterIndex+i]); -#define ACTION_PARAM_VARARG(var, i) \ - int *var = &StateParameters[ParameterIndex+i]; - + FName var = EvalExpressionName(ParameterIndex+i, self); #define ACTION_PARAM_ANGLE(var,i) \ - angle_t var = angle_t(EvalExpressionF(StateParameters[ParameterIndex+i], self)*ANGLE_90/90.f); + angle_t var = angle_t(EvalExpressionF(ParameterIndex+i, self)*ANGLE_90/90.f); #define ACTION_SET_RESULT(v) if (statecall != NULL) statecall->Result = v; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 85bd5c8ad..524b191bc 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -232,6 +232,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) ACTION_PARAM_CLASS(MissileType, 2); ACTION_PARAM_FIXED(MissileHeight, 3); + if (MissileType == NULL) return; DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); } @@ -359,31 +360,24 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) // Do the state jump // //========================================================================== -static void DoJump(AActor * self, FState * CallingState, int offset, StateCallData *statecall) +static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) { + if (jumpto == NULL) return; if (statecall != NULL) { - FState *jumpto = P_GetState(statecall->Item, CallingState, offset); - if (jumpto == NULL) return; statecall->State = jumpto; } else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) { - FState *jumpto = P_GetState(self->player->ReadyWeapon, CallingState, offset); - if (jumpto == NULL) return; P_SetPsprite(self->player, ps_weapon, jumpto); } else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) { - FState *jumpto = P_GetState(self->player->ReadyWeapon, CallingState, offset); - if (jumpto == NULL) return; P_SetPsprite(self->player, ps_flash, jumpto); } else if (CallingState == self->state) { - FState *jumpto = P_GetState(self, CallingState, offset); - if (jumpto == NULL) return; self->SetState (jumpto); } else @@ -405,20 +399,14 @@ static void DoJump(AActor * self, FState * CallingState, int offset, StateCallDa DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) { ACTION_PARAM_START(3); - ACTION_PARAM_CONST(count, 0); + ACTION_PARAM_INT(count, 0); ACTION_PARAM_INT(maxchance, 1); - ACTION_PARAM_VARARG(jumps, 2); if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) { - if (count == 2) - { - ACTION_JUMP(*jumps); - } - else - { - ACTION_JUMP(jumps[(pr_cajump() % (count - 1))]); - } + int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); + ACTION_PARAM_STATE(jumpto, jumps); + ACTION_JUMP(jumpto); } ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! } @@ -482,7 +470,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) // State jump function // //========================================================================== -void DoJumpIfInventory(AActor * self, AActor * owner, DECLARE_PARAMINFO) +void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) { ACTION_PARAM_START(3); ACTION_PARAM_CLASS(Type, 0); @@ -504,12 +492,12 @@ void DoJumpIfInventory(AActor * self, AActor * owner, DECLARE_PARAMINFO) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) { - DoJumpIfInventory(self, self, PUSH_PARAMINFO); + DoJumpIfInventory(self, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) { - DoJumpIfInventory(self, self->target, PUSH_PARAMINFO); + DoJumpIfInventory(self->target, PUSH_PARAMINFO); } //========================================================================== @@ -1143,7 +1131,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) // //=========================================================================== -static void DoGiveInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) +static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) { ACTION_PARAM_START(2); ACTION_PARAM_CLASS(mi, 0); @@ -1184,12 +1172,12 @@ static void DoGiveInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) { - DoGiveInventory(self, self, PUSH_PARAMINFO); + DoGiveInventory(self, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) { - DoGiveInventory(self, self->target, PUSH_PARAMINFO); + DoGiveInventory(self->target, PUSH_PARAMINFO); } //=========================================================================== @@ -1198,13 +1186,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) // //=========================================================================== -void DoTakeInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) +void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) { ACTION_PARAM_START(2); ACTION_PARAM_CLASS(item, 0); ACTION_PARAM_INT(amount, 1); - if (receiver == NULL) return; + if (item == NULL || receiver == NULL) return; bool res = false; @@ -1228,12 +1216,12 @@ void DoTakeInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) { - DoTakeInventory(self, self, PUSH_PARAMINFO); + DoTakeInventory(self, PUSH_PARAMINFO); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) { - DoTakeInventory(self, self->target, PUSH_PARAMINFO); + DoTakeInventory(self->target, PUSH_PARAMINFO); } //=========================================================================== @@ -1463,6 +1451,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) ACTION_PARAM_FIXED(zmom, 3); ACTION_PARAM_BOOL(useammo, 4); + if (missile == NULL) return; + if (ACTION_CALL_FROM_WEAPON()) { // Used from a weapon so use some ammo @@ -1529,7 +1519,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) ACTION_PARAM_START(1); ACTION_PARAM_CLASS(cls, 0); - if (self->player == NULL) return; + if (cls == NULL || self->player == NULL) + { + ACTION_SET_RESULT(false); + return; + } AWeapon * weaponitem = static_cast(self->FindInventory(cls)); @@ -1714,10 +1708,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) ACTION_PARAM_START(1); ACTION_PARAM_CLASS(drop, 0); - AInventory * inv = self->FindInventory(drop); - if (inv) + if (drop) { - self->DropInventory(inv); + AInventory * inv = self->FindInventory(drop); + if (inv) + { + self->DropInventory(inv); + } } } diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index d7cd8d11e..ab4564af8 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -84,11 +84,6 @@ FxExpression *ParseExpression (FScanner &sc, PClass *cls) return data; } -int ParseExpression (FScanner &sc, bool _not, PClass *cls) -{ - return AddExpression(ParseExpression (sc, cls)); -} - static FxExpression *ParseExpressionM (FScanner &sc, const PClass *cls) { FxExpression *condition = ParseExpressionL (sc, cls); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 3bcb40c3f..8b0391584 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -56,65 +56,11 @@ extern PSymbolTable GlobalSymbols; // //========================================================================== -enum -{ - MSG_WARNING, - MSG_ERROR, - MSG_DEBUG, - MSG_LOG, - MSG_DEBUGLOG -}; - -//========================================================================== -// -// -// -//========================================================================== - -struct FScriptPosition -{ - FString FileName; - int ScriptLine; - - FScriptPosition() - { - ScriptLine=0; - } - FScriptPosition(const FScriptPosition &other) - { - FileName = other.FileName; - ScriptLine = other.ScriptLine; - } - FScriptPosition(FString fname, int line) - { - FileName = fname; - ScriptLine = line; - } - FScriptPosition(FScanner &sc) - { - FileName = sc.ScriptName; - ScriptLine = sc.GetMessageLine(); - } - FScriptPosition &operator=(const FScriptPosition &other) - { - FileName = other.FileName; - ScriptLine = other.ScriptLine; - return *this; - } - - void Message(int severity, const char *message,...) const; -}; - -//========================================================================== -// -// -// -//========================================================================== - struct FCompileContext { const PClass *cls; bool lax; + bool isconst; PSymbol *FindInClass(FName identifier) @@ -143,26 +89,51 @@ struct ExpVal void *pointer; }; - int GetInt() + int GetInt() const { return Type == VAL_Int? Int : Type == VAL_Float? int(Float) : 0; } - double GetFloat() + double GetFloat() const { return Type == VAL_Int? double(Int) : Type == VAL_Float? Float : 0; } - bool GetBool() + bool GetBool() const { - return Type == VAL_Int? !!Int : Type == VAL_Float? Float!=0. : false; + return (Type == VAL_Int || Type == VAL_Sound) ? !!Int : Type == VAL_Float? Float!=0. : false; } - template T *GetPointer() + template T *GetPointer() const { return Type == VAL_Object || Type == VAL_Pointer? (T*)pointer : NULL; } + FSoundID GetSoundID() const + { + return Type == VAL_Sound? Int : 0; + } + + int GetColor() const + { + return Type == VAL_Color? Int : 0; + } + + FName GetName() const + { + return Type == VAL_Name? ENamedName(Int) : NAME_None; + } + + FState *GetState() const + { + return Type == VAL_State? (FState*)pointer : NULL; + } + + const PClass *GetClass() const + { + return Type == VAL_Class? (const PClass *)pointer : NULL; + } + }; @@ -172,7 +143,7 @@ struct ExpVal // //========================================================================== -struct FxExpression +class FxExpression { protected: FxExpression(const FScriptPosition &pos) @@ -181,12 +152,9 @@ protected: ScriptPosition = pos; } public: + virtual ~FxExpression() {} virtual FxExpression *Resolve(FCompileContext &ctx); - FxExpression *ResolveAsBoolean(FCompileContext &ctx) - { - // This will need more handling if other types than Int and Float are added - return Resolve(ctx); - } + FxExpression *ResolveAsBoolean(FCompileContext &ctx); virtual ExpVal EvalExpression (AActor *self); virtual bool isConstant() const; @@ -194,7 +162,7 @@ public: FScriptPosition ScriptPosition; FExpressionType ValueType; -protected: + bool isresolved; }; @@ -263,18 +231,50 @@ public: { ValueType = value.Type = VAL_Int; value.Int = val; + isresolved = true; } FxConstant(double val, const FScriptPosition &pos) : FxExpression(pos) { ValueType = value.Type = VAL_Float; value.Float = val; + isresolved = true; + } + + FxConstant(FSoundID val, const FScriptPosition &pos) : FxExpression(pos) + { + ValueType = value.Type = VAL_Sound; + value.Int = val; + isresolved = true; + } + + FxConstant(FName val, const FScriptPosition &pos) : FxExpression(pos) + { + ValueType = value.Type = VAL_Name; + value.Int = val; + isresolved = true; } FxConstant(ExpVal cv, const FScriptPosition &pos) : FxExpression(pos) { value = cv; ValueType = cv.Type; + isresolved = true; + } + + FxConstant(const PClass *val, const FScriptPosition &pos) : FxExpression(pos) + { + value.pointer = (void*)val; + ValueType = val; + value.Type = VAL_Class; + isresolved = true; + } + + FxConstant(FState *state, const FScriptPosition &pos) : FxExpression(pos) + { + value.pointer = state; + ValueType = value.Type = VAL_State; + isresolved = true; } static FxExpression *MakeConstant(PSymbol *sym, const FScriptPosition &pos); @@ -704,10 +704,64 @@ public: }; +//========================================================================== +// +// +// +//========================================================================== + +class FxClassTypeCast : public FxExpression +{ + const PClass *desttype; + FxExpression *basex; + +public: + + FxClassTypeCast(const PClass *dtype, FxExpression *x); + ~FxClassTypeCast(); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self); +}; + +//========================================================================== +// +// Only used to resolve the old jump by index feature of DECORATE +// +//========================================================================== + +class FxStateByIndex : public FxExpression +{ + int index; + +public: + + FxStateByIndex(int i, const FScriptPosition &pos) : FxExpression(pos) + { + index = i; + } + FxExpression *Resolve(FCompileContext&); +}; + +//========================================================================== +// +// +// +//========================================================================== + +class FxMultiNameState : public FxExpression +{ + const PClass *scope; + TArray names; +public: + + FxMultiNameState(const char *statestring, const FScriptPosition &pos); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self); +}; + FxExpression *ParseExpression (FScanner &sc, PClass *cls); -int AddExpression (FxExpression *data); #endif diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 980a56332..e917f013a 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -51,7 +51,6 @@ #include "thingdef_exp.h" #include "autosegs.h" -extern int thingdef_terminate; int testglobalvar = 1337; // just for having one global variable to test with DEFINE_GLOBAL_VARIABLE(testglobalvar) @@ -74,19 +73,6 @@ DEFINE_MEMBER_VARIABLE(momx, AActor) DEFINE_MEMBER_VARIABLE(momy, AActor) DEFINE_MEMBER_VARIABLE(momz, AActor) -static TDeletingArray StateExpressions; - -int AddExpression (FxExpression *data) -{ - if (StateExpressions.Size()==0) - { - // StateExpressions[0] always is const 0; - FxExpression *data = new FxConstant(0, FScriptPosition()); - StateExpressions.Push (data); - } - return StateExpressions.Push (data); -} - //========================================================================== // // EvalExpression @@ -95,32 +81,44 @@ int AddExpression (FxExpression *data) //========================================================================== -bool IsExpressionConst(int id) +int EvalExpressionI (DWORD xi, AActor *self) { - if (StateExpressions.Size() <= (unsigned int)id) return false; + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; - return StateExpressions[id]->isConstant(); + return x->EvalExpression (self).GetInt(); } -int EvalExpressionI (int id, AActor *self) +int EvalExpressionCol (DWORD xi, AActor *self) { - if (StateExpressions.Size() <= (unsigned int)id) return 0; + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; - return StateExpressions[id]->EvalExpression (self).GetInt(); + return x->EvalExpression (self).GetColor(); } -double EvalExpressionF (int id, AActor *self) +FSoundID EvalExpressionSnd (DWORD xi, AActor *self) { - if (StateExpressions.Size() <= (unsigned int)id) return 0.f; + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; - return StateExpressions[id]->EvalExpression (self).GetFloat(); + return x->EvalExpression (self).GetSoundID(); } -fixed_t EvalExpressionFix (int id, AActor *self) +double EvalExpressionF (DWORD xi, AActor *self) { - if (StateExpressions.Size() <= (unsigned int)id) return 0; + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; - ExpVal val = StateExpressions[id]->EvalExpression (self); + return x->EvalExpression (self).GetFloat(); +} + +fixed_t EvalExpressionFix (DWORD xi, AActor *self) +{ + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; + + ExpVal val = x->EvalExpression (self); switch (val.Type) { @@ -133,6 +131,31 @@ fixed_t EvalExpressionFix (int id, AActor *self) } } +FName EvalExpressionName (DWORD xi, AActor *self) +{ + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; + + return x->EvalExpression (self).GetName(); +} + +const PClass * EvalExpressionClass (DWORD xi, AActor *self) +{ + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; + + return x->EvalExpression (self).GetClass(); +} + +FState *EvalExpressionState (DWORD xi, AActor *self) +{ + FxExpression *x = StateParams.Get(xi); + if (x == NULL) return 0; + + return x->EvalExpression (self).GetState(); +} + + //========================================================================== // // @@ -152,6 +175,21 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type) ret.Int = *(int*)address; break; + case VAL_Sound: + ret.Type = VAL_Sound; + ret.Int = *(FSoundID*)address; + break; + + case VAL_Name: + ret.Type = VAL_Name; + ret.Int = *(FName*)address; + break; + + case VAL_Color: + ret.Type = VAL_Color; + ret.Int = *(int*)address; + break; + case VAL_Bool: ret.Type = VAL_Int; ret.Int = *(bool*)address; @@ -186,62 +224,6 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type) return ret; } -//========================================================================== -// -// FScriptPosition::Message -// -//========================================================================== - -void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...) const -{ - FString composed; - - if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && !developer) return; - - if (message == NULL) - { - composed = "Bad syntax."; - } - else - { - va_list arglist; - va_start (arglist, message); - composed.VFormat (message, arglist); - va_end (arglist); - } - const char *type = ""; - int level = PRINT_HIGH; - - switch (severity) - { - default: - return; - - case MSG_WARNING: - type = "warning"; - break; - - case MSG_ERROR: - thingdef_terminate++; - type = "error"; - break; - - case MSG_DEBUG: - type = "message"; - break; - - case MSG_DEBUGLOG: - case MSG_LOG: - type = "message"; - level = PRINT_LOG; - break; - } - - Printf (level, "Script %s, \"%s\" line %d:\n%s\n", type, - FileName.GetChars(), ScriptLine, composed.GetChars()); -} - - //========================================================================== // // @@ -278,9 +260,37 @@ bool FxExpression::isConstant() const FxExpression *FxExpression::Resolve(FCompileContext &ctx) { + isresolved = true; return this; } + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxExpression::ResolveAsBoolean(FCompileContext &ctx) +{ + FxExpression *x = Resolve(ctx); + if (x != NULL) + { + switch (x->ValueType.Type) + { + case VAL_Sound: + case VAL_Color: + case VAL_Name: + x->ValueType = VAL_Int; + break; + + default: + break; + } + } + return x; +} + //========================================================================== // // @@ -1113,11 +1123,22 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) if (!ValueType.isNumeric() && !ValueType.isPointer()) { + if (left->ValueType.Type == right->ValueType.Type) + { + // compare other types? + if (left->ValueType == VAL_Sound || left->ValueType == VAL_Color || left->ValueType == VAL_Name) + { + left->ValueType = right->ValueType = VAL_Int; + goto cont; + } + } + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } - else if (left->isConstant() && right->isConstant()) +cont: + if (left->isConstant() && right->isConstant()) { int v; @@ -2405,4 +2426,403 @@ ExpVal FxGlobalFunctionCall::EvalExpression (AActor *self) if (Name == NAME_Sin) ret.Float = FIXED2FLOAT (finesine[angle>>ANGLETOFINESHIFT]); else ret.Float = FIXED2FLOAT (finecosine[angle>>ANGLETOFINESHIFT]); return ret; -} \ No newline at end of file +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxClassTypeCast::FxClassTypeCast(const PClass *dtype, FxExpression *x) +: FxExpression(x->ScriptPosition) +{ + desttype = dtype; + basex=x; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxClassTypeCast::~FxClassTypeCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->ValueType != VAL_Name) + { + ScriptPosition.Message(MSG_ERROR, "Cannot convert to class type"); + delete this; + return NULL; + } + + if (basex->isConstant()) + { + FName clsname = basex->EvalExpression(NULL).GetName(); + const PClass *cls; + + if (clsname != NAME_None || !ctx.isconst) + { + cls= PClass::FindClass(clsname); + if (cls == NULL) + { + if (!ctx.lax) + { + ScriptPosition.Message(MSG_ERROR,"Unknown class name '%s'", clsname.GetChars()); + delete this; + return NULL; + } + // Since this happens in released WADs it must pass without a terminal error... :( + ScriptPosition.Message(MSG_WARNING, + "Unknown class name '%s'", + clsname.GetChars(), desttype->TypeName.GetChars()); + } + else + { + if (!cls->IsDescendantOf(desttype)) + { + ScriptPosition.Message(MSG_ERROR,"class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); + delete this; + return NULL; + } + } + ScriptPosition.Message(MSG_DEBUG,"resolving '%s' as class name", clsname.GetChars()); + } + FxExpression *x = new FxConstant(cls, ScriptPosition); + delete this; + return x; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxClassTypeCast::EvalExpression (AActor *self) +{ + FName clsname = basex->EvalExpression(NULL).GetName(); + const PClass *cls = PClass::FindClass(clsname); + + if (!cls->IsDescendantOf(desttype)) + { + Printf("class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); + cls = NULL; + } + + ExpVal ret; + ret.Type = VAL_Class; + ret.pointer = (void*)cls; + return ret; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + if (ctx.cls->ActorInfo == NULL || ctx.cls->ActorInfo->NumOwnedStates == 0) + { + // This can't really happen + assert(false); + } + if (ctx.cls->ActorInfo->NumOwnedStates <= index) + { + ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d", + ctx.cls->TypeName.GetChars(), index); + delete this; + return NULL; + } + FxExpression *x = new FxConstant(ctx.cls->ActorInfo->OwnedStates + index, ScriptPosition); + delete this; + return x; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos) + :FxExpression(pos) +{ + FName scopename; + FString statestring = _statestring; + int scopeindex = statestring.IndexOf("::"); + + if (scopeindex >= 0) + { + scopename = FName(statestring, scopeindex, false); + statestring = statestring.Right(statestring.Len() - scopeindex - 2); + } + else + { + scopename = NULL; + } + names = MakeStateNameList(statestring); + names.Insert(0, scopename); + scope = NULL; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + if (names[0] == NAME_None) + { + scope = NULL; + } + else if (names[0] == NAME_Super) + { + scope = ctx.cls->ParentClass; + } + else + { + scope = PClass::FindClass(names[0]); + if (scope == NULL) + { + ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars()); + delete this; + return NULL; + } + else if (!scope->IsDescendantOf(ctx.cls)) + { + ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(),ctx.cls->TypeName.GetChars()); + delete this; + return NULL; + } + } + if (scope != NULL) + { + // If the label is class specific we can resolve it right here + if (scope->ActorInfo == NULL) + { + ScriptPosition.Message(MSG_ERROR, "'%s' has no actorinfo", names[0].GetChars()); + delete this; + return NULL; + } + FState *destination = scope->ActorInfo->FindState(names.Size()-1, &names[1], false); + if (destination == NULL) + { + ScriptPosition.Message(ctx.lax? MSG_WARNING:MSG_ERROR, "Unknown state jump destination"); + delete this; + return NULL; + } + FxExpression *x = new FxConstant(destination, ScriptPosition); + delete this; + return x; + } + names.Delete(0); + names.ShrinkToFit(); + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxMultiNameState::EvalExpression (AActor *self) +{ + ExpVal ret; + ret.Type = VAL_State; + ret.pointer = self->GetClass()->ActorInfo->FindState(names.Size(), &names[0]); + if (ret.pointer == NULL) + { + const char *dot=""; + Printf("Jump target '"); + for (int i=0;iGetClass()->TypeName.GetChars()); + } + return ret; +} + + + +//========================================================================== +// +// NOTE: I don't expect any of the following to survive Doomscript ;) +// +//========================================================================== + +FStateExpressions StateParams; + + +//========================================================================== +// +// +// +//========================================================================== + +FStateExpressions::~FStateExpressions() +{ + for(unsigned i=0; i= 0 && num < int(Size())) + { + assert(expressions[num].expr == NULL || expressions[num].cloned); + expressions[num].expr = x; + expressions[num].cloned = false; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FStateExpressions::Copy(int dest, int src, int cnt) +{ + for(int i=0; iResolve(ctx); + if (expressions[i].expr == NULL) + { + errorcount++; + } + else if (expressions[i].constant && !expressions[i].expr->isConstant()) + { + expressions[i].expr->ScriptPosition.Message(MSG_ERROR, "Constant expression expected"); + errorcount++; + } + } + } + + for(unsigned i=0; iisresolved) + { + expressions[i].expr->ScriptPosition.Message(MSG_ERROR, "Expression at index %d not resolved\n", i); + errorcount++; + } + } + } + + return errorcount; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FStateExpressions::Get(int num) +{ + if (num >= 0 && num < int(Size())) + return expressions[num].expr; + return NULL; +} + diff --git a/src/thingdef/thingdef_main.cpp b/src/thingdef/thingdef_main.cpp deleted file mode 100644 index c33961a00..000000000 --- a/src/thingdef/thingdef_main.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* -** decorations.cpp -** Loads custom actors out of DECORATE lumps. -** -**--------------------------------------------------------------------------- -** Copyright 2002-2007 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. -** -** 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. -**--------------------------------------------------------------------------- -** -*/ - -// HEADER FILES ------------------------------------------------------------ - -#include "actor.h" -#include "info.h" -#include "sc_man.h" -#include "tarray.h" -#include "w_wad.h" -#include "templates.h" -#include "s_sound.h" -#include "cmdlib.h" -#include "thingdef.h" -#include "i_system.h" - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -void InitThingdef(); -void ParseActor(FScanner &sc); -void FinishThingdef(); -void ParseOldDecoration(FScanner &sc, EDefinitionType def); - -// STATIC FUNCTION PROTOTYPES -------------------------------------------- -PSymbolTable GlobalSymbols; - -int thingdef_terminate; - -//========================================================================== -// -// ParseDecorate -// -// Parses a single DECORATE lump -// -//========================================================================== - -static void ParseDecorate (FScanner &sc) -{ - // Get actor class name. - for(;;) - { - FScanner::SavedPos pos = sc.SavePos(); - if (!sc.GetToken ()) - { - return; - } - switch (sc.TokenType) - { - case TK_Include: - { - sc.MustGetString(); - FScanner newscanner; - newscanner.Open(sc.String); - ParseDecorate(newscanner); - break; - } - - case TK_Const: - ParseConstant (sc, &GlobalSymbols, NULL); - break; - - case TK_Enum: - ParseEnum (sc, &GlobalSymbols, NULL); - break; - - case TK_Pickup: - ParseOldDecoration (sc, DEF_Pickup); - break; - - case TK_Breakable: - ParseOldDecoration (sc, DEF_BreakableDecoration); - break; - - case TK_Projectile: - ParseOldDecoration (sc, DEF_Projectile); - break; - - case TK_Native: - ParseVariable(sc, &GlobalSymbols, NULL); - break; - - case ';': - // ';' is the start of a comment in the non-cmode parser which - // is used to parse parts of the DECORATE lump. If we don't add - // a check here the user will only get weird non-informative - // error messages if a semicolon is found. - sc.ScriptError("Unexpected ';'"); - break; - - case TK_Identifier: - // 'ACTOR' cannot be a keyword because it is also needed as a class identifier - // so let's do a special case for this. - if (sc.Compare("ACTOR")) - { - ParseActor (sc); - break; - } - - default: - // without the option of game filters following, anything but an opening brace - // here means a syntax error. - sc.MustGetStringName("{"); - sc.RestorePos(pos); - ParseOldDecoration(sc, DEF_Decoration); - break; - } - } -} - -//========================================================================== -// -// LoadDecorations -// -// Called from FActor::StaticInit() -// -//========================================================================== - -void LoadDecorations () -{ - int lastlump, lump; - - InitThingdef(); - lastlump = 0; - while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1) - { - FScanner sc(lump); - ParseDecorate (sc); - } - if (thingdef_terminate) - { - I_Error("%d errors found", thingdef_terminate); - } - FinishThingdef(); -} - diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 22813851b..77b6f82a5 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -52,7 +52,112 @@ #include "i_system.h" #include "thingdef_exp.h" #include "w_wad.h" +#include "v_video.h" +void ParseOldDecoration(FScanner &sc, EDefinitionType def); + +//========================================================================== +// +// ParseParameter +// +// Parses aparameter - either a default in a function declaration +// or an argument in a function call. +// +//========================================================================== + +FxExpression *ParseParameter(FScanner &sc, PClass *cls, char type, bool constant) +{ + FxExpression *x = NULL; + int v; + + switch(type) + { + case 'S': + case 's': // Sound name + sc.MustGetString(); + x = new FxConstant(FSoundID(sc.String), sc); + break; + + case 'M': + case 'm': // Actor name + sc.SetEscape(true); + sc.MustGetString(); + sc.SetEscape(false); + x = new FxClassTypeCast(RUNTIME_CLASS(AActor), new FxConstant(FName(sc.String), sc)); + break; + + case 'T': + case 't': // String + sc.SetEscape(true); + sc.MustGetString(); + sc.SetEscape(false); + x = new FxConstant(sc.String[0]? FName(sc.String) : NAME_None, sc); + break; + + case 'C': + case 'c': // Color + sc.MustGetString (); + if (sc.Compare("none")) + { + v = -1; + } + else if (sc.Compare("")) + { + v = 0; + } + else + { + int c = V_GetColor (NULL, sc.String); + // 0 needs to be the default so we have to mark the color. + v = MAKEARGB(1, RPART(c), GPART(c), BPART(c)); + } + { + ExpVal val; + val.Type = VAL_Color; + val.Int = v; + x = new FxConstant(val, sc); + break; + } + + case 'L': + case 'l': + { + // This forces quotation marks around the state name. + sc.MustGetToken(TK_StringConst); + if (sc.String[0] == 0 || sc.Compare("None")) + { + x = new FxConstant((FState*)NULL, sc); + } + else if (sc.Compare("*")) + { + if (constant) + { + x = new FxConstant((FState*)(intptr_t)-1, sc); + } + else sc.ScriptError("Invalid state name '*'"); + } + else + { + x = new FxMultiNameState(sc.String, sc); + } + break; + } + + case 'X': + case 'x': + x = ParseExpression (sc, cls); + if (constant && !x->isConstant()) + { + sc.ScriptError("Default parameter must be constant."); + } + break; + + default: + assert(false); + return NULL; + } + return x; +} //========================================================================== // @@ -62,7 +167,7 @@ // //========================================================================== -void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) +static void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) { // Read the type and make sure it's int or float. if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) @@ -108,7 +213,7 @@ void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) // //========================================================================== -void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) +static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) { int currvalue = 0; @@ -148,7 +253,7 @@ void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) // //========================================================================== -void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) +static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) { FExpressionType valuetype; @@ -227,7 +332,7 @@ void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) // Parses a flag name // //========================================================================== -void ParseActorFlag (FScanner &sc, Baggage &bag, int mod) +static void ParseActorFlag (FScanner &sc, Baggage &bag, int mod) { sc.MustGetString (); @@ -335,7 +440,7 @@ static int ParseMorphStyle (FScanner &sc) // //========================================================================== -FState *CheckState(FScanner &sc, PClass *type) +static FState *CheckState(FScanner &sc, PClass *type) { int v=0; @@ -388,7 +493,7 @@ FState *CheckState(FScanner &sc, PClass *type) // //========================================================================== -bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag) +static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag) { static TArray params; static TArray strings; @@ -417,7 +522,8 @@ bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Ba if (sc.CheckString ("(")) { - conv.i = 0x40000000 | ParseExpression (sc, false, bag.Info->Class); + FxExpression *x = ParseExpression(sc, bag.Info->Class); + conv.i = 0x40000000 | StateParams.Add(x, bag.Info->Class, false); params.Push(conv); sc.MustGetStringName(")"); break; @@ -564,7 +670,7 @@ bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Ba // //========================================================================== -void ParseActorProperty(FScanner &sc, Baggage &bag) +static void ParseActorProperty(FScanner &sc, Baggage &bag) { static const char *statenames[] = { "Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn", @@ -615,44 +721,372 @@ void ParseActorProperty(FScanner &sc, Baggage &bag) } } - //========================================================================== // -// Finalizes an actor definition +// ActorActionDef +// +// Parses an action function definition. A lot of this is essentially +// documentation in the declaration for when I have a proper language +// ready. // //========================================================================== -void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag) +static void ParseActionDef (FScanner &sc, PClass *cls) { - AActor *defaults = (AActor*)info->Class->Defaults; + enum + { + OPTIONAL = 1 + }; + + AFuncDesc *afd; + FName funcname; + FString args; + TArray DefaultParams; + bool hasdefaults = false; + + if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) + { + sc.ScriptError ("action functions can only be imported by internal class and actor definitions!"); + } + + sc.MustGetToken(TK_Native); + sc.MustGetToken(TK_Identifier); + funcname = sc.String; + afd = FindFunction(sc.String); + if (afd == NULL) + { + sc.ScriptError ("The function '%s' has not been exported from the executable.", sc.String); + } + sc.MustGetToken('('); + if (!sc.CheckToken(')')) + { + while (sc.TokenType != ')') + { + int flags = 0; + char type = '@'; + + // Retrieve flags before type name + for (;;) + { + if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) + { + } + else + { + break; + } + } + // Read the variable type + sc.MustGetAnyToken(); + switch (sc.TokenType) + { + case TK_Bool: + case TK_Int: + case TK_Float: + type = 'x'; + break; + + case TK_Sound: type = 's'; break; + case TK_String: type = 't'; break; + case TK_Name: type = 't'; break; + case TK_State: type = 'l'; break; + case TK_Color: type = 'c'; break; + case TK_Class: + sc.MustGetToken('<'); + sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care + sc.MustGetToken('>'); + type = 'm'; + break; + case TK_Ellipsis: + type = '+'; + sc.MustGetToken(')'); + sc.UnGet(); + break; + default: + sc.ScriptError ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); + break; + } + // Read the optional variable name + if (!sc.CheckToken(',') && !sc.CheckToken(')')) + { + sc.MustGetToken(TK_Identifier); + } + else + { + sc.UnGet(); + } + + FxExpression *def; + if (sc.CheckToken('=')) + { + hasdefaults = true; + flags |= OPTIONAL; + def = ParseParameter(sc, cls, type, true); + } + else + { + def = NULL; + } + DefaultParams.Push(def); + + if (!(flags & OPTIONAL) && type != '+') + { + type -= 'a' - 'A'; + } + args += type; + sc.MustGetAnyToken(); + if (sc.TokenType != ',' && sc.TokenType != ')') + { + sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); + } + } + } + sc.MustGetToken(';'); + PSymbolActionFunction *sym = new PSymbolActionFunction(funcname); + sym->Arguments = args; + sym->Function = afd->Function; + if (hasdefaults) + { + sym->defaultparameterindex = StateParams.Size(); + for(unsigned int i = 0; i < DefaultParams.Size(); i++) + { + StateParams.Add(DefaultParams[i], cls, true); + } + } + else + { + sym->defaultparameterindex = -1; + } + if (cls->Symbols.AddSymbol (sym) == NULL) + { + delete sym; + sc.ScriptError ("'%s' is already defined in class '%s'.", + funcname.GetChars(), cls->TypeName.GetChars()); + } +} + +//========================================================================== +// +// Starts a new actor definition +// +//========================================================================== +static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag) +{ + FName typeName; + FName parentName; + FName replaceName; + bool native = false; + int DoomEdNum = -1; + + // Get actor name + sc.MustGetString(); + + char *colon = strchr(sc.String, ':'); + if (colon != NULL) + { + *colon++ = 0; + } + + typeName = sc.String; + + // Do some tweaking so that a definition like 'Actor:Parent' which is read as a single token is recognized as well + // without having resort to C-mode (which disallows periods in actor names.) + if (colon == NULL) + { + sc.MustGetString (); + if (sc.String[0]==':') + { + colon = sc.String + 1; + } + } + + if (colon != NULL) + { + if (colon[0] == 0) + { + sc.MustGetString (); + colon = sc.String; + } + } + + if (colon == NULL) + { + sc.UnGet(); + } + + parentName = colon; + + // Check for "replaces" + if (sc.CheckString ("replaces")) + { + // Get actor name + sc.MustGetString (); + replaceName = sc.String; + } + + // Now, after the actor names have been parsed, it is time to switch to C-mode + // for the rest of the actor definition. + sc.SetCMode (true); + if (sc.CheckNumber()) + { + if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number; + else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]"); + } + + if (sc.CheckString("native")) + { + native = true; + } try { - bag.statedef.FinishStates (info, defaults, bag.StateArray); + FActorInfo *info = CreateNewActor(sc, typeName, parentName, replaceName, DoomEdNum, native); + ResetBaggage (bag, info->Class->ParentClass); + bag->Info = info; + bag->Lumpnum = sc.LumpNum; +#ifdef _DEBUG + bag->ClassName = typeName; +#endif + return info; } catch (CRecoverableError &err) { - sc.ScriptError(err.GetMessage()); - } - bag.statedef.InstallStates (info, defaults); - bag.StateArray.Clear (); - if (bag.DropItemSet) - { - if (bag.DropItemList == NULL) - { - if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0) - { - info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0); - } - } - else - { - info->Class->Meta.SetMetaInt (ACMETA_DropItems, - StoreDropItemChain(bag.DropItemList)); - } - } - if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - defaults->flags |= MF_SPECIAL; + sc.ScriptError("%s", err.GetMessage()); + return NULL; + } +} + +//========================================================================== +// +// Reads an actor definition +// +//========================================================================== +static void ParseActor(FScanner &sc) +{ + FActorInfo * info=NULL; + Baggage bag; + + info = ParseActorHeader(sc, &bag); + sc.MustGetToken('{'); + while (sc.MustGetAnyToken(), sc.TokenType != '}') + { + switch (sc.TokenType) + { + case TK_Action: + ParseActionDef (sc, info->Class); + break; + + case TK_Const: + ParseConstant (sc, &info->Class->Symbols, info->Class); + break; + + case TK_Enum: + ParseEnum (sc, &info->Class->Symbols, info->Class); + break; + + case TK_Native: + ParseVariable (sc, &info->Class->Symbols, info->Class); + break; + + case TK_Identifier: + // other identifier related checks here + case TK_Projectile: // special case: both keyword and property name + ParseActorProperty(sc, bag); + break; + + case '+': + case '-': + ParseActorFlag(sc, bag, sc.TokenType); + break; + + default: + sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars()); + break; + } + } + FinishActor(sc, info, bag); + sc.SetCMode (false); +} + + + +//========================================================================== +// +// ParseDecorate +// +// Parses a single DECORATE lump +// +//========================================================================== + +void ParseDecorate (FScanner &sc) +{ + // Get actor class name. + for(;;) + { + FScanner::SavedPos pos = sc.SavePos(); + if (!sc.GetToken ()) + { + return; + } + switch (sc.TokenType) + { + case TK_Include: + { + sc.MustGetString(); + FScanner newscanner; + newscanner.Open(sc.String); + ParseDecorate(newscanner); + break; + } + + case TK_Const: + ParseConstant (sc, &GlobalSymbols, NULL); + break; + + case TK_Enum: + ParseEnum (sc, &GlobalSymbols, NULL); + break; + + case TK_Pickup: + ParseOldDecoration (sc, DEF_Pickup); + break; + + case TK_Breakable: + ParseOldDecoration (sc, DEF_BreakableDecoration); + break; + + case TK_Projectile: + ParseOldDecoration (sc, DEF_Projectile); + break; + + case TK_Native: + ParseVariable(sc, &GlobalSymbols, NULL); + break; + + case ';': + // ';' is the start of a comment in the non-cmode parser which + // is used to parse parts of the DECORATE lump. If we don't add + // a check here the user will only get weird non-informative + // error messages if a semicolon is found. + sc.ScriptError("Unexpected ';'"); + break; + + case TK_Identifier: + // 'ACTOR' cannot be a keyword because it is also needed as a class identifier + // so let's do a special case for this. + if (sc.Compare("ACTOR")) + { + ParseActor (sc); + break; + } + + default: + // without the option of game filters following, anything but an opening brace + // here means a syntax error. + sc.MustGetStringName("{"); + sc.RestorePos(pos); + ParseOldDecoration(sc, DEF_Decoration); + break; + } } } diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 030491290..a20edd461 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -55,10 +55,7 @@ #include "s_sound.h" #include "i_system.h" #include "colormatcher.h" - -TArray StateParameters; - - +#include "thingdef_exp.h" //========================================================================== //*** @@ -66,13 +63,9 @@ TArray StateParameters; // creates an empty parameter list for a parameterized function call // //========================================================================== -int PrepareStateParameters(FState * state, int numparams) +static int PrepareStateParameters(FState * state, int numparams, const PClass *cls) { - int paramindex=StateParameters.Size(); - int i, v; - - v=0; - for(i=0;iParameterIndex = paramindex+1; return paramindex; } @@ -94,21 +87,16 @@ bool DoActionSpecials(FScanner &sc, FState & state, bool multistate, int * state if (special > 0 && min_args >= 0) { - int paramindex=PrepareStateParameters(&state, 6); + int paramindex=PrepareStateParameters(&state, 6, bag.Info->Class); - // The function expects the special to be passed as expression so we - // have to convert it. - specname.Format("%d", special); - FScanner sc2; - sc2.OpenMem("", (char*)specname.GetChars(), int(specname.Len())); - StateParameters[paramindex] = ParseExpression(sc2, false, bag.Info->Class); + StateParams.Set(paramindex, new FxConstant(special, sc)); // Make this consistent with all other parameter parsing if (sc.CheckToken('(')) { for (i = 0; i < 5;) { - StateParameters[paramindex+i+1] = ParseExpression (sc, false, bag.Info->Class); + StateParams.Set(paramindex+i+1, ParseExpression (sc, bag.Info->Class)); i++; if (!sc.CheckToken (',')) break; } @@ -351,23 +339,25 @@ do_stop: } } - int paramindex = PrepareStateParameters(&state, numparams); + int paramindex = PrepareStateParameters(&state, numparams, bag.Info->Class); int paramstart = paramindex; bool varargs = params[numparams - 1] == '+'; + int varargcount=0; if (varargs) { - StateParameters[paramindex++] = 0; + varargcount++; + paramindex++; } else if (afd->defaultparameterindex > -1) { - memcpy(&StateParameters[paramindex], &StateParameters[afd->defaultparameterindex], - afd->Arguments.Len() * sizeof (StateParameters[0])); + StateParams.Copy(paramindex, afd->defaultparameterindex, int(afd->Arguments.Len())); } while (*params) { + FxExpression *x; if ((*params == 'l' || *params == 'L') && sc.CheckNumber()) { // Special case: State label as an offset @@ -382,19 +372,18 @@ do_stop: sc.ScriptError("Negative jump offsets are not allowed"); } - int minreq=count+v; - if (minreq>minrequiredstate) minrequiredstate=minreq; + x = new FxStateByIndex(count+v, sc); } else { // Use the generic parameter parser for everything else - v = ParseParameter(sc, bag.Info->Class, *params, false); + x = ParseParameter(sc, bag.Info->Class, *params, false); } - StateParameters[paramindex++] = v; + StateParams.Set(paramindex++, x); params++; if (varargs) { - StateParameters[paramstart]++; + varargcount++; } if (*params) { @@ -402,11 +391,11 @@ do_stop: { if (sc.CheckString(")")) { + StateParams.Set(paramstart, new FxConstant(varargcount, sc)); goto endofstate; } params--; - v = 0; - StateParameters.Push(v); + StateParams.Reserve(1, bag.Info->Class); } else if ((islower(*params) || *params=='!') && sc.CheckString(")")) { @@ -451,10 +440,6 @@ endofstate: count++; } } - if (count<=minrequiredstate) - { - sc.ScriptError("A_Jump offset out of range in %s", actor->Class->TypeName.GetChars()); - } sc.SetEscape(true); // re-enable escape sequences return count; } diff --git a/src/thingdef/thingdef_type.h b/src/thingdef/thingdef_type.h index cadc3c3dd..2544c6931 100644 --- a/src/thingdef/thingdef_type.h +++ b/src/thingdef/thingdef_type.h @@ -8,13 +8,18 @@ enum ExpValType { - VAL_Int, - VAL_Float, - VAL_Unknown, - VAL_Array, - VAL_Object, - VAL_Class, - VAL_Pointer, + VAL_Int, // integer number + VAL_Float, // floating point number + VAL_Unknown, // nothing + VAL_Array, // Array (very limited right now) + VAL_Object, // Object reference + VAL_Class, // Class reference + VAL_Pointer, // Dereferenced variable (only used for addressing arrays for now.) + VAL_Sound, // Sound identifier. Internally it's an int. + VAL_Name, // A Name + VAL_MultiName, // Multiple names for multi-label states + VAL_Color, // A color. + VAL_State, // A State pointer // only used for accessing external variables to ensure proper conversion VAL_Fixed, diff --git a/wadsrc/static/actors/chex/chexweapons.txt b/wadsrc/static/actors/chex/chexweapons.txt index 5c5458aa6..4fa7eec56 100644 --- a/wadsrc/static/actors/chex/chexweapons.txt +++ b/wadsrc/static/actors/chex/chexweapons.txt @@ -83,10 +83,11 @@ actor PhasingZorcher : PlasmaRifle 2004 PLSG B 20 A_ReFire Goto Ready Flash: - PLSF A 0 A_Jump(128, 2) - PLSF A 4 Bright A_Light1 + PLSF A 0 A_Jump(128, "Flash2") + PLSF A 3 Bright A_Light1 Goto LightDone - PLSF B 4 Bright A_Light1 + Flash2: + PLSF B 3 Bright A_Light1 Goto LightDone } } diff --git a/zdoom.vcproj b/zdoom.vcproj index 417293330..7bcee86e3 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1075,10 +1075,6 @@ RelativePath=".\src\thingdef\thingdef_expression.cpp" > - -