- 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) September 21, 2008 (Changes by Graf Zahl)
- did some code cleanup and reorganization in thingdef.cpp. - did some code cleanup and reorganization in thingdef.cpp.
- Replaced the translation parser for TEXTURES with FRemapTable::AddToTranslation. - Replaced the translation parser for TEXTURES with FRemapTable::AddToTranslation.

View file

@ -785,8 +785,18 @@ public:
bool isFast(); bool isFast();
void SetIdle(); void SetIdle();
FState *FindState (FName label) const; FState *FindState (FName label) const
FState *FindState (FName label, FName sublabel, bool exact = false) 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; bool HasSpecialDeathStates () const;
}; };

View file

@ -682,6 +682,7 @@ static int PatchThing (int thingy)
bool hadHeight = false; bool hadHeight = false;
bool hadTranslucency = false; bool hadTranslucency = false;
bool hadStyle = false; bool hadStyle = false;
FStateDefinitions statedef;
bool patchedStates = false; bool patchedStates = false;
int oldflags; int oldflags;
const PClass *type; const PClass *type;
@ -813,36 +814,36 @@ static int PatchThing (int thingy)
if (type != NULL && !patchedStates) if (type != NULL && !patchedStates)
{ {
MakeStateDefines(type->ActorInfo->StateList); statedef.MakeStateDefines(type);
patchedStates = true; patchedStates = true;
} }
if (!strnicmp (Line1, "Initial", 7)) 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)) else if (!strnicmp (Line1, "First moving", 12))
AddState("See", state); statedef.AddState("See", state);
else if (!strnicmp (Line1, "Injury", 6)) else if (!strnicmp (Line1, "Injury", 6))
AddState("Pain", state); statedef.AddState("Pain", state);
else if (!strnicmp (Line1, "Close attack", 12)) else if (!strnicmp (Line1, "Close attack", 12))
{ {
if (thingy != 1) // Not for players! if (thingy != 1) // Not for players!
{ {
AddState("Melee", state); statedef.AddState("Melee", state);
} }
} }
else if (!strnicmp (Line1, "Far attack", 10)) else if (!strnicmp (Line1, "Far attack", 10))
{ {
if (thingy != 1) // Not for players! if (thingy != 1) // Not for players!
{ {
AddState("Missile", state); statedef.AddState("Missile", state);
} }
} }
else if (!strnicmp (Line1, "Death", 5)) else if (!strnicmp (Line1, "Death", 5))
AddState("Death", state); statedef.AddState("Death", state);
else if (!strnicmp (Line1, "Exploding", 9)) else if (!strnicmp (Line1, "Exploding", 9))
AddState("XDeath", state); statedef.AddState("XDeath", state);
else if (!strnicmp (Line1, "Respawn", 7)) else if (!strnicmp (Line1, "Respawn", 7))
AddState("Raise", state); statedef.AddState("Raise", state);
} }
else if (stricmp (Line1 + linelen - 6, " sound") == 0) else if (stricmp (Line1 + linelen - 6, " sound") == 0)
{ {
@ -1048,7 +1049,7 @@ static int PatchThing (int thingy)
} }
if (patchedStates) if (patchedStates)
{ {
InstallStates(type->ActorInfo, info); statedef.InstallStates(type->ActorInfo, info);
} }
} }
@ -1359,6 +1360,7 @@ static int PatchWeapon (int weapNum)
AWeapon *info; AWeapon *info;
BYTE dummy[sizeof(AWeapon)]; BYTE dummy[sizeof(AWeapon)];
bool patchedStates = false; bool patchedStates = false;
FStateDefinitions statedef;
if (weapNum >= 0 && weapNum < 9) if (weapNum >= 0 && weapNum < 9)
{ {
@ -1385,20 +1387,20 @@ static int PatchWeapon (int weapNum)
if (type != NULL && !patchedStates) if (type != NULL && !patchedStates)
{ {
MakeStateDefines(type->ActorInfo->StateList); statedef.MakeStateDefines(type);
patchedStates = true; patchedStates = true;
} }
if (strnicmp (Line1, "Deselect", 8) == 0) if (strnicmp (Line1, "Deselect", 8) == 0)
AddState("Select", state); statedef.AddState("Select", state);
else if (strnicmp (Line1, "Select", 6) == 0) else if (strnicmp (Line1, "Select", 6) == 0)
AddState("Deselect", state); statedef.AddState("Deselect", state);
else if (strnicmp (Line1, "Bobbing", 7) == 0) else if (strnicmp (Line1, "Bobbing", 7) == 0)
AddState("Ready", state); statedef.AddState("Ready", state);
else if (strnicmp (Line1, "Shooting", 8) == 0) else if (strnicmp (Line1, "Shooting", 8) == 0)
AddState("Fire", state); statedef.AddState("Fire", state);
else if (strnicmp (Line1, "Firing", 6) == 0) else if (strnicmp (Line1, "Firing", 6) == 0)
AddState("Flash", state); statedef.AddState("Flash", state);
} }
else if (stricmp (Line1, "Ammo type") == 0) else if (stricmp (Line1, "Ammo type") == 0)
{ {
@ -1455,7 +1457,7 @@ static int PatchWeapon (int weapNum)
if (patchedStates) if (patchedStates)
{ {
InstallStates(type->ActorInfo, info); statedef.InstallStates(type->ActorInfo, info);
} }
return result; return result;
@ -2582,13 +2584,14 @@ void FinishDehPatch ()
memcpy (defaults2, defaults1, sizeof(AActor)); memcpy (defaults2, defaults1, sizeof(AActor));
// Make a copy the state labels // Make a copy the state labels
MakeStateDefines(type->ActorInfo->StateList);
if (!type->IsDescendantOf(RUNTIME_CLASS(AInventory))) if (!type->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{ {
// If this is a hacked non-inventory item we must also copy AInventory's special states // 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 // Use the DECORATE replacement feature to redirect all spawns
// of the original class to the new one. // of the original class to the new one.

View file

@ -51,122 +51,6 @@
extern void LoadDecorations (); 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; SBYTE Misc1;
BYTE Misc2; BYTE Misc2;
BYTE Frame; BYTE Frame;
BYTE DefineFlags; // Unused byte so let's use it during state creation.
FState *NextState; FState *NextState;
actionf_p ActionFunc; actionf_p ActionFunc;
int ParameterIndex; int ParameterIndex;
@ -204,8 +205,12 @@ struct FActorInfo
void SetDamageFactor(FName type, fixed_t factor); void SetDamageFactor(FName type, fixed_t factor);
void SetPainChance(FName type, int chance); void SetPainChance(FName type, int chance);
FState *FindState (FName name) const;
FState *FindState (int numnames, FName *names, bool exact=false) 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 *GetReplacement ();
FActorInfo *GetReplacee (); FActorInfo *GetReplacee ();
@ -252,6 +257,6 @@ private:
extern FDoomEdMap DoomEdMap; extern FDoomEdMap DoomEdMap;
int GetSpriteIndex(const char * spritename); int GetSpriteIndex(const char * spritename);
void MakeStateNameList(const char * fname, TArray<FName> * out); TArray<FName> &MakeStateNameList(const char * fname);
#endif // __INFO_H__ #endif // __INFO_H__

View file

@ -5230,16 +5230,13 @@ int DLevelScript::RunScript ()
case PCD_SETACTORSTATE: case PCD_SETACTORSTATE:
{ {
const char *statename = FBehavior::StaticLookupString (STACK(2)); const char *statename = FBehavior::StaticLookupString (STACK(2));
TArray<FName> statelist;
FState *state; FState *state;
MakeStateNameList(statename, &statelist);
if (STACK(3) == 0) if (STACK(3) == 0)
{ {
if (activator != NULL) 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) if (state != NULL)
{ {
activator->SetState (state); activator->SetState (state);
@ -5259,7 +5256,7 @@ int DLevelScript::RunScript ()
while ( (actor = iterator.Next ()) ) 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) if (state != NULL)
{ {
actor->SetState (state); 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) 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; FState *death;
if (flags5 & MF5_NODAMAGE) if (flags5 & MF5_NODAMAGE)
@ -4828,7 +4824,7 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN
// it needs to work. // it needs to work.
// Always kill if there is a regular death state or no death states at all. // 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; 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 --------------------------------------------- // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
TArray<FState> &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc); FExtraInfo &extra, EDefinitionType def, FScanner &sc);
static void ParseSpriteFrames (FActorInfo *info, TArray<FState> &states, FScanner &sc); static void ParseSpriteFrames (FActorInfo *info, TArray<FState> &states, FScanner &sc);
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -135,12 +135,13 @@ static const char *RenderStyles[] =
void ParseOldDecoration(FScanner &sc, EDefinitionType def) void ParseOldDecoration(FScanner &sc, EDefinitionType def)
{ {
TArray<FState> states; Baggage bag;
FExtraInfo extra; FExtraInfo extra;
FActorInfo *info;
PClass *type; PClass *type;
PClass *parent; PClass *parent;
FActorInfo *info;
FName typeName; FName typeName;
FStateDefinitions statedef;
if (def == DEF_Pickup) parent = RUNTIME_CLASS(AFakeInventory); if (def == DEF_Pickup) parent = RUNTIME_CLASS(AFakeInventory);
else parent = RUNTIME_CLASS(AActor); else parent = RUNTIME_CLASS(AActor);
@ -148,19 +149,17 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
sc.MustGetString(); sc.MustGetString();
typeName = FName(sc.String); typeName = FName(sc.String);
type = parent->CreateDerivedClass (typeName, parent->Size); type = parent->CreateDerivedClass (typeName, parent->Size);
info = type->ActorInfo; ResetBaggage(&bag, parent);
info->GameFilter = 0x80; info = bag.Info = type->ActorInfo;
MakeStateDefines(parent->ActorInfo->StateList);
info->GameFilter = GAME_Any; info->GameFilter = GAME_Any;
sc.MustGetStringName("{"); sc.MustGetStringName("{");
states.Clear ();
memset (&extra, 0, sizeof(extra)); 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(); bag.Info->NumOwnedStates = bag.StateArray.Size();
if (info->NumOwnedStates == 0) if (bag.Info->NumOwnedStates == 0)
{ {
sc.ScriptError ("%s does not define any animation frames", typeName.GetChars() ); 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) if (extra.IceDeathEnd != 0)
{ {
// Make a copy of the final frozen frame for A_FreezeDeathChunks // Make a copy of the final frozen frame for A_FreezeDeathChunks
FState icecopy = states[extra.IceDeathEnd-1]; FState icecopy = bag.StateArray[extra.IceDeathEnd-1];
states.Push (icecopy); bag.StateArray.Push (icecopy);
info->NumOwnedStates += 1; info->NumOwnedStates += 1;
} }
info->OwnedStates = new FState[info->NumOwnedStates]; 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) if (info->NumOwnedStates == 1)
{ {
info->OwnedStates->Tics = -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; if (extra.DeathHeight == 0) extra.DeathHeight = ((AActor*)(type->Defaults))->height;
info->Class->Meta.SetMetaFixed (AMETA_DeathHeight, extra.DeathHeight); 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 // 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; if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->height;
type->Meta.SetMetaFixed (AMETA_BurnHeight, extra.BurnHeight); 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 // 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].Tics = 1;
info->OwnedStates[i].Misc1 = 0; info->OwnedStates[i].Misc1 = 0;
info->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks")); info->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks"));
AddState("Ice", &info->OwnedStates[extra.IceDeathStart]); statedef.AddState("Ice", &info->OwnedStates[extra.IceDeathStart]);
} }
else if (extra.bGenericIceDeath) 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) if (def == DEF_BreakableDecoration)
@ -321,8 +320,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
{ {
((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE; ((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE;
} }
AddState("Spawn", &info->OwnedStates[extra.SpawnStart]); statedef.AddState("Spawn", &info->OwnedStates[extra.SpawnStart]);
InstallStates (info, ((AActor *)(type->Defaults))); statedef.InstallStates (info, ((AActor *)(type->Defaults)));
} }
//========================================================================== //==========================================================================
@ -333,8 +332,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
// //
//========================================================================== //==========================================================================
static void ParseInsideDecoration (FActorInfo *info, AActor *defaults, static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
TArray<FState> &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc) FExtraInfo &extra, EDefinitionType def, FScanner &sc)
{ {
AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults); AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults);
char sprite[5] = "TNT1"; 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]"); 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")) 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]"); 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") || ( else if (sc.Compare ("Sprite") || (
(def == DEF_BreakableDecoration || def == DEF_Projectile) && (def == DEF_BreakableDecoration || def == DEF_Projectile) &&
@ -383,31 +382,31 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
else if (sc.Compare ("Frames")) else if (sc.Compare ("Frames"))
{ {
sc.MustGetString (); sc.MustGetString ();
extra.SpawnStart = states.Size(); extra.SpawnStart = bag.StateArray.Size();
ParseSpriteFrames (info, states, sc); ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.SpawnEnd = states.Size(); extra.SpawnEnd = bag.StateArray.Size();
} }
else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) && else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) &&
sc.Compare ("DeathFrames")) sc.Compare ("DeathFrames"))
{ {
sc.MustGetString (); sc.MustGetString ();
extra.DeathStart = states.Size(); extra.DeathStart = bag.StateArray.Size();
ParseSpriteFrames (info, states, sc); ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.DeathEnd = states.Size(); extra.DeathEnd = bag.StateArray.Size();
} }
else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames")) else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames"))
{ {
sc.MustGetString (); sc.MustGetString ();
extra.IceDeathStart = states.Size(); extra.IceDeathStart = bag.StateArray.Size();
ParseSpriteFrames (info, states, sc); ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.IceDeathEnd = states.Size(); extra.IceDeathEnd = bag.StateArray.Size();
} }
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames")) else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames"))
{ {
sc.MustGetString (); sc.MustGetString ();
extra.FireDeathStart = states.Size(); extra.FireDeathStart = bag.StateArray.Size();
ParseSpriteFrames (info, states, sc); ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.FireDeathEnd = states.Size(); extra.FireDeathEnd = bag.StateArray.Size();
} }
else if (def == DEF_BreakableDecoration && sc.Compare ("GenericIceDeath")) 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")) else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius"))
{ {
sc.MustGetNumber (); sc.MustGetNumber ();
info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number); bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number);
extra.bExplosive = true; extra.bExplosive = true;
} }
else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage")) else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage"))
{ {
sc.MustGetNumber (); sc.MustGetNumber ();
info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number); bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number);
extra.bExplosive = true; extra.bExplosive = true;
} }
else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter")) 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")) 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")) else if (def == DEF_Pickup && sc.Compare ("PickupMessage"))
{ {
sc.MustGetString (); 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")) else if (def == DEF_Pickup && sc.Compare ("Respawns"))
{ {
@ -576,8 +575,6 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
} }
else if (sc.String[0] != '*') else if (sc.String[0] != '*')
{ {
Baggage bag;
bag.Info = info;
HandleActorFlag(sc, bag, sc.String, NULL, '+'); HandleActorFlag(sc, bag, sc.String, NULL, '+');
} }
else else
@ -590,16 +587,16 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
unsigned int i; unsigned int i;
int spr = GetSpriteIndex(sprite); 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) if (extra.DeathSprite[0] && extra.DeathEnd != 0)
{ {
int spr = GetSpriteIndex(extra.DeathSprite); int spr = GetSpriteIndex(extra.DeathSprite);
for (i = extra.DeathStart; i < extra.DeathEnd; ++i) 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. // be checked here.
JumpParameters.Push(NAME_None); JumpParameters.Push(NAME_None);
} }
TArray<FName> names;
MakeStateNameList(statestring, &names); TArray<FName> &names = MakeStateNameList(statestring);
if (stype != NULL) if (stype != NULL)
{ {
@ -420,8 +420,6 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac
info = ti->ActorInfo; info = ti->ActorInfo;
} }
MakeStateDefines(parent->ActorInfo->StateList);
info->DoomEdNum = -1; info->DoomEdNum = -1;
if (parent->ActorInfo->DamageFactors != NULL) if (parent->ActorInfo->DamageFactors != NULL)
{ {
@ -522,7 +520,7 @@ static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag)
try try
{ {
FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native); FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native);
ResetBaggage (bag); ResetBaggage (bag, info->Class->ParentClass);
bag->Info = info; bag->Info = info;
bag->Lumpnum = sc.LumpNum; bag->Lumpnum = sc.LumpNum;
return info; 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()); sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars());
break; break;
} }
FinishActor(sc, info, bag);
} }
FinishActor(sc, info, bag);
sc.SetCMode (false); 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. // Extra info maintained while defining an actor.
@ -62,16 +124,20 @@ struct Baggage
bool StateSet; bool StateSet;
int CurrentState; int CurrentState;
int Lumpnum; int Lumpnum;
FStateDefinitions statedef;
TArray<FState> StateArray;
FDropItem *DropItemList; FDropItem *DropItemList;
}; };
inline void ResetBaggage (Baggage *bag) inline void ResetBaggage (Baggage *bag, const PClass *stateclass)
{ {
bag->DropItemList = NULL; bag->DropItemList = NULL;
bag->DropItemSet = false; bag->DropItemSet = false;
bag->CurrentState = 0; bag->CurrentState = 0;
bag->StateSet = false; 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); 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); 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) else if (MatchString(propname, statenames) != -1)
{ {
AddState(propname, CheckState (sc, bag.Info->Class)); bag.statedef.AddState(propname, CheckState (sc, bag.Info->Class));
} }
else else
{ {
@ -528,13 +528,14 @@ void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag)
try try
{ {
FinishStates (info, defaults); bag.statedef.FinishStates (info, defaults, bag.StateArray);
} }
catch (CRecoverableError &err) catch (CRecoverableError &err)
{ {
sc.ScriptError(err.GetMessage()); sc.ScriptError(err.GetMessage());
} }
InstallStates (info, defaults); bag.statedef.InstallStates (info, defaults);
bag.StateArray.Clear ();
if (bag.DropItemSet) if (bag.DropItemSet)
{ {
if (bag.DropItemList == NULL) if (bag.DropItemList == NULL)

View file

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

View file

@ -57,9 +57,7 @@
#include "colormatcher.h" #include "colormatcher.h"
TArray<int> StateParameters; TArray<int> StateParameters;
TArray<FName> JumpParameters;
static TArray<AFuncDesc> AFTable; static TArray<AFuncDesc> AFTable;
static TArray<FState> StateArray;
//========================================================================== //==========================================================================
// //
@ -132,291 +130,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name)
return NULL; 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) if (laststate != NULL)
{ // Following a state definition: Modify it. { // Following a state definition: Modify it.
laststate->NextState = (FState*)copystring(statestring); laststate->NextState = (FState*)copystring(statestring);
laststate->DefineFlags = SDF_LABEL;
} }
else if (lastlabel >= 0) else if (lastlabel >= 0)
{ // Following a label: Retarget it. { // Following a label: Retarget it.
RetargetStates (count+1, statestring); bag.statedef.RetargetStates (count+1, statestring);
} }
else else
{ {
@ -567,11 +283,11 @@ do_goto:
do_stop: do_stop:
if (laststate!=NULL) if (laststate!=NULL)
{ {
laststate->NextState=(FState*)-1; laststate->DefineFlags = SDF_STOP;
} }
else if (lastlabel >=0) else if (lastlabel >=0)
{ {
RetargetStates (count+1, NULL); bag.statedef.RetargetStates (count+1, NULL);
} }
else else
{ {
@ -586,7 +302,7 @@ do_stop:
sc.ScriptError("%s before first state", sc.String); sc.ScriptError("%s before first state", sc.String);
continue; continue;
} }
laststate->NextState=(FState*)-2; laststate->DefineFlags = SDF_WAIT;
} }
else if (!statestring.CompareNoCase("LOOP")) else if (!statestring.CompareNoCase("LOOP"))
{ {
@ -596,6 +312,7 @@ do_stop:
continue; continue;
} }
laststate->NextState=(FState*)(lastlabel+1); laststate->NextState=(FState*)(lastlabel+1);
laststate->DefineFlags = SDF_INDEX;
} }
else else
{ {
@ -608,7 +325,7 @@ do_stop:
do do
{ {
lastlabel = count; lastlabel = count;
AddState(statestring, (FState *) (count+1)); bag.statedef.AddState(statestring, (FState *) (count+1), SDF_INDEX);
statestring = ParseStateString(sc); statestring = ParseStateString(sc);
if (!statestring.CompareNoCase("GOTO")) if (!statestring.CompareNoCase("GOTO"))
{ {
@ -789,7 +506,7 @@ do_stop:
} }
sc.UnGet(); sc.UnGet();
endofstate: endofstate:
StateArray.Push(state); bag.StateArray.Push(state);
while (*statestrp) while (*statestrp)
{ {
int frame=((*statestrp++)&223)-'A'; int frame=((*statestrp++)&223)-'A';
@ -801,10 +518,10 @@ endofstate:
} }
state.Frame=(state.Frame&(SF_FULLBRIGHT))|frame; state.Frame=(state.Frame&(SF_FULLBRIGHT))|frame;
StateArray.Push(state); bag.StateArray.Push(state);
count++; count++;
} }
laststate=&StateArray[count]; laststate=&bag.StateArray[count];
count++; count++;
} }
} }
@ -816,186 +533,3 @@ endofstate:
return count; 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_SETMASTER=16;
const int SXF_NOCHECKPOSITION = 32; const int SXF_NOCHECKPOSITION = 32;
const int SXF_TELEFRAG=64; const int SXF_TELEFRAG=64;
const int SXF_TRANSFERAMBUSHFLAG=128; // 128 was uses by Skulltag
const int SXF_TRANSFERAMBUSHFLAG=256;
// Flags for A_Chase // Flags for A_Chase
const int CHF_FASTCHASE = 1; const int CHF_FASTCHASE = 1;

View file

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