From 954955c5a5c7025d79336e017fd6f7cef01b4fd4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 22 Sep 2008 18:55:29 +0000 Subject: [PATCH] - Used the one unused byte in the state structure as a flag to tell what type the NextState parameter is. The code did some rather unsafe checks with it to determine its type. - moved all state related code into a new file: p_states.cpp. - merged all FindState functions. All the different variations are now inlined and call the same function to do the real work. SVN r1243 (trunk) --- docs/rh-log.txt | 8 + src/actor.h | 14 +- src/d_dehacked.cpp | 43 +- src/info.cpp | 319 ----------- src/info.h | 9 +- src/p_acs.cpp | 7 +- src/p_mobj.cpp | 6 +- src/p_states.cpp | 814 +++++++++++++++++++++++++++ src/thingdef/olddecorations.cpp | 87 ++- src/thingdef/thingdef.cpp | 10 +- src/thingdef/thingdef.h | 85 ++- src/thingdef/thingdef_parse.cpp | 7 +- src/thingdef/thingdef_properties.cpp | 3 +- src/thingdef/thingdef_states.cpp | 486 +--------------- wadsrc/static/actors/constants.txt | 3 +- zdoom.vcproj | 4 + 16 files changed, 1001 insertions(+), 904 deletions(-) create mode 100644 src/p_states.cpp diff --git a/docs/rh-log.txt b/docs/rh-log.txt index c3ee999f7..727658133 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,11 @@ +September 22, 2008 (Changes by Graf Zahl) +- Used the one unused byte in the state structure as a flag to tell what type + the NextState parameter is. The code did some rather unsafe checks with it + to determine its type. +- moved all state related code into a new file: p_states.cpp. +- merged all FindState functions. All the different variations are now inlined + and call the same function to do the real work. + September 21, 2008 (Changes by Graf Zahl) - did some code cleanup and reorganization in thingdef.cpp. - Replaced the translation parser for TEXTURES with FRemapTable::AddToTranslation. diff --git a/src/actor.h b/src/actor.h index adf6f635d..0dfa13b7e 100644 --- a/src/actor.h +++ b/src/actor.h @@ -785,8 +785,18 @@ public: bool isFast(); void SetIdle(); - FState *FindState (FName label) const; - FState *FindState (FName label, FName sublabel, bool exact = false) const; + FState *FindState (FName label) const + { + return GetClass()->ActorInfo->FindState(1, &label); + } + + FState *FindState (FName label, FName sublabel, bool exact = false) const + { + FName names[]={label, sublabel}; + return GetClass()->ActorInfo->FindState(2, &label, exact); + } + + bool HasSpecialDeathStates () const; }; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index d3e20387b..4a857219d 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -682,6 +682,7 @@ static int PatchThing (int thingy) bool hadHeight = false; bool hadTranslucency = false; bool hadStyle = false; + FStateDefinitions statedef; bool patchedStates = false; int oldflags; const PClass *type; @@ -813,36 +814,36 @@ static int PatchThing (int thingy) if (type != NULL && !patchedStates) { - MakeStateDefines(type->ActorInfo->StateList); + statedef.MakeStateDefines(type); patchedStates = true; } if (!strnicmp (Line1, "Initial", 7)) - AddState("Spawn", state ? state : GetDefault()->SpawnState); + statedef.AddState("Spawn", state ? state : GetDefault()->SpawnState); else if (!strnicmp (Line1, "First moving", 12)) - AddState("See", state); + statedef.AddState("See", state); else if (!strnicmp (Line1, "Injury", 6)) - AddState("Pain", state); + statedef.AddState("Pain", state); else if (!strnicmp (Line1, "Close attack", 12)) { if (thingy != 1) // Not for players! { - AddState("Melee", state); + statedef.AddState("Melee", state); } } else if (!strnicmp (Line1, "Far attack", 10)) { if (thingy != 1) // Not for players! { - AddState("Missile", state); + statedef.AddState("Missile", state); } } else if (!strnicmp (Line1, "Death", 5)) - AddState("Death", state); + statedef.AddState("Death", state); else if (!strnicmp (Line1, "Exploding", 9)) - AddState("XDeath", state); + statedef.AddState("XDeath", state); else if (!strnicmp (Line1, "Respawn", 7)) - AddState("Raise", state); + statedef.AddState("Raise", state); } else if (stricmp (Line1 + linelen - 6, " sound") == 0) { @@ -1048,7 +1049,7 @@ static int PatchThing (int thingy) } if (patchedStates) { - InstallStates(type->ActorInfo, info); + statedef.InstallStates(type->ActorInfo, info); } } @@ -1359,6 +1360,7 @@ static int PatchWeapon (int weapNum) AWeapon *info; BYTE dummy[sizeof(AWeapon)]; bool patchedStates = false; + FStateDefinitions statedef; if (weapNum >= 0 && weapNum < 9) { @@ -1385,20 +1387,20 @@ static int PatchWeapon (int weapNum) if (type != NULL && !patchedStates) { - MakeStateDefines(type->ActorInfo->StateList); + statedef.MakeStateDefines(type); patchedStates = true; } if (strnicmp (Line1, "Deselect", 8) == 0) - AddState("Select", state); + statedef.AddState("Select", state); else if (strnicmp (Line1, "Select", 6) == 0) - AddState("Deselect", state); + statedef.AddState("Deselect", state); else if (strnicmp (Line1, "Bobbing", 7) == 0) - AddState("Ready", state); + statedef.AddState("Ready", state); else if (strnicmp (Line1, "Shooting", 8) == 0) - AddState("Fire", state); + statedef.AddState("Fire", state); else if (strnicmp (Line1, "Firing", 6) == 0) - AddState("Flash", state); + statedef.AddState("Flash", state); } else if (stricmp (Line1, "Ammo type") == 0) { @@ -1455,7 +1457,7 @@ static int PatchWeapon (int weapNum) if (patchedStates) { - InstallStates(type->ActorInfo, info); + statedef.InstallStates(type->ActorInfo, info); } return result; @@ -2582,13 +2584,14 @@ void FinishDehPatch () memcpy (defaults2, defaults1, sizeof(AActor)); // Make a copy the state labels - MakeStateDefines(type->ActorInfo->StateList); if (!type->IsDescendantOf(RUNTIME_CLASS(AInventory))) { // If this is a hacked non-inventory item we must also copy AInventory's special states - AddStateDefines(RUNTIME_CLASS(AInventory)->ActorInfo->StateList); + FStateDefinitions statedef; + statedef.MakeStateDefines(type); + statedef.AddStateDefines(RUNTIME_CLASS(AInventory)->ActorInfo->StateList); + statedef.InstallStates(subclass->ActorInfo, defaults2); } - InstallStates(subclass->ActorInfo, defaults2); // Use the DECORATE replacement feature to redirect all spawns // of the original class to the new one. diff --git a/src/info.cpp b/src/info.cpp index 043a16b38..4487d7ac7 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -51,122 +51,6 @@ extern void LoadDecorations (); -// Each state is owned by an actor. Actors can own any number of -// states, but a single state cannot be owned by more than one -// actor. States are archived by recording the actor they belong -// to and the index into that actor's list of states. - -// For NULL states, which aren't owned by any actor, the owner -// is recorded as AActor with the following state. AActor should -// never actually have this many states of its own, so this -// is (relatively) safe. - -#define NULL_STATE_INDEX 127 - -//========================================================================== -// -// -//========================================================================== - -FArchive &operator<< (FArchive &arc, FState *&state) -{ - const PClass *info; - - if (arc.IsStoring ()) - { - if (state == NULL) - { - arc.UserWriteClass (RUNTIME_CLASS(AActor)); - arc.WriteCount (NULL_STATE_INDEX); - return arc; - } - - info = FState::StaticFindStateOwner (state); - - if (info != NULL) - { - arc.UserWriteClass (info); - arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates)); - } - else - { - /* this was never working as intended. - I_Error ("Cannot find owner for state %p:\n" - "%s %c%c %3d [%p] -> %p", state, - sprites[state->sprite].name, - state->GetFrame() + 'A', - state->GetFullbright() ? '*' : ' ', - state->GetTics(), - state->GetAction(), - state->GetNextState()); - */ - } - } - else - { - const PClass *info; - DWORD ofs; - - arc.UserReadClass (info); - ofs = arc.ReadCount (); - if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor)) - { - state = NULL; - } - else if (info->ActorInfo != NULL) - { - state = info->ActorInfo->OwnedStates + ofs; - } - else - { - state = NULL; - } - } - return arc; -} - -//========================================================================== -// -// Find the actor that a state belongs to. -// -//========================================================================== - -const PClass *FState::StaticFindStateOwner (const FState *state) -{ - for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i) - { - FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo; - if (state >= info->OwnedStates && - state < info->OwnedStates + info->NumOwnedStates) - { - return info->Class; - } - } - - return NULL; -} - -//========================================================================== -// -// Find the actor that a state belongs to, but restrict the search to -// the specified type and its ancestors. -// -//========================================================================== - -const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info) -{ - while (info != NULL) - { - if (state >= info->OwnedStates && - state < info->OwnedStates + info->NumOwnedStates) - { - return info->Class; - } - info = info->Class->ParentClass->ActorInfo; - } - return NULL; -} - //========================================================================== // // @@ -344,209 +228,6 @@ void FActorInfo::SetPainChance(FName type, int chance) } } -//========================================================================== -// -// -//========================================================================== - -FStateLabel *FStateLabels::FindLabel (FName label) -{ - return const_cast(BinarySearch (Labels, NumLabels, &FStateLabel::Label, label)); -} - -void FStateLabels::Destroy () -{ - for(int i=0; iDestroy(); - free (Labels[i].Children); // These are malloc'd, not new'd! - Labels[i].Children=NULL; - } - } -} - - -//=========================================================================== -// -// HasStates -// -// Checks whether the actor has special death states. -// -//=========================================================================== - -bool AActor::HasSpecialDeathStates () const -{ - const FActorInfo *info = GetClass()->ActorInfo; - - if (info->StateList != NULL) - { - FStateLabel *slabel = info->StateList->FindLabel (NAME_Death); - if (slabel != NULL && slabel->Children != NULL) - { - for(int i=0;iChildren->NumLabels;i++) - { - if (slabel->Children->Labels[i].State != NULL) return true; - } - } - } - return false; -} - -//=========================================================================== -// -// FindState (one name version) -// -// Finds a state with the exact specified name. -// -//=========================================================================== - -FState *AActor::FindState (FName label) const -{ - const FActorInfo *info = GetClass()->ActorInfo; - - if (info->StateList != NULL) - { - FStateLabel *slabel = info->StateList->FindLabel (label); - if (slabel != NULL) - { - return slabel->State; - } - } - return NULL; -} - -//=========================================================================== -// -// FindState (two name version) -// -//=========================================================================== - -FState *AActor::FindState (FName label, FName sublabel, bool exact) const -{ - const FActorInfo *info = GetClass()->ActorInfo; - - if (info->StateList != NULL) - { - FStateLabel *slabel = info->StateList->FindLabel (label); - if (slabel != NULL) - { - if (slabel->Children != NULL) - { - FStateLabel *slabel2 = slabel->Children->FindLabel(sublabel); - if (slabel2 != NULL) - { - return slabel2->State; - } - } - if (!exact) return slabel->State; - } - } - return NULL; -} - -//=========================================================================== -// -// FindState (multiple names version) -// -// Finds a state that matches as many of the supplied names as possible. -// A state with more names than those provided does not match. -// A state with fewer names can match if there are no states with the exact -// same number of names. -// -// The search proceeds like this. For the current class, keeping matching -// names until there are no more. If both the argument list and the state -// are out of names, it's an exact match, so return it. If the state still -// has names, ignore it. If the argument list still has names, remember it. -// -//=========================================================================== -FState *FActorInfo::FindState (FName name) const -{ - return FindState(1, &name); -} - -FState *FActorInfo::FindState (int numnames, FName *names, bool exact) const -{ - FStateLabels *labels = StateList; - FState *best = NULL; - - if (labels != NULL) - { - int count = 0; - FStateLabel *slabel = NULL; - FName label; - - // Find the best-matching label for this class. - while (labels != NULL && count < numnames) - { - label = *names++; - slabel = labels->FindLabel (label); - - if (slabel != NULL) - { - count++; - labels = slabel->Children; - best = slabel->State; - } - else - { - break; - } - } - if (count < numnames && exact) return NULL; - } - return best; -} - -//========================================================================== -// -// Creates a list of names from a string. Dots are used as separator -// -//========================================================================== - -void MakeStateNameList(const char * fname, TArray * out) -{ - FName firstpart, secondpart; - char * c; - - // Handle the old names for the existing death states - char * name = copystring(fname); - firstpart = strtok(name, "."); - switch (firstpart) - { - case NAME_Burn: - firstpart = NAME_Death; - secondpart = NAME_Fire; - break; - case NAME_Ice: - firstpart = NAME_Death; - secondpart = NAME_Ice; - break; - case NAME_Disintegrate: - firstpart = NAME_Death; - secondpart = NAME_Disintegrate; - break; - case NAME_XDeath: - firstpart = NAME_Death; - secondpart = NAME_Extreme; - break; - } - - out->Clear(); - out->Push(firstpart); - if (secondpart!=NAME_None) out->Push(secondpart); - - while ((c = strtok(NULL, "."))!=NULL) - { - FName cc = c; - out->Push(cc); - } - delete [] name; -} - - - //========================================================================== // // diff --git a/src/info.h b/src/info.h index f7e9212d3..07b6650d0 100644 --- a/src/info.h +++ b/src/info.h @@ -89,6 +89,7 @@ struct FState SBYTE Misc1; BYTE Misc2; BYTE Frame; + BYTE DefineFlags; // Unused byte so let's use it during state creation. FState *NextState; actionf_p ActionFunc; int ParameterIndex; @@ -204,8 +205,12 @@ struct FActorInfo void SetDamageFactor(FName type, fixed_t factor); void SetPainChance(FName type, int chance); - FState *FindState (FName name) const; FState *FindState (int numnames, FName *names, bool exact=false) const; + FState *FindStateByString(const char *name, bool exact=false); + FState *FindState (FName name) const + { + return FindState(1, &name); + } FActorInfo *GetReplacement (); FActorInfo *GetReplacee (); @@ -252,6 +257,6 @@ private: extern FDoomEdMap DoomEdMap; int GetSpriteIndex(const char * spritename); -void MakeStateNameList(const char * fname, TArray * out); +TArray &MakeStateNameList(const char * fname); #endif // __INFO_H__ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index b64ce92a6..9949f7a4b 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5230,16 +5230,13 @@ int DLevelScript::RunScript () case PCD_SETACTORSTATE: { const char *statename = FBehavior::StaticLookupString (STACK(2)); - TArray statelist; FState *state; - MakeStateNameList(statename, &statelist); - if (STACK(3) == 0) { if (activator != NULL) { - state = activator->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); + state = activator->GetClass()->ActorInfo->FindStateByString (statename, !!STACK(1)); if (state != NULL) { activator->SetState (state); @@ -5259,7 +5256,7 @@ int DLevelScript::RunScript () while ( (actor = iterator.Next ()) ) { - state = actor->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); + state = actor->GetClass()->ActorInfo->FindStateByString (statename, !!STACK(1)); if (state != NULL) { actor->SetState (state); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 90bdffd11..39ab102fb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4811,10 +4811,6 @@ int AActor::DoSpecialDamage (AActor *target, int damage) int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) { - // If the actor does not have a corresponding death state, then it does not take damage. - // Note that DeathState matches every kind of damagetype, so if an actor has that, it can - // be hurt with any type of damage. Exception: Massacre damage always succeeds, because - // it needs to work. FState *death; if (flags5 & MF5_NODAMAGE) @@ -4828,7 +4824,7 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN // it needs to work. // Always kill if there is a regular death state or no death states at all. - if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates()) + if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates() || damagetype == NAME_Massacre) { return damage; } diff --git a/src/p_states.cpp b/src/p_states.cpp new file mode 100644 index 000000000..0bda6dca3 --- /dev/null +++ b/src/p_states.cpp @@ -0,0 +1,814 @@ +/* +** p_states.cpp +** state management +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** Copyright 2006-2008 Christoph Oelckers +** 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. +**--------------------------------------------------------------------------- +** +** +*/ +#include "actor.h" +#include "farchive.h" +#include "templates.h" +#include "cmdlib.h" +#include "i_system.h" +#include "thingdef/thingdef.h" + +// Each state is owned by an actor. Actors can own any number of +// states, but a single state cannot be owned by more than one +// actor. States are archived by recording the actor they belong +// to and the index into that actor's list of states. + +// For NULL states, which aren't owned by any actor, the owner +// is recorded as AActor with the following state. AActor should +// never actually have this many states of its own, so this +// is (relatively) safe. + +#define NULL_STATE_INDEX 127 + +TArray JumpParameters; + +//========================================================================== +// +// +//========================================================================== + +FArchive &operator<< (FArchive &arc, FState *&state) +{ + const PClass *info; + + if (arc.IsStoring ()) + { + if (state == NULL) + { + arc.UserWriteClass (RUNTIME_CLASS(AActor)); + arc.WriteCount (NULL_STATE_INDEX); + return arc; + } + + info = FState::StaticFindStateOwner (state); + + if (info != NULL) + { + arc.UserWriteClass (info); + arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates)); + } + else + { + /* this was never working as intended. + I_Error ("Cannot find owner for state %p:\n" + "%s %c%c %3d [%p] -> %p", state, + sprites[state->sprite].name, + state->GetFrame() + 'A', + state->GetFullbright() ? '*' : ' ', + state->GetTics(), + state->GetAction(), + state->GetNextState()); + */ + } + } + else + { + const PClass *info; + DWORD ofs; + + arc.UserReadClass (info); + ofs = arc.ReadCount (); + if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor)) + { + state = NULL; + } + else if (info->ActorInfo != NULL) + { + state = info->ActorInfo->OwnedStates + ofs; + } + else + { + state = NULL; + } + } + return arc; +} + +//========================================================================== +// +// Find the actor that a state belongs to. +// +//========================================================================== + +const PClass *FState::StaticFindStateOwner (const FState *state) +{ + for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i) + { + FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo; + if (state >= info->OwnedStates && + state < info->OwnedStates + info->NumOwnedStates) + { + return info->Class; + } + } + + return NULL; +} + +//========================================================================== +// +// Find the actor that a state belongs to, but restrict the search to +// the specified type and its ancestors. +// +//========================================================================== + +const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info) +{ + while (info != NULL) + { + if (state >= info->OwnedStates && + state < info->OwnedStates + info->NumOwnedStates) + { + return info->Class; + } + info = info->Class->ParentClass->ActorInfo; + } + return NULL; +} + + +//========================================================================== +// +// +//========================================================================== + +FStateLabel *FStateLabels::FindLabel (FName label) +{ + return const_cast(BinarySearch (Labels, NumLabels, &FStateLabel::Label, label)); +} + +void FStateLabels::Destroy () +{ + for(int i=0; iDestroy(); + free (Labels[i].Children); // These are malloc'd, not new'd! + Labels[i].Children=NULL; + } + } +} + + +//=========================================================================== +// +// HasStates +// +// Checks whether the actor has special death states. +// +//=========================================================================== + +bool AActor::HasSpecialDeathStates () const +{ + const FActorInfo *info = GetClass()->ActorInfo; + + if (info->StateList != NULL) + { + FStateLabel *slabel = info->StateList->FindLabel (NAME_Death); + if (slabel != NULL && slabel->Children != NULL) + { + for(int i=0;iChildren->NumLabels;i++) + { + if (slabel->Children->Labels[i].State != NULL) return true; + } + } + } + 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 +// +//========================================================================== + +TArray &MakeStateNameList(const char * fname) +{ + static TArray namelist(3); + FName firstpart, secondpart; + char * c; + + // Handle the old names for the existing death states + char * name = copystring(fname); + firstpart = strtok(name, "."); + switch (firstpart) + { + case NAME_Burn: + firstpart = NAME_Death; + secondpart = NAME_Fire; + break; + case NAME_Ice: + firstpart = NAME_Death; + secondpart = NAME_Ice; + break; + case NAME_Disintegrate: + firstpart = NAME_Death; + secondpart = NAME_Disintegrate; + break; + case NAME_XDeath: + firstpart = NAME_Death; + secondpart = NAME_Extreme; + break; + } + + namelist.Clear(); + namelist.Push(firstpart); + if (secondpart!=NAME_None) namelist.Push(secondpart); + + while ((c = strtok(NULL, "."))!=NULL) + { + FName cc = c; + namelist.Push(cc); + } + delete [] name; + return namelist; +} + +//=========================================================================== +// +// FindState (multiple names version) +// +// Finds a state that matches as many of the supplied names as possible. +// A state with more names than those provided does not match. +// A state with fewer names can match if there are no states with the exact +// same number of names. +// +// The search proceeds like this. For the current class, keeping matching +// names until there are no more. If both the argument list and the state +// are out of names, it's an exact match, so return it. If the state still +// has names, ignore it. If the argument list still has names, remember it. +// +//=========================================================================== +FState *FActorInfo::FindState (int numnames, FName *names, bool exact) const +{ + FStateLabels *labels = StateList; + FState *best = NULL; + + if (labels != NULL) + { + int count = 0; + FStateLabel *slabel = NULL; + FName label; + + // Find the best-matching label for this class. + while (labels != NULL && count < numnames) + { + label = *names++; + slabel = labels->FindLabel (label); + + if (slabel != NULL) + { + count++; + labels = slabel->Children; + best = slabel->State; + } + else + { + break; + } + } + if (count < numnames && exact) return NULL; + } + return best; +} + +//========================================================================== +// +// Finds the state associated with the given string +// +//========================================================================== + +FState *FActorInfo::FindStateByString(const char *name, bool exact) +{ + TArray &namelist = MakeStateNameList(name); + return FindState(namelist.Size(), &namelist[0], exact); +} + + + + +//========================================================================== +// +// Search one list of state definitions for the given name +// +//========================================================================== + +FStateDefine *FStateDefinitions::FindStateLabelInList(TArray & list, FName name, bool create) +{ + for(unsigned i = 0; i &namelist = MakeStateNameList(name); + + TArray * statelist = &StateLabels; + for(unsigned i=0;iChildren; + } + return statedef; +} + +//========================================================================== +// +// Adds a new state tp the curremt list +// +//========================================================================== + +void FStateDefinitions::AddState (const char *statename, FState *state, BYTE defflags) +{ + FStateDefine *std = FindStateAddress(statename); + std->State = state; + std->DefineFlags = defflags; +} + +//========================================================================== +// +// Finds the state associated with the given name +// returns NULL if none found +// +//========================================================================== + +FState * FStateDefinitions::FindState(const char * name) +{ + FStateDefine * statedef=NULL; + + TArray &namelist = MakeStateNameList(name); + + TArray * statelist = &StateLabels; + for(unsigned i=0;iChildren; + } + return statedef? statedef->State : NULL; +} + +//========================================================================== +// +// Creates the final list of states from the state definitions +// +//========================================================================== + +static int STACK_ARGS labelcmp(const void * a, const void * b) +{ + FStateLabel * A = (FStateLabel *)a; + FStateLabel * B = (FStateLabel *)b; + return ((int)A->Label - (int)B->Label); +} + +FStateLabels * FStateDefinitions::CreateStateLabelList(TArray & statelist) +{ + // First delete all empty labels from the list + for (int i=statelist.Size()-1;i>=0;i--) + { + if (statelist[i].Label == NAME_None || (statelist[i].State == NULL && statelist[i].Children.Size() == 0)) + { + statelist.Delete(i); + } + } + + int count=statelist.Size(); + + if (count == 0) return NULL; + + FStateLabels * list = (FStateLabels*)M_Malloc(sizeof(FStateLabels)+(count-1)*sizeof(FStateLabel)); + list->NumLabels = count; + + for (int i=0;iLabels[i].Label = statelist[i].Label; + list->Labels[i].State = statelist[i].State; + list->Labels[i].Children = CreateStateLabelList(statelist[i].Children); + } + qsort(list->Labels, count, sizeof(FStateLabel), labelcmp); + return list; +} + +//=========================================================================== +// +// InstallStates +// +// Creates the actor's state list from the current definition +// +//=========================================================================== + +void FStateDefinitions::InstallStates(FActorInfo *info, AActor *defaults) +{ + // First ensure we have a valid spawn state. + FState *state = FindState("Spawn"); + + if (state == NULL) + { + // A NULL spawn state will crash the engine so set it to something valid. + AddState("Spawn", GetDefault()->SpawnState); + } + + if (info->StateList != NULL) + { + info->StateList->Destroy(); + M_Free(info->StateList); + } + info->StateList = CreateStateLabelList(StateLabels); + + // Cache these states as member veriables. + defaults->SpawnState = info->FindState(NAME_Spawn); + defaults->SeeState = info->FindState(NAME_See); + // Melee and Missile states are manipulated by the scripted marines so they + // have to be stored locally + defaults->MeleeState = info->FindState(NAME_Melee); + defaults->MissileState = info->FindState(NAME_Missile); +} + +//=========================================================================== +// +// MakeStateDefines +// +// Creates a list of state definitions from an existing actor +// Used by Dehacked to modify an actor's state list +// +//=========================================================================== + +void FStateDefinitions::MakeStateList(const FStateLabels *list, TArray &dest) +{ + dest.Clear(); + if (list != NULL) for(int i=0;iNumLabels;i++) + { + FStateDefine def; + + def.Label = list->Labels[i].Label; + def.State = list->Labels[i].State; + dest.Push(def); + if (list->Labels[i].Children != NULL) + { + MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children); + } + } +} + +void FStateDefinitions::MakeStateDefines(const PClass *cls) +{ + if (cls->ActorInfo && cls->ActorInfo->StateList) + { + MakeStateList(cls->ActorInfo->StateList, StateLabels); + } + else + { + ClearStateLabels(); + } +} + +//=========================================================================== +// +// AddStateDefines +// +// Adds a list of states to the current definitions +// +//=========================================================================== + +void FStateDefinitions::AddStateDefines(const FStateLabels *list) +{ + if (list != NULL) for(int i=0;iNumLabels;i++) + { + if (list->Labels[i].Children == NULL) + { + if (!FindStateLabelInList(StateLabels, list->Labels[i].Label, false)) + { + FStateDefine def; + + def.Label = list->Labels[i].Label; + def.State = list->Labels[i].State; + StateLabels.Push(def); + } + } + } +} + +//========================================================================== +// +// RetargetState(Pointer)s +// +// These functions are used when a goto follows one or more labels. +// Because multiple labels are permitted to occur consecutively with no +// intervening states, it is not enough to remember the last label defined +// and adjust it. So these functions search for all labels that point to +// the current position in the state array and give them a copy of the +// target string instead. +// +//========================================================================== + +void FStateDefinitions::RetargetStatePointers (intptr_t count, const char *target, TArray & statelist) +{ + for(unsigned i = 0;i 0) + { + RetargetStatePointers(count, target, statelist[i].Children); + } + } +} + +void FStateDefinitions::RetargetStates (intptr_t count, const char *target) +{ + RetargetStatePointers(count, target, StateLabels); +} + + +//========================================================================== +// +// ResolveGotoLabel +// +// Resolves any strings being stored in a state's NextState field +// +//========================================================================== + +FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name) +{ + const PClass *type=mytype; + FState *state; + char *namestart = name; + char *label, *offset, *pt; + int v; + + // Check for classname + if ((pt = strstr (name, "::")) != NULL) + { + const char *classname = name; + *pt = '\0'; + name = pt + 2; + + // The classname may either be "Super" to identify this class's immediate + // superclass, or it may be the name of any class that this one derives from. + if (stricmp (classname, "Super") == 0) + { + type = type->ParentClass; + actor = GetDefaultByType (type); + } + else + { + // first check whether a state of the desired name exists + const PClass *stype = PClass::FindClass (classname); + if (stype == NULL) + { + I_Error ("%s is an unknown class.", classname); + } + if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor))) + { + I_Error ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars()); + } + if (!stype->IsAncestorOf (type)) + { + I_Error ("%s is not derived from %s so cannot access its states.", + type->TypeName.GetChars(), stype->TypeName.GetChars()); + } + if (type != stype) + { + type = stype; + actor = GetDefaultByType (type); + } + } + } + label = name; + // Check for offset + offset = NULL; + if ((pt = strchr (name, '+')) != NULL) + { + *pt = '\0'; + offset = pt + 1; + } + v = offset ? strtol (offset, NULL, 0) : 0; + + // Get the state's address. + if (type==mytype) state = FindState (label); + else state = type->ActorInfo->FindStateByString(label, true); + + if (state != NULL) + { + state += v; + } + else if (v != 0) + { + I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars()); + } + delete[] namestart; // free the allocated string buffer + return state; +} + +//========================================================================== +// +// FixStatePointers +// +// Fixes an actor's default state pointers. +// +//========================================================================== + +void FStateDefinitions::FixStatePointers (FActorInfo *actor, TArray & list) +{ + for(unsigned i=0;i= 1 && v < 0x10000) + { + list[i].State = actor->OwnedStates + v - 1; + } + if (list[i].Children.Size() > 0) FixStatePointers(actor, list[i].Children); + } +} + +//========================================================================== +// +// ResolveGotoLabels +// +// Resolves an actor's state pointers that were specified as jumps. +// +//========================================================================== + +void FStateDefinitions::ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray & list) +{ + for(unsigned i=0;iClass, (char *)list[i].State); + list[i].DefineFlags = SDF_STATE; + } + if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, defaults, list[i].Children); + } +} + + +//========================================================================== +// +// FinishStates +// copies a state block and fixes all state links using the current list of labels +// +//========================================================================== + +int FStateDefinitions::FinishStates (FActorInfo *actor, AActor *defaults, TArray &StateArray) +{ + static int c=0; + int count = StateArray.Size(); + + if (count > 0) + { + FState *realstates = new FState[count]; + int i; + int currange; + + memcpy(realstates, &StateArray[0], count*sizeof(FState)); + actor->OwnedStates = realstates; + actor->NumOwnedStates = count; + + // adjust the state pointers + // In the case new states are added these must be adjusted, too! + FixStatePointers (actor, StateLabels); + + for(i = currange = 0; i < count; i++) + { + // resolve labels and jumps + switch(realstates[i].DefineFlags) + { + case SDF_STOP: // stop + realstates[i].NextState = NULL; + break; + + case SDF_WAIT: // wait + realstates[i].NextState = &realstates[i]; + break; + + case SDF_NEXT: // next + realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]); + break; + + case SDF_INDEX: // loop + realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1]; + break; + + case SDF_LABEL: + realstates[i].NextState = ResolveGotoLabel (defaults, actor->Class, (char *)realstates[i].NextState); + break; + } + } + } + + // Fix state pointers that are gotos + ResolveGotoLabels (actor, defaults, StateLabels); + + return count; +} + diff --git a/src/thingdef/olddecorations.cpp b/src/thingdef/olddecorations.cpp index fab128550..d4c36b9f4 100644 --- a/src/thingdef/olddecorations.cpp +++ b/src/thingdef/olddecorations.cpp @@ -103,8 +103,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- -static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, - TArray &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc); +static void ParseInsideDecoration (Baggage &bag, AActor *defaults, + FExtraInfo &extra, EDefinitionType def, FScanner &sc); static void ParseSpriteFrames (FActorInfo *info, TArray &states, FScanner &sc); // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -135,12 +135,13 @@ static const char *RenderStyles[] = void ParseOldDecoration(FScanner &sc, EDefinitionType def) { - TArray states; + Baggage bag; FExtraInfo extra; + FActorInfo *info; PClass *type; PClass *parent; - FActorInfo *info; FName typeName; + FStateDefinitions statedef; if (def == DEF_Pickup) parent = RUNTIME_CLASS(AFakeInventory); else parent = RUNTIME_CLASS(AActor); @@ -148,19 +149,17 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) sc.MustGetString(); typeName = FName(sc.String); type = parent->CreateDerivedClass (typeName, parent->Size); - info = type->ActorInfo; - info->GameFilter = 0x80; - MakeStateDefines(parent->ActorInfo->StateList); + ResetBaggage(&bag, parent); + info = bag.Info = type->ActorInfo; info->GameFilter = GAME_Any; sc.MustGetStringName("{"); - states.Clear (); memset (&extra, 0, sizeof(extra)); - ParseInsideDecoration (info, (AActor *)(type->Defaults), states, extra, def, sc); + ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc); - info->NumOwnedStates = states.Size(); - if (info->NumOwnedStates == 0) + bag.Info->NumOwnedStates = bag.StateArray.Size(); + if (bag.Info->NumOwnedStates == 0) { sc.ScriptError ("%s does not define any animation frames", typeName.GetChars() ); } @@ -180,13 +179,13 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) if (extra.IceDeathEnd != 0) { // Make a copy of the final frozen frame for A_FreezeDeathChunks - FState icecopy = states[extra.IceDeathEnd-1]; - states.Push (icecopy); + FState icecopy = bag.StateArray[extra.IceDeathEnd-1]; + bag.StateArray.Push (icecopy); info->NumOwnedStates += 1; } info->OwnedStates = new FState[info->NumOwnedStates]; - memcpy (info->OwnedStates, &states[0], info->NumOwnedStates * sizeof(info->OwnedStates[0])); + memcpy (info->OwnedStates, &bag.StateArray[0], info->NumOwnedStates * sizeof(info->OwnedStates[0])); if (info->NumOwnedStates == 1) { info->OwnedStates->Tics = -1; @@ -247,7 +246,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) if (extra.DeathHeight == 0) extra.DeathHeight = ((AActor*)(type->Defaults))->height; info->Class->Meta.SetMetaFixed (AMETA_DeathHeight, extra.DeathHeight); } - AddState("Death", &info->OwnedStates[extra.DeathStart]); + statedef.AddState("Death", &info->OwnedStates[extra.DeathStart]); } // Burn states are the same as death states, except they can optionally terminate @@ -285,7 +284,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->height; type->Meta.SetMetaFixed (AMETA_BurnHeight, extra.BurnHeight); - AddState("Burn", &info->OwnedStates[extra.FireDeathStart]); + statedef.AddState("Burn", &info->OwnedStates[extra.FireDeathStart]); } // Ice states are similar to burn and death, except their final frame enters @@ -306,11 +305,11 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) info->OwnedStates[i].Tics = 1; info->OwnedStates[i].Misc1 = 0; info->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks")); - AddState("Ice", &info->OwnedStates[extra.IceDeathStart]); + statedef.AddState("Ice", &info->OwnedStates[extra.IceDeathStart]); } else if (extra.bGenericIceDeath) { - AddState("Ice", RUNTIME_CLASS(AActor)->ActorInfo->FindState(NAME_GenericFreezeDeath)); + statedef.AddState("Ice", RUNTIME_CLASS(AActor)->ActorInfo->FindState(NAME_GenericFreezeDeath)); } } if (def == DEF_BreakableDecoration) @@ -321,8 +320,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) { ((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE; } - AddState("Spawn", &info->OwnedStates[extra.SpawnStart]); - InstallStates (info, ((AActor *)(type->Defaults))); + statedef.AddState("Spawn", &info->OwnedStates[extra.SpawnStart]); + statedef.InstallStates (info, ((AActor *)(type->Defaults))); } //========================================================================== @@ -333,8 +332,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) // //========================================================================== -static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, - TArray &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc) +static void ParseInsideDecoration (Baggage &bag, AActor *defaults, + FExtraInfo &extra, EDefinitionType def, FScanner &sc) { AFakeInventory *const inv = static_cast(defaults); char sprite[5] = "TNT1"; @@ -349,7 +348,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, { sc.ScriptError ("DoomEdNum must be in the range [-1,32767]"); } - info->DoomEdNum = (SWORD)sc.Number; + bag.Info->DoomEdNum = (SWORD)sc.Number; } else if (sc.Compare ("SpawnNum")) { @@ -358,7 +357,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, { sc.ScriptError ("SpawnNum must be in the range [0,255]"); } - info->SpawnID = (BYTE)sc.Number; + bag.Info->SpawnID = (BYTE)sc.Number; } else if (sc.Compare ("Sprite") || ( (def == DEF_BreakableDecoration || def == DEF_Projectile) && @@ -383,31 +382,31 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, else if (sc.Compare ("Frames")) { sc.MustGetString (); - extra.SpawnStart = states.Size(); - ParseSpriteFrames (info, states, sc); - extra.SpawnEnd = states.Size(); + extra.SpawnStart = bag.StateArray.Size(); + ParseSpriteFrames (bag.Info, bag.StateArray, sc); + extra.SpawnEnd = bag.StateArray.Size(); } else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) && sc.Compare ("DeathFrames")) { sc.MustGetString (); - extra.DeathStart = states.Size(); - ParseSpriteFrames (info, states, sc); - extra.DeathEnd = states.Size(); + extra.DeathStart = bag.StateArray.Size(); + ParseSpriteFrames (bag.Info, bag.StateArray, sc); + extra.DeathEnd = bag.StateArray.Size(); } else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames")) { sc.MustGetString (); - extra.IceDeathStart = states.Size(); - ParseSpriteFrames (info, states, sc); - extra.IceDeathEnd = states.Size(); + extra.IceDeathStart = bag.StateArray.Size(); + ParseSpriteFrames (bag.Info, bag.StateArray, sc); + extra.IceDeathEnd = bag.StateArray.Size(); } else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames")) { sc.MustGetString (); - extra.FireDeathStart = states.Size(); - ParseSpriteFrames (info, states, sc); - extra.FireDeathEnd = states.Size(); + extra.FireDeathStart = bag.StateArray.Size(); + ParseSpriteFrames (bag.Info, bag.StateArray, sc); + extra.FireDeathEnd = bag.StateArray.Size(); } else if (def == DEF_BreakableDecoration && sc.Compare ("GenericIceDeath")) { @@ -464,18 +463,18 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius")) { sc.MustGetNumber (); - info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number); + bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number); extra.bExplosive = true; } else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage")) { sc.MustGetNumber (); - info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number); + bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number); extra.bExplosive = true; } else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter")) { - info->Class->Meta.SetMetaInt(ACMETA_DontHurtShooter, true); + bag.Info->Class->Meta.SetMetaInt(ACMETA_DontHurtShooter, true); } else if (def == DEF_Projectile && sc.Compare ("Damage")) { @@ -560,7 +559,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, else if (def == DEF_Pickup && sc.Compare ("PickupMessage")) { sc.MustGetString (); - info->Class->Meta.SetMetaString(AIMETA_PickupMessage, sc.String); + bag.Info->Class->Meta.SetMetaString(AIMETA_PickupMessage, sc.String); } else if (def == DEF_Pickup && sc.Compare ("Respawns")) { @@ -576,8 +575,6 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, } else if (sc.String[0] != '*') { - Baggage bag; - bag.Info = info; HandleActorFlag(sc, bag, sc.String, NULL, '+'); } else @@ -590,16 +587,16 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, unsigned int i; int spr = GetSpriteIndex(sprite); - for (i = 0; i < states.Size(); ++i) + for (i = 0; i < bag.StateArray.Size(); ++i) { - states[i].sprite = spr; + bag.StateArray[i].sprite = spr; } if (extra.DeathSprite[0] && extra.DeathEnd != 0) { int spr = GetSpriteIndex(extra.DeathSprite); for (i = extra.DeathStart; i < extra.DeathEnd; ++i) { - states[i].sprite = spr; + bag.StateArray[i].sprite = spr; } } } diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 71fd99db2..3a634b9b2 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -169,8 +169,8 @@ int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant) // be checked here. JumpParameters.Push(NAME_None); } - TArray names; - MakeStateNameList(statestring, &names); + + TArray &names = MakeStateNameList(statestring); if (stype != NULL) { @@ -420,8 +420,6 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac info = ti->ActorInfo; } - MakeStateDefines(parent->ActorInfo->StateList); - info->DoomEdNum = -1; if (parent->ActorInfo->DamageFactors != NULL) { @@ -522,7 +520,7 @@ static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag) try { FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native); - ResetBaggage (bag); + ResetBaggage (bag, info->Class->ParentClass); bag->Info = info; bag->Lumpnum = sc.LumpNum; return info; @@ -577,8 +575,8 @@ void ParseActor(FScanner &sc) sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars()); break; } - FinishActor(sc, info, bag); } + FinishActor(sc, info, bag); sc.SetCMode (false); } diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 28a439a3d..87a49f292 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -48,6 +48,68 @@ public: }; +//========================================================================== +// +// State parser +// +//========================================================================== + +extern TArray StateParameters; +extern TArray JumpParameters; + +struct FStateLabels; + +enum EStateDefineFlags +{ + SDF_NEXT = 0, + SDF_STATE = 1, + SDF_STOP = 2, + SDF_WAIT = 3, + SDF_LABEL = 4, + SDF_INDEX = 5, +}; + +struct FStateDefine +{ + FName Label; + TArray Children; + FState *State; + BYTE DefineFlags; +}; + +class FStateDefinitions +{ + TArray StateLabels; + + static FStateDefine *FindStateLabelInList(TArray &list, FName name, bool create); + static FStateLabels *CreateStateLabelList(TArray &statelist); + static void MakeStateList(const FStateLabels *list, TArray &dest); + static void RetargetStatePointers (intptr_t count, const char *target, TArray & statelist); + FStateDefine *FindStateAddress(const char *name); + FState *FindState(const char *name); + + FState *ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name); + static void FixStatePointers (FActorInfo *actor, TArray & list); + void ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray & list); + +public: + + + void ClearStateLabels() + { + StateLabels.Clear(); + } + + void AddState (const char * statename, FState * state, BYTE defflags = SDF_STATE); + void InstallStates(FActorInfo *info, AActor *defaults); + int FinishStates (FActorInfo *actor, AActor *defaults, TArray &StateArray); + + void MakeStateDefines(const PClass *cls); + void AddStateDefines(const FStateLabels *list); + void RetargetStates (intptr_t count, const char *target); + +}; + //========================================================================== // // Extra info maintained while defining an actor. @@ -62,16 +124,20 @@ struct Baggage bool StateSet; int CurrentState; int Lumpnum; + FStateDefinitions statedef; + TArray StateArray; FDropItem *DropItemList; }; -inline void ResetBaggage (Baggage *bag) +inline void ResetBaggage (Baggage *bag, const PClass *stateclass) { bag->DropItemList = NULL; bag->DropItemSet = false; bag->CurrentState = 0; bag->StateSet = false; + bag->StateArray.Clear(); + bag->statedef.MakeStateDefines(stateclass); } //========================================================================== @@ -90,25 +156,8 @@ AFuncDesc * FindFunction(const char * string); -//========================================================================== -// -// State parser -// -//========================================================================== - -extern TArray StateParameters; -extern TArray JumpParameters; - -void ClearStateLabels(); -void AddState (const char * statename, FState * state); -FState * FindState(AActor * actor, const PClass * type, const char * name); -void InstallStates(FActorInfo *info, AActor *defaults); -void MakeStateDefines(const FStateLabels *list); -void AddStateDefines(const FStateLabels *list); FState *P_GetState(AActor *self, FState *CallingState, int offset); -int FinishStates (FActorInfo *actor, AActor *defaults); int ParseStates(FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag); -FState *CheckState(FScanner &sc, PClass *type); //========================================================================== diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index a1922d9d4..cd6cada33 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -507,7 +507,7 @@ void ParseActorProperty(FScanner &sc, Baggage &bag) } else if (MatchString(propname, statenames) != -1) { - AddState(propname, CheckState (sc, bag.Info->Class)); + bag.statedef.AddState(propname, CheckState (sc, bag.Info->Class)); } else { @@ -528,13 +528,14 @@ void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag) try { - FinishStates (info, defaults); + bag.statedef.FinishStates (info, defaults, bag.StateArray); } catch (CRecoverableError &err) { sc.ScriptError(err.GetMessage()); } - InstallStates (info, defaults); + bag.statedef.InstallStates (info, defaults); + bag.StateArray.Clear (); if (bag.DropItemSet) { if (bag.DropItemList == NULL) diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 9143eb0a0..ea12c5530 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -573,8 +573,7 @@ DEFINE_PROPERTY(skip_super, 0, Actor) { FreeDropItemChain (bag.DropItemList); } - ResetBaggage (&bag); - MakeStateDefines(NULL); + ResetBaggage (&bag, RUNTIME_CLASS(AActor)); } //========================================================================== diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 15dbb66eb..f315b3829 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -57,9 +57,7 @@ #include "colormatcher.h" TArray StateParameters; -TArray JumpParameters; static TArray AFTable; -static TArray StateArray; //========================================================================== // @@ -132,291 +130,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name) return NULL; } -//========================================================================== -// -// Find a state address -// -//========================================================================== - -struct FStateDefine -{ - FName Label; - TArray Children; - FState *State; -}; - -static TArray StateLabels; - -void ClearStateLabels() -{ - StateLabels.Clear(); -} - -//========================================================================== -// -// Search one list of state definitions for the given name -// -//========================================================================== - -static FStateDefine * FindStateLabelInList(TArray & list, FName name, bool create) -{ - for(unsigned i = 0; i namelist(3); - FStateDefine * statedef=NULL; - - MakeStateNameList(name, &namelist); - - TArray * statelist = &StateLabels; - for(unsigned i=0;iChildren; - } - return statedef; -} - -void AddState (const char * statename, FState * state) -{ - FStateDefine * std = FindStateAddress(statename); - std->State = state; -} - -//========================================================================== -// -// Finds the state associated with the given name -// -//========================================================================== - -FState * FindState(AActor * actor, const PClass * type, const char * name) -{ - static TArray namelist(3); - FStateDefine * statedef=NULL; - - MakeStateNameList(name, &namelist); - - TArray * statelist = &StateLabels; - for(unsigned i=0;iChildren; - } - return statedef? statedef->State : NULL; -} - -//========================================================================== -// -// Finds the state associated with the given name -// -//========================================================================== - -FState * FindStateInClass(AActor * actor, const PClass * type, const char * name) -{ - static TArray namelist(3); - - MakeStateNameList(name, &namelist); - FActorInfo * info = type->ActorInfo; - if (info) return info->FindState(namelist.Size(), &namelist[0], true); - return NULL; -} - -//========================================================================== -// -// Checks if a state list is empty -// A list is empty if it doesn't contain any states and no children -// that contain any states -// -//========================================================================== - -static bool IsStateListEmpty(TArray & statelist) -{ - for(unsigned i=0;iLabel - (int)B->Label); -} - -static FStateLabels * CreateStateLabelList(TArray & statelist) -{ - // First delete all empty labels from the list - for (int i=statelist.Size()-1;i>=0;i--) - { - if (statelist[i].Label == NAME_None || (statelist[i].State == NULL && statelist[i].Children.Size() == 0)) - { - statelist.Delete(i); - } - } - - int count=statelist.Size(); - - if (count == 0) return NULL; - - FStateLabels * list = (FStateLabels*)M_Malloc(sizeof(FStateLabels)+(count-1)*sizeof(FStateLabel)); - list->NumLabels = count; - - for (int i=0;iLabels[i].Label = statelist[i].Label; - list->Labels[i].State = statelist[i].State; - list->Labels[i].Children = CreateStateLabelList(statelist[i].Children); - } - qsort(list->Labels, count, sizeof(FStateLabel), labelcmp); - return list; -} - -//=========================================================================== -// -// InstallStates -// -// Creates the actor's state list from the current definition -// -//=========================================================================== - -void InstallStates(FActorInfo *info, AActor *defaults) -{ - // First ensure we have a valid spawn state. - FState * state = FindState(defaults, info->Class, "Spawn"); - - if (state == NULL) - { - // A NULL spawn state will crash the engine so set it to something valid. - AddState("Spawn", GetDefault()->SpawnState); - } - - if (info->StateList != NULL) - { - info->StateList->Destroy(); - M_Free(info->StateList); - } - info->StateList = CreateStateLabelList(StateLabels); - - // Cache these states as member veriables. - defaults->SpawnState = info->FindState(NAME_Spawn); - defaults->SeeState = info->FindState(NAME_See); - // Melee and Missile states are manipulated by the scripted marines so they - // have to be stored locally - defaults->MeleeState = info->FindState(NAME_Melee); - defaults->MissileState = info->FindState(NAME_Missile); -} -//=========================================================================== -// -// MakeStateDefines -// -// Creates a list of state definitions from an existing actor -// Used by Dehacked to modify an actor's state list -// -//=========================================================================== - -static void MakeStateList(const FStateLabels *list, TArray &dest) -{ - dest.Clear(); - if (list != NULL) for(int i=0;iNumLabels;i++) - { - FStateDefine def; - - def.Label = list->Labels[i].Label; - def.State = list->Labels[i].State; - dest.Push(def); - if (list->Labels[i].Children != NULL) - { - MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children); - } - } -} - -void MakeStateDefines(const FStateLabels *list) -{ - MakeStateList(list, StateLabels); -} - -void AddStateDefines(const FStateLabels *list) -{ - if (list != NULL) for(int i=0;iNumLabels;i++) - { - if (list->Labels[i].Children == NULL) - { - if (!FindStateLabelInList(StateLabels, list->Labels[i].Label, false)) - { - FStateDefine def; - - def.Label = list->Labels[i].Label; - def.State = list->Labels[i].State; - StateLabels.Push(def); - } - } - } -} - -//========================================================================== -// -// RetargetState(Pointer)s -// -// These functions are used when a goto follows one or more labels. -// Because multiple labels are permitted to occur consecutively with no -// intervening states, it is not enough to remember the last label defined -// and adjust it. So these functions search for all labels that point to -// the current position in the state array and give them a copy of the -// target string instead. -// -//========================================================================== - -static void RetargetStatePointers (intptr_t count, const char *target, TArray & statelist) -{ - for(unsigned i = 0;i 0) - { - RetargetStatePointers(count, target, statelist[i].Children); - } - } -} - -static void RetargetStates (intptr_t count, const char *target) -{ - RetargetStatePointers(count, target, StateLabels); -} //========================================================================== @@ -552,10 +267,11 @@ do_goto: if (laststate != NULL) { // Following a state definition: Modify it. laststate->NextState = (FState*)copystring(statestring); + laststate->DefineFlags = SDF_LABEL; } else if (lastlabel >= 0) { // Following a label: Retarget it. - RetargetStates (count+1, statestring); + bag.statedef.RetargetStates (count+1, statestring); } else { @@ -567,11 +283,11 @@ do_goto: do_stop: if (laststate!=NULL) { - laststate->NextState=(FState*)-1; + laststate->DefineFlags = SDF_STOP; } else if (lastlabel >=0) { - RetargetStates (count+1, NULL); + bag.statedef.RetargetStates (count+1, NULL); } else { @@ -586,7 +302,7 @@ do_stop: sc.ScriptError("%s before first state", sc.String); continue; } - laststate->NextState=(FState*)-2; + laststate->DefineFlags = SDF_WAIT; } else if (!statestring.CompareNoCase("LOOP")) { @@ -596,6 +312,7 @@ do_stop: continue; } laststate->NextState=(FState*)(lastlabel+1); + laststate->DefineFlags = SDF_INDEX; } else { @@ -608,7 +325,7 @@ do_stop: do { lastlabel = count; - AddState(statestring, (FState *) (count+1)); + bag.statedef.AddState(statestring, (FState *) (count+1), SDF_INDEX); statestring = ParseStateString(sc); if (!statestring.CompareNoCase("GOTO")) { @@ -789,7 +506,7 @@ do_stop: } sc.UnGet(); endofstate: - StateArray.Push(state); + bag.StateArray.Push(state); while (*statestrp) { int frame=((*statestrp++)&223)-'A'; @@ -801,10 +518,10 @@ endofstate: } state.Frame=(state.Frame&(SF_FULLBRIGHT))|frame; - StateArray.Push(state); + bag.StateArray.Push(state); count++; } - laststate=&StateArray[count]; + laststate=&bag.StateArray[count]; count++; } } @@ -816,186 +533,3 @@ endofstate: return count; } -//========================================================================== -// -// ResolveGotoLabel -// -//========================================================================== - -static FState *ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name) -{ - const PClass *type=mytype; - FState *state; - char *namestart = name; - char *label, *offset, *pt; - int v; - - // Check for classname - if ((pt = strstr (name, "::")) != NULL) - { - const char *classname = name; - *pt = '\0'; - name = pt + 2; - - // The classname may either be "Super" to identify this class's immediate - // superclass, or it may be the name of any class that this one derives from. - if (stricmp (classname, "Super") == 0) - { - type = type->ParentClass; - actor = GetDefaultByType (type); - } - else - { - // first check whether a state of the desired name exists - const PClass *stype = PClass::FindClass (classname); - if (stype == NULL) - { - I_Error ("%s is an unknown class.", classname); - } - if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor))) - { - I_Error ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars()); - } - if (!stype->IsAncestorOf (type)) - { - I_Error ("%s is not derived from %s so cannot access its states.", - type->TypeName.GetChars(), stype->TypeName.GetChars()); - } - if (type != stype) - { - type = stype; - actor = GetDefaultByType (type); - } - } - } - label = name; - // Check for offset - offset = NULL; - if ((pt = strchr (name, '+')) != NULL) - { - *pt = '\0'; - offset = pt + 1; - } - v = offset ? strtol (offset, NULL, 0) : 0; - - // Get the state's address. - if (type==mytype) state = FindState (actor, type, label); - else state = FindStateInClass (actor, type, label); - - if (state != NULL) - { - state += v; - } - else if (v != 0) - { - I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars()); - } - delete[] namestart; // free the allocated string buffer - return state; -} - -//========================================================================== -// -// FixStatePointers -// -// Fixes an actor's default state pointers. -// -//========================================================================== - -static void FixStatePointers (FActorInfo *actor, TArray & list) -{ - for(unsigned i=0;i= 1 && v < 0x10000) - { - list[i].State = actor->OwnedStates + v - 1; - } - if (list[i].Children.Size() > 0) FixStatePointers(actor, list[i].Children); - } -} - -//========================================================================== -// -// FixStatePointersAgain -// -// Resolves an actor's state pointers that were specified as jumps. -// -//========================================================================== - -static void FixStatePointersAgain (FActorInfo *actor, AActor *defaults, TArray & list) -{ - for(unsigned i=0;iClass, (char *)list[i].State); - } - if (list[i].Children.Size() > 0) FixStatePointersAgain(actor, defaults, list[i].Children); - } -} - - -//========================================================================== -// -// FinishStates -// copies a state block and fixes all state links -// -//========================================================================== - -int FinishStates (FActorInfo *actor, AActor *defaults) -{ - static int c=0; - int count = StateArray.Size(); - - if (count > 0) - { - FState *realstates = new FState[count]; - int i; - int currange; - - memcpy(realstates, &StateArray[0], count*sizeof(FState)); - actor->OwnedStates = realstates; - actor->NumOwnedStates = count; - - // adjust the state pointers - // In the case new states are added these must be adjusted, too! - FixStatePointers (actor, StateLabels); - - for(i = currange = 0; i < count; i++) - { - // resolve labels and jumps - switch((ptrdiff_t)realstates[i].NextState) - { - case 0: // next - realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]); - break; - - case -1: // stop - realstates[i].NextState = NULL; - break; - - case -2: // wait - realstates[i].NextState = &realstates[i]; - break; - - default: // loop - if ((size_t)realstates[i].NextState < 0x10000) - { - realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1]; - } - else // goto - { - realstates[i].NextState = ResolveGotoLabel (defaults, actor->Class, (char *)realstates[i].NextState); - } - } - } - } - StateArray.Clear (); - - // Fix state pointers that are gotos - FixStatePointersAgain (actor, defaults, StateLabels); - - return count; -} - diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index bbf0b4448..3e7d28343 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -13,7 +13,8 @@ const int SXF_ABSOLUTEMOMENTUM=8; const int SXF_SETMASTER=16; const int SXF_NOCHECKPOSITION = 32; const int SXF_TELEFRAG=64; -const int SXF_TRANSFERAMBUSHFLAG=128; +// 128 was uses by Skulltag +const int SXF_TRANSFERAMBUSHFLAG=256; // Flags for A_Chase const int CHF_FASTCHASE = 1; diff --git a/zdoom.vcproj b/zdoom.vcproj index c79012dc7..a8d235bc9 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -840,6 +840,10 @@ RelativePath=".\src\p_spec.cpp" > + +