mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 14:51:51 +00:00
- 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:
parent
b523ebd2a7
commit
954955c5a5
16 changed files with 1001 additions and 904 deletions
|
@ -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.
|
||||
|
|
14
src/actor.h
14
src/actor.h
|
@ -785,8 +785,18 @@ public:
|
|||
bool isFast();
|
||||
void SetIdle();
|
||||
|
||||
FState *FindState (FName label) const;
|
||||
FState *FindState (FName label, FName sublabel, bool exact = false) const;
|
||||
FState *FindState (FName label) const
|
||||
{
|
||||
return GetClass()->ActorInfo->FindState(1, &label);
|
||||
}
|
||||
|
||||
FState *FindState (FName label, FName sublabel, bool exact = false) const
|
||||
{
|
||||
FName names[]={label, sublabel};
|
||||
return GetClass()->ActorInfo->FindState(2, &label, exact);
|
||||
}
|
||||
|
||||
|
||||
bool HasSpecialDeathStates () const;
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
319
src/info.cpp
319
src/info.cpp
|
@ -51,122 +51,6 @@
|
|||
|
||||
extern void LoadDecorations ();
|
||||
|
||||
// Each state is owned by an actor. Actors can own any number of
|
||||
// states, but a single state cannot be owned by more than one
|
||||
// actor. States are archived by recording the actor they belong
|
||||
// to and the index into that actor's list of states.
|
||||
|
||||
// For NULL states, which aren't owned by any actor, the owner
|
||||
// is recorded as AActor with the following state. AActor should
|
||||
// never actually have this many states of its own, so this
|
||||
// is (relatively) safe.
|
||||
|
||||
#define NULL_STATE_INDEX 127
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FArchive &operator<< (FArchive &arc, FState *&state)
|
||||
{
|
||||
const PClass *info;
|
||||
|
||||
if (arc.IsStoring ())
|
||||
{
|
||||
if (state == NULL)
|
||||
{
|
||||
arc.UserWriteClass (RUNTIME_CLASS(AActor));
|
||||
arc.WriteCount (NULL_STATE_INDEX);
|
||||
return arc;
|
||||
}
|
||||
|
||||
info = FState::StaticFindStateOwner (state);
|
||||
|
||||
if (info != NULL)
|
||||
{
|
||||
arc.UserWriteClass (info);
|
||||
arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this was never working as intended.
|
||||
I_Error ("Cannot find owner for state %p:\n"
|
||||
"%s %c%c %3d [%p] -> %p", state,
|
||||
sprites[state->sprite].name,
|
||||
state->GetFrame() + 'A',
|
||||
state->GetFullbright() ? '*' : ' ',
|
||||
state->GetTics(),
|
||||
state->GetAction(),
|
||||
state->GetNextState());
|
||||
*/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const PClass *info;
|
||||
DWORD ofs;
|
||||
|
||||
arc.UserReadClass (info);
|
||||
ofs = arc.ReadCount ();
|
||||
if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor))
|
||||
{
|
||||
state = NULL;
|
||||
}
|
||||
else if (info->ActorInfo != NULL)
|
||||
{
|
||||
state = info->ActorInfo->OwnedStates + ofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = NULL;
|
||||
}
|
||||
}
|
||||
return arc;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find the actor that a state belongs to.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const PClass *FState::StaticFindStateOwner (const FState *state)
|
||||
{
|
||||
for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i)
|
||||
{
|
||||
FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo;
|
||||
if (state >= info->OwnedStates &&
|
||||
state < info->OwnedStates + info->NumOwnedStates)
|
||||
{
|
||||
return info->Class;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find the actor that a state belongs to, but restrict the search to
|
||||
// the specified type and its ancestors.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info)
|
||||
{
|
||||
while (info != NULL)
|
||||
{
|
||||
if (state >= info->OwnedStates &&
|
||||
state < info->OwnedStates + info->NumOwnedStates)
|
||||
{
|
||||
return info->Class;
|
||||
}
|
||||
info = info->Class->ParentClass->ActorInfo;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
@ -344,209 +228,6 @@ void FActorInfo::SetPainChance(FName type, int chance)
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FStateLabel *FStateLabels::FindLabel (FName label)
|
||||
{
|
||||
return const_cast<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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
814
src/p_states.cpp
Normal 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, ¶ms[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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -573,8 +573,7 @@ DEFINE_PROPERTY(skip_super, 0, Actor)
|
|||
{
|
||||
FreeDropItemChain (bag.DropItemList);
|
||||
}
|
||||
ResetBaggage (&bag);
|
||||
MakeStateDefines(NULL);
|
||||
ResetBaggage (&bag, RUNTIME_CLASS(AActor));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -840,6 +840,10 @@
|
|||
RelativePath=".\src\p_spec.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\p_states.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\p_switch.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue