- 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)
This commit is contained in:
Christoph Oelckers 2008-09-22 18:55:29 +00:00
parent b523ebd2a7
commit 954955c5a5
16 changed files with 1001 additions and 904 deletions

View File

@ -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.

View File

@ -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;
};

View File

@ -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<AActor>()->SpawnState);
statedef.AddState("Spawn", state ? state : GetDefault<AActor>()->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.

View File

@ -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<FStateLabel *>(BinarySearch<FStateLabel, FName> (Labels, NumLabels, &FStateLabel::Label, label));
}
void FStateLabels::Destroy ()
{
for(int i=0; i<NumLabels;i++)
{
if (Labels[i].Children != NULL)
{
Labels[i].Children->Destroy();
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;i<slabel->Children->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<FName> * 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;
}
//==========================================================================
//
//

View File

@ -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<FName> * out);
TArray<FName> &MakeStateNameList(const char * fname);
#endif // __INFO_H__

View File

@ -5230,16 +5230,13 @@ int DLevelScript::RunScript ()
case PCD_SETACTORSTATE:
{
const char *statename = FBehavior::StaticLookupString (STACK(2));
TArray<FName> 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);

View File

@ -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;
}

814
src/p_states.cpp Normal file
View File

@ -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<FName> 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<FStateLabel *>(BinarySearch<FStateLabel, FName> (Labels, NumLabels, &FStateLabel::Label, label));
}
void FStateLabels::Destroy ()
{
for(int i=0; i<NumLabels;i++)
{
if (Labels[i].Children != NULL)
{
Labels[i].Children->Destroy();
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;i<slabel->Children->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, &params[2]);
if (jumpto == NULL)
{
const char *dot="";
Printf("Jump target '");
if (classname != NAME_None) Printf("%s::", classname.GetChars());
for (int i=0;i<numnames;i++)
{
Printf("%s%s", dot, params[2+i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
return jumpto;
}
else return NULL;
}
//==========================================================================
//
// Creates a list of names from a string. Dots are used as separator
//
//==========================================================================
TArray<FName> &MakeStateNameList(const char * fname)
{
static TArray<FName> 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<FName> &namelist = MakeStateNameList(name);
return FindState(namelist.Size(), &namelist[0], exact);
}
//==========================================================================
//
// Search one list of state definitions for the given name
//
//==========================================================================
FStateDefine *FStateDefinitions::FindStateLabelInList(TArray<FStateDefine> & list, FName name, bool create)
{
for(unsigned i = 0; i<list.Size(); i++)
{
if (list[i].Label == name) return &list[i];
}
if (create)
{
FStateDefine def;
def.Label=name;
def.State=NULL;
def.DefineFlags = SDF_NEXT;
return &list[list.Push(def)];
}
return NULL;
}
//==========================================================================
//
// Finds the address of a state label given by name.
// Adds the state label if it doesn't exist
//
//==========================================================================
FStateDefine * FStateDefinitions::FindStateAddress(const char *name)
{
FStateDefine * statedef=NULL;
TArray<FName> &namelist = MakeStateNameList(name);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], true);
statelist = &statedef->Children;
}
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<FName> &namelist = MakeStateNameList(name);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], false);
if (statedef == NULL) return NULL;
statelist = &statedef->Children;
}
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<FStateDefine> & 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;i<count;i++)
{
list->Labels[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<AActor>()->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<FStateDefine> &dest)
{
dest.Clear();
if (list != NULL) for(int i=0;i<list->NumLabels;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;i<list->NumLabels;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<FStateDefine> & statelist)
{
for(unsigned i = 0;i<statelist.Size(); i++)
{
if (statelist[i].State == (FState*)count)
{
if (target == NULL)
{
statelist[i].State = NULL;
statelist[i].DefineFlags = SDF_STOP;
}
else
{
statelist[i].State = (FState *)copystring (target);
statelist[i].DefineFlags = SDF_LABEL;
}
}
if (statelist[i].Children.Size() > 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<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
size_t v=(size_t)list[i].State;
if (v >= 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<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
if (list[i].State != NULL && list[i].DefineFlags == SDF_LABEL)
{ // It's not a valid state, so it must be a label string. Resolve it.
list[i].State = ResolveGotoLabel (defaults, actor->Class, (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<FState> &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;
}

View File

@ -103,8 +103,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name);
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
TArray<FState> &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<FState> &states, FScanner &sc);
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -135,12 +135,13 @@ static const char *RenderStyles[] =
void ParseOldDecoration(FScanner &sc, EDefinitionType def)
{
TArray<FState> 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<FState> &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<AFakeInventory *>(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;
}
}
}

View File

@ -169,8 +169,8 @@ int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant)
// be checked here.
JumpParameters.Push(NAME_None);
}
TArray<FName> names;
MakeStateNameList(statestring, &names);
TArray<FName> &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);
}

View File

@ -48,6 +48,68 @@ public:
};
//==========================================================================
//
// State parser
//
//==========================================================================
extern TArray<int> StateParameters;
extern TArray<FName> 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<FStateDefine> Children;
FState *State;
BYTE DefineFlags;
};
class FStateDefinitions
{
TArray<FStateDefine> StateLabels;
static FStateDefine *FindStateLabelInList(TArray<FStateDefine> &list, FName name, bool create);
static FStateLabels *CreateStateLabelList(TArray<FStateDefine> &statelist);
static void MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest);
static void RetargetStatePointers (intptr_t count, const char *target, TArray<FStateDefine> & 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<FStateDefine> & list);
void ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray<FStateDefine> & 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<FState> &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<FState> 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<int> StateParameters;
extern TArray<FName> 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);
//==========================================================================

View File

@ -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)

View File

@ -573,8 +573,7 @@ DEFINE_PROPERTY(skip_super, 0, Actor)
{
FreeDropItemChain (bag.DropItemList);
}
ResetBaggage (&bag);
MakeStateDefines(NULL);
ResetBaggage (&bag, RUNTIME_CLASS(AActor));
}
//==========================================================================

View File

@ -57,9 +57,7 @@
#include "colormatcher.h"
TArray<int> StateParameters;
TArray<FName> JumpParameters;
static TArray<AFuncDesc> AFTable;
static TArray<FState> StateArray;
//==========================================================================
//
@ -132,291 +130,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name)
return NULL;
}
//==========================================================================
//
// Find a state address
//
//==========================================================================
struct FStateDefine
{
FName Label;
TArray<FStateDefine> Children;
FState *State;
};
static TArray<FStateDefine> StateLabels;
void ClearStateLabels()
{
StateLabels.Clear();
}
//==========================================================================
//
// Search one list of state definitions for the given name
//
//==========================================================================
static FStateDefine * FindStateLabelInList(TArray<FStateDefine> & list, FName name, bool create)
{
for(unsigned i = 0; i<list.Size(); i++)
{
if (list[i].Label == name) return &list[i];
}
if (create)
{
FStateDefine def;
def.Label=name;
def.State=NULL;
return &list[list.Push(def)];
}
return NULL;
}
//==========================================================================
//
// Finds the address of a state given by name.
// Adds the state if it doesn't exist
//
//==========================================================================
static FStateDefine * FindStateAddress(const char * name)
{
static TArray<FName> namelist(3);
FStateDefine * statedef=NULL;
MakeStateNameList(name, &namelist);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], true);
statelist = &statedef->Children;
}
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<FName> namelist(3);
FStateDefine * statedef=NULL;
MakeStateNameList(name, &namelist);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], false);
if (statedef == NULL) return NULL;
statelist = &statedef->Children;
}
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<FName> 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<FStateDefine> & statelist)
{
for(unsigned i=0;i<statelist.Size();i++)
{
if (statelist[i].State!=NULL || !IsStateListEmpty(statelist[i].Children)) return false;
}
return true;
}
//==========================================================================
//
// 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);
}
static FStateLabels * CreateStateLabelList(TArray<FStateDefine> & 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;i<count;i++)
{
list->Labels[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<AActor>()->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<FStateDefine> &dest)
{
dest.Clear();
if (list != NULL) for(int i=0;i<list->NumLabels;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;i<list->NumLabels;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<FStateDefine> & statelist)
{
for(unsigned i = 0;i<statelist.Size(); i++)
{
if (statelist[i].State == (FState*)count)
{
statelist[i].State = target == NULL ? NULL : (FState *)copystring (target);
}
if (statelist[i].Children.Size() > 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<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
size_t v=(size_t)list[i].State;
if (v >= 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<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
if (list[i].State != NULL && FState::StaticFindStateOwner (list[i].State, actor) == NULL)
{ // It's not a valid state, so it must be a label string. Resolve it.
list[i].State = ResolveGotoLabel (defaults, actor->Class, (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;
}

View File

@ -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;

View File

@ -840,6 +840,10 @@
RelativePath=".\src\p_spec.cpp"
>
</File>
<File
RelativePath=".\src\p_states.cpp"
>
</File>
<File
RelativePath=".\src\p_switch.cpp"
>