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()); + sc.ScriptError("%s", err.GetMessage()); + return NULL; } - bag.statedef.InstallStates (info, defaults); - bag.StateArray.Clear (); - if (bag.DropItemSet) +} + +//========================================================================== +// +// 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 != '}') { - if (bag.DropItemList == NULL) + switch (sc.TokenType) { - if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0) + 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")) { - info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0); + ParseActor (sc); + break; } - } - else - { - info->Class->Meta.SetMetaInt (ACMETA_DropItems, - StoreDropItemChain(bag.DropItemList)); + + 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; } - } - if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - defaults->flags |= MF_SPECIAL; } } 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" > - -