mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-28 23:11:58 +00:00
d86f03e2e0
- instead add a list of SpecialInits to VMScriptFunction so this can be done transparently when setting up and popping the stack frame. The only drawback is that this requires permanent allocation of stack objects for the entire lifetime of a function but this is a relatively small tradeoff for significantly reduced maintenance work throughout. - removed most #include "vm.h", because nearly all files already pull this in through dobject.h.
1056 lines
28 KiB
C++
1056 lines
28 KiB
C++
/*
|
|
** 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 "templates.h"
|
|
#include "cmdlib.h"
|
|
#include "i_system.h"
|
|
#include "c_dispatch.h"
|
|
#include "v_text.h"
|
|
#include "thingdef.h"
|
|
|
|
// stores indices for symbolic state labels for some old-style DECORATE functions.
|
|
FStateLabelStorage StateLabels;
|
|
|
|
// 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.
|
|
|
|
//==========================================================================
|
|
//
|
|
// Find the actor that a state belongs to.
|
|
//
|
|
//==========================================================================
|
|
|
|
PClassActor *FState::StaticFindStateOwner (const FState *state)
|
|
{
|
|
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
|
|
{
|
|
PClassActor *info = PClassActor::AllActorClasses[i];
|
|
if (state >= info->OwnedStates &&
|
|
state < info->OwnedStates + info->NumOwnedStates)
|
|
{
|
|
return info;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Find the actor that a state belongs to, but restrict the search to
|
|
// the specified type and its ancestors.
|
|
//
|
|
//==========================================================================
|
|
|
|
PClassActor *FState::StaticFindStateOwner (const FState *state, PClassActor *info)
|
|
{
|
|
while (info != NULL)
|
|
{
|
|
if (state >= info->OwnedStates &&
|
|
state < info->OwnedStates + info->NumOwnedStates)
|
|
{
|
|
return info;
|
|
}
|
|
info = dyn_cast<PClassActor>(info->ParentClass);
|
|
}
|
|
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 PClassActor *info = static_cast<PClassActor *>(GetClass());
|
|
|
|
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;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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 *PClassActor::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 *PClassActor::FindStateByString(const char *name, bool exact)
|
|
{
|
|
TArray<FName> &namelist = MakeStateNameList(name);
|
|
return FindState(namelist.Size(), &namelist[0], exact);
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// validate a runtime state index.
|
|
//
|
|
//==========================================================================
|
|
|
|
static bool VerifyJumpTarget(PClassActor *cls, FState *CallingState, int index)
|
|
{
|
|
while (cls != RUNTIME_CLASS(AActor))
|
|
{
|
|
// both calling and target state need to belong to the same class.
|
|
if (cls->OwnsState(CallingState))
|
|
{
|
|
return cls->OwnsState(CallingState + index);
|
|
}
|
|
|
|
// We can safely assume the ParentClass is of type PClassActor
|
|
// since we stop when we see the Actor base class.
|
|
cls = static_cast<PClassActor *>(cls->ParentClass);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Get a statw pointer from a symbolic label
|
|
//
|
|
//==========================================================================
|
|
|
|
FState *FStateLabelStorage::GetState(int pos, PClassActor *cls)
|
|
{
|
|
if (pos > 0x10000000)
|
|
{
|
|
return cls? cls->FindState(ENamedName(pos - 0x10000000)) : nullptr;
|
|
}
|
|
else if (pos < 0)
|
|
{
|
|
// decode the combined value produced by the script.
|
|
int index = (pos >> 16) & 32767;
|
|
pos = ((pos & 65535) - 1) * 4;
|
|
FState *state;
|
|
memcpy(&state, &Storage[pos + sizeof(int)], sizeof(state));
|
|
if (VerifyJumpTarget(cls, state, index))
|
|
return state + index;
|
|
else
|
|
return nullptr;
|
|
}
|
|
else if (pos > 0)
|
|
{
|
|
int val;
|
|
pos = (pos - 1) * 4;
|
|
memcpy(&val, &Storage[pos], sizeof(int));
|
|
|
|
if (val == 0)
|
|
{
|
|
FState *state;
|
|
memcpy(&state, &Storage[pos + sizeof(int)], sizeof(state));
|
|
return state;
|
|
}
|
|
else if (cls != nullptr)
|
|
{
|
|
FName *labels = (FName*)&Storage[pos + sizeof(int)];
|
|
return cls->FindState(val, labels, false);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// State label conversion function for scripts
|
|
//
|
|
//==========================================================================
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, FindState)
|
|
{
|
|
PARAM_SELF_PROLOGUE(AActor);
|
|
PARAM_STATE(newstate);
|
|
ACTION_RETURN_STATE(newstate);
|
|
}
|
|
|
|
// same as above but context aware.
|
|
DEFINE_ACTION_FUNCTION(AActor, ResolveState)
|
|
{
|
|
PARAM_ACTION_PROLOGUE(AActor);
|
|
PARAM_STATE_ACTION(newstate);
|
|
ACTION_RETURN_STATE(newstate);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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 to the curremt list
|
|
//
|
|
//==========================================================================
|
|
|
|
void FStateDefinitions::SetStateLabel(const char *statename, FState *state, BYTE defflags)
|
|
{
|
|
FStateDefine *std = FindStateAddress(statename);
|
|
std->State = state;
|
|
std->DefineFlags = defflags;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Adds a new state to the current list
|
|
//
|
|
//==========================================================================
|
|
|
|
void FStateDefinitions::AddStateLabel(const char *statename)
|
|
{
|
|
intptr_t index = StateArray.Size();
|
|
FStateDefine *std = FindStateAddress(statename);
|
|
std->State = (FState *)(index+1);
|
|
std->DefineFlags = SDF_INDEX;
|
|
laststate = NULL;
|
|
lastlabel = index;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Returns the index a state label points to. May only be called before
|
|
// installing states.
|
|
//
|
|
//==========================================================================
|
|
|
|
int FStateDefinitions::GetStateLabelIndex (FName statename)
|
|
{
|
|
FStateDefine *std = FindStateLabelInList(StateLabels, statename, false);
|
|
if (std == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
assert((size_t)std->State <= StateArray.Size() + 1);
|
|
return (int)((ptrdiff_t)std->State - 1);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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 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(PClassActor *info, AActor *defaults)
|
|
{
|
|
if (defaults == nullptr)
|
|
{
|
|
I_Error("Called InstallStates without actor defaults in %s", info->TypeName.GetChars());
|
|
}
|
|
|
|
// 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.
|
|
SetStateLabel("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;
|
|
def.DefineFlags = SDF_STATE;
|
|
dest.Push(def);
|
|
if (list->Labels[i].Children != NULL)
|
|
{
|
|
MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FStateDefinitions::MakeStateDefines(const PClassActor *cls)
|
|
{
|
|
StateArray.Clear();
|
|
laststate = NULL;
|
|
laststatebeforelabel = NULL;
|
|
lastlabel = -1;
|
|
|
|
if (cls != NULL && cls->StateList != NULL)
|
|
{
|
|
MakeStateList(cls->StateList, StateLabels);
|
|
}
|
|
else
|
|
{
|
|
StateLabels.Clear();
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// 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;
|
|
def.DefineFlags = SDF_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 && statelist[i].DefineFlags == SDF_INDEX)
|
|
{
|
|
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, PClassActor *mytype, char *name)
|
|
{
|
|
PClassActor *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 = dyn_cast<PClassActor>(type->ParentClass);
|
|
actor = GetDefaultByType(type);
|
|
}
|
|
else
|
|
{
|
|
// first check whether a state of the desired name exists
|
|
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 = static_cast<PClassActor *>(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->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());
|
|
}
|
|
else
|
|
{
|
|
Printf (TEXTCOLOR_RED "Attempt to get invalid state %s from actor %s.\n", label, type->TypeName.GetChars());
|
|
}
|
|
delete[] namestart; // free the allocated string buffer
|
|
return state;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FixStatePointers
|
|
//
|
|
// Fixes an actor's default state pointers.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FStateDefinitions::FixStatePointers (PClassActor *actor, TArray<FStateDefine> & list)
|
|
{
|
|
for (unsigned i = 0; i < list.Size(); i++)
|
|
{
|
|
if (list[i].DefineFlags == SDF_INDEX)
|
|
{
|
|
size_t v = (size_t)list[i].State;
|
|
list[i].State = actor->OwnedStates + v - 1;
|
|
list[i].DefineFlags = SDF_STATE;
|
|
}
|
|
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 (PClassActor *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, (char *)list[i].State);
|
|
list[i].DefineFlags = SDF_STATE;
|
|
}
|
|
if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, defaults, list[i].Children);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// SetGotoLabel
|
|
//
|
|
// sets a jump at the current state or retargets a label
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FStateDefinitions::SetGotoLabel(const char *string)
|
|
{
|
|
// copy the text - this must be resolved later!
|
|
if (laststate != NULL)
|
|
{ // Following a state definition: Modify it.
|
|
laststate->NextState = (FState*)copystring(string);
|
|
laststate->DefineFlags = SDF_LABEL;
|
|
laststatebeforelabel = NULL;
|
|
return true;
|
|
}
|
|
else if (lastlabel >= 0)
|
|
{ // Following a label: Retarget it.
|
|
RetargetStates (lastlabel+1, string);
|
|
if (laststatebeforelabel != NULL)
|
|
{
|
|
laststatebeforelabel->NextState = (FState*)copystring(string);
|
|
laststatebeforelabel->DefineFlags = SDF_LABEL;
|
|
laststatebeforelabel = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SetStop
|
|
//
|
|
// sets a stop operation
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FStateDefinitions::SetStop()
|
|
{
|
|
if (laststate != NULL)
|
|
{
|
|
laststate->DefineFlags = SDF_STOP;
|
|
laststatebeforelabel = NULL;
|
|
return true;
|
|
}
|
|
else if (lastlabel >=0)
|
|
{
|
|
RetargetStates (lastlabel+1, NULL);
|
|
if (laststatebeforelabel != NULL)
|
|
{
|
|
laststatebeforelabel->DefineFlags = SDF_STOP;
|
|
laststatebeforelabel = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SetWait
|
|
//
|
|
// sets a wait or fail operation
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FStateDefinitions::SetWait()
|
|
{
|
|
if (laststate != NULL)
|
|
{
|
|
laststate->DefineFlags = SDF_WAIT;
|
|
laststatebeforelabel = NULL;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SetLoop
|
|
//
|
|
// sets a loop operation
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FStateDefinitions::SetLoop()
|
|
{
|
|
if (laststate != NULL)
|
|
{
|
|
laststate->DefineFlags = SDF_INDEX;
|
|
laststate->NextState = (FState*)(lastlabel+1);
|
|
laststatebeforelabel = NULL;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// AddStates
|
|
//
|
|
// Adds some state to the current definition set. Returns the number of
|
|
// states added. Positive = no errors, negative = errors.
|
|
//
|
|
//==========================================================================
|
|
|
|
int FStateDefinitions::AddStates(FState *state, const char *framechars, const FScriptPosition &sc)
|
|
{
|
|
bool error = false;
|
|
int frame = 0;
|
|
int count = 0;
|
|
while (*framechars)
|
|
{
|
|
bool noframe = false;
|
|
|
|
if (*framechars == '#')
|
|
noframe = true;
|
|
else if (*framechars == '^')
|
|
frame = '\\' - 'A';
|
|
else
|
|
frame = (*framechars & 223) - 'A';
|
|
|
|
framechars++;
|
|
if (frame < 0 || frame > 28)
|
|
{
|
|
frame = 0;
|
|
error = true;
|
|
}
|
|
|
|
state->Frame = frame;
|
|
state->SameFrame = noframe;
|
|
StateArray.Push(*state);
|
|
SourceLines.Push(sc);
|
|
++count;
|
|
|
|
// NODELAY flag is not carried past the first state
|
|
state->NoDelay = false;
|
|
}
|
|
laststate = &StateArray[StateArray.Size() - 1];
|
|
laststatebeforelabel = laststate;
|
|
return !error ? count : -count;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FinishStates
|
|
// copies a state block and fixes all state links using the current list of labels
|
|
//
|
|
//==========================================================================
|
|
|
|
int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
|
|
{
|
|
int count = StateArray.Size();
|
|
|
|
if (count > 0)
|
|
{
|
|
FState *realstates = new FState[count];
|
|
int i;
|
|
|
|
memcpy(realstates, &StateArray[0], count*sizeof(FState));
|
|
actor->OwnedStates = realstates;
|
|
actor->NumOwnedStates = count;
|
|
SaveStateSourceLines(realstates, SourceLines);
|
|
|
|
// adjust the state pointers
|
|
// In the case new states are added these must be adjusted, too!
|
|
FixStatePointers(actor, StateLabels);
|
|
|
|
// Fix state pointers that are gotos
|
|
ResolveGotoLabels(actor, defaults, StateLabels);
|
|
|
|
for (i = 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, (char *)realstates[i].NextState);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fix state pointers that are gotos
|
|
ResolveGotoLabels(actor, defaults, StateLabels);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// Prints all state label info to the logfile
|
|
//
|
|
//==========================================================================
|
|
|
|
void DumpStateHelper(FStateLabels *StateList, const FString &prefix)
|
|
{
|
|
for (int i = 0; i < StateList->NumLabels; i++)
|
|
{
|
|
if (StateList->Labels[i].State != NULL)
|
|
{
|
|
const PClassActor *owner = FState::StaticFindStateOwner(StateList->Labels[i].State);
|
|
if (owner == NULL)
|
|
{
|
|
Printf(PRINT_LOG, "%s%s: invalid\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars());
|
|
}
|
|
else
|
|
{
|
|
Printf(PRINT_LOG, "%s%s: %s.%d\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(),
|
|
owner->TypeName.GetChars(), int(StateList->Labels[i].State - owner->OwnedStates));
|
|
}
|
|
}
|
|
if (StateList->Labels[i].Children != NULL)
|
|
{
|
|
DumpStateHelper(StateList->Labels[i].Children, prefix + '.' + StateList->Labels[i].Label.GetChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
CCMD(dumpstates)
|
|
{
|
|
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
|
|
{
|
|
PClassActor *info = PClassActor::AllActorClasses[i];
|
|
Printf(PRINT_LOG, "State labels for %s\n", info->TypeName.GetChars());
|
|
DumpStateHelper(info->StateList, "");
|
|
Printf(PRINT_LOG, "----------------------------\n");
|
|
}
|
|
}
|