gzdoom/src/p_states.cpp
2024-04-08 19:14:08 +02:00

1170 lines
31 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 "cmdlib.h"
#include "c_dispatch.h"
#include "v_text.h"
#include "thingdef.h"
#include "r_state.h"
#include "templates.h"
#include "codegen.h"
// stores indices for symbolic state labels for some old-style DECORATE functions.
FStateLabelStorage StateLabels;
TMap<int, FState*> dehExtStates;
// 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.
//==========================================================================
//
// This wraps everything needed to get a current sprite from a state into
// one single script function.
//
//==========================================================================
DEFINE_ACTION_FUNCTION(FState, GetSpriteTexture)
{
PARAM_SELF_STRUCT_PROLOGUE(FState);
PARAM_INT(rotation);
PARAM_INT(skin);
PARAM_FLOAT(scalex);
PARAM_FLOAT(scaley);
PARAM_INT(spritenum);
PARAM_INT(framenum);
int sprnum = (spritenum == -1) ? self->sprite : spritenum;
int frnum = (framenum == -1) ? self->GetFrame() : framenum;
spriteframe_t *sprframe;
if (skin == 0)
{
sprframe = &SpriteFrames[sprites[sprnum].spriteframes + frnum];
}
else
{
sprframe = &SpriteFrames[sprites[Skins[skin].sprite].spriteframes + frnum];
scalex = Skins[skin].Scale.X;
scaley = Skins[skin].Scale.Y;
}
if (numret > 0) ret[0].SetInt(sprframe->Texture[rotation].GetIndex());
if (numret > 1) ret[1].SetInt(!!(sprframe->Flip & (1 << rotation)));
if (numret > 2) ret[2].SetVector2(DVector2(scalex, scaley));
return min(3, numret);
}
//==========================================================================
//
// 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 (info->OwnsState(state))
{
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 (info->OwnsState(state))
{
return info;
}
info = ValidateActor(info->ParentClass);
}
return NULL;
}
//==========================================================================
//
//
//==========================================================================
FString FState::StaticGetStateName(const FState *state, PClassActor *info)
{
auto so = FState::StaticFindStateOwner(state);
if (so == nullptr)
{
so = FState::StaticFindStateOwner(state, info);
}
if (so == nullptr)
{
if (state->DehIndex > 0) return FStringf("DehExtraState.%d", state->DehIndex);
return "<unknown>";
}
return FStringf("%s.%d", so->TypeName.GetChars(), int(state - so->GetStates()));
}
//==========================================================================
//
//
//==========================================================================
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();
M_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->GetStateLabels() != NULL)
{
FStateLabel *slabel = info->GetStateLabels()->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 = NAME_None, secondpart = NAME_None;
char *c;
// Handle the old names for the existing death states
char *name = copystring(fname);
firstpart = strtok(name, ".");
switch (firstpart.GetIndex())
{
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 = GetStateLabels();
FState *best = nullptr;
if (labels != nullptr)
{
int count = 0;
// Find the best-matching label for this class.
while (labels != nullptr && count < numnames)
{
FName label = *names++;
FStateLabel *slabel = labels->FindLabel(label);
if (slabel != nullptr)
{
count++;
labels = slabel->Children;
best = slabel->State;
}
else
{
break;
}
}
if (count < numnames && exact)
{
return nullptr;
}
}
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, bool exact)
{
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, exact);
}
}
return nullptr;
}
//==========================================================================
//
// State label conversion functions for scripts
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, FindState)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(newstate);
PARAM_BOOL(exact);
ACTION_RETURN_STATE(StateLabels.GetState(newstate, self->GetClass(), exact));
}
// same as above but context aware.
DEFINE_ACTION_FUNCTION(AActor, ResolveState)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE_ACTION(newstate);
ACTION_RETURN_STATE(newstate);
}
// find state by string instead of label
DEFINE_ACTION_FUNCTION(AActor, FindStateByString)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STRING(newstate);
PARAM_BOOL(exact);
ACTION_RETURN_STATE(self->GetClass()->FindStateByString(newstate.GetChars(), 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 = nullptr;
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, uint8_t 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;
}
if ((size_t)std->State <= StateArray.Size() + 1)
return (int)((ptrdiff_t)std->State - 1);
else
return -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.GetIndex() - (int)B->Label.GetIndex());
}
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);
}
auto &sl = info->ActorInfo()->StateList;
if (sl != NULL)
{
sl->Destroy();
M_Free(sl);
}
sl = 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->GetStateLabels() != NULL)
{
MakeStateList(cls->GetStateLabels(), 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 *)FxAlloc.Strdup(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 (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 = ValidateActor(type->ParentClass);
}
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);
}
}
}
label = name;
// Check for offset
offset = NULL;
if ((pt = strchr (name, '+')) != NULL)
{
*pt = '\0';
offset = pt + 1;
}
v = offset ? (int)strtoll (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());
}
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->GetStates() + 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, 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 (actor, (char *)list[i].State);
list[i].DefineFlags = SDF_STATE;
}
if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, 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*)FxAlloc.Strdup(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*)FxAlloc.Strdup(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;
if (noframe) state->StateFlags |= STF_SAMEFRAME;
else state->StateFlags &= ~STF_SAMEFRAME;
StateArray.Push(*state);
SourceLines.Push(sc);
++count;
// NODELAY flag is not carried past the first state
state->StateFlags &= ~STF_NODELAY;
}
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)
{
int count = StateArray.Size();
if (count > 0)
{
FState *realstates = (FState*)ClassDataAllocator.Alloc(count * sizeof(FState));
int i;
memcpy(realstates, &StateArray[0], count*sizeof(FState));
actor->ActorInfo()->OwnedStates = realstates;
actor->ActorInfo()->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, StateLabels);
for (i = 0; i < count; i++)
{
realstates[i].DehIndex = -1;
// 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(actor, (char *)realstates[i].NextState);
break;
}
}
}
else
{
// Fix state pointers that are gotos
ResolveGotoLabels(actor, 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++)
{
auto state = StateList->Labels[i].State;
if (state != NULL)
{
const PClassActor *owner = FState::StaticFindStateOwner(state);
if (owner == NULL)
{
if (state->DehIndex >= 0)
Printf(PRINT_LOG, "%s%s: DehExtra %d\n", prefix.GetChars(), state->DehIndex);
else
Printf(PRINT_LOG, "%s%s: invalid\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars());
}
else
{
Printf(PRINT_LOG, "%s%s: %s\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), FState::StaticGetStateName(state).GetChars());
}
}
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->GetStateLabels(), "");
Printf(PRINT_LOG, "----------------------------\n");
}
}
//==========================================================================
//
// sets up the script-side version of states
//
//==========================================================================
DEFINE_FIELD(FState, NextState)
DEFINE_FIELD(FState, sprite)
DEFINE_FIELD(FState, Tics)
DEFINE_FIELD(FState, TicRange)
DEFINE_FIELD(FState, Frame)
DEFINE_FIELD(FState, UseFlags)
DEFINE_FIELD(FState, Misc1)
DEFINE_FIELD(FState, Misc2)
DEFINE_FIELD_BIT(FState, StateFlags, bSlow, STF_SLOW)
DEFINE_FIELD_BIT(FState, StateFlags, bFast, STF_FAST)
DEFINE_FIELD_BIT(FState, StateFlags, bFullbright, STF_FULLBRIGHT)
DEFINE_FIELD_BIT(FState, StateFlags, bNoDelay, STF_NODELAY)
DEFINE_FIELD_BIT(FState, StateFlags, bSameFrame, STF_SAMEFRAME)
DEFINE_FIELD_BIT(FState, StateFlags, bCanRaise, STF_CANRAISE)
DEFINE_FIELD_BIT(FState, StateFlags, bDehacked, STF_DEHACKED)
DEFINE_ACTION_FUNCTION(FState, DistanceTo)
{
PARAM_SELF_STRUCT_PROLOGUE(FState);
PARAM_POINTER(other, FState);
int retv = INT_MIN;
if (other != nullptr)
{
// Safely calculate the distance between two states.
auto o1 = FState::StaticFindStateOwner(self);
if (o1 && o1->OwnsState(other)) retv = int(other - self);
}
ACTION_RETURN_INT(retv);
}
DEFINE_ACTION_FUNCTION(FState, ValidateSpriteFrame)
{
PARAM_SELF_STRUCT_PROLOGUE(FState);
ACTION_RETURN_BOOL(self->Frame < sprites[self->sprite].numframes);
}