mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 07:11:54 +00:00
- added compile time checks for bad state links and state label references.
- preserve a state's source line information for the postprocessing phase so that the checker can output more useful information. - added missing check for weapon psprites to DPSprite::SetState.
This commit is contained in:
parent
647e1399f1
commit
96d093d01f
10 changed files with 179 additions and 45 deletions
|
@ -131,7 +131,7 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state)
|
|||
if (!(state->UseFlags & SUF_ITEM))
|
||||
{
|
||||
auto so = FState::StaticFindStateOwner(state);
|
||||
Printf("State %s.%d not flagged for use in CustomInventory state chains.\n", so->TypeName.GetChars(), int(state - so->OwnedStates));
|
||||
Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in CustomInventory state chains.\n", so->TypeName.GetChars(), int(state - so->OwnedStates));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -629,7 +629,7 @@ bool AActor::SetState (FState *newstate, bool nofunction)
|
|||
if (!(newstate->UseFlags & SUF_ACTOR))
|
||||
{
|
||||
auto so = FState::StaticFindStateOwner(newstate);
|
||||
Printf("State %s.%d in %s not flagged for use as an actor sprite\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates), GetClass()->TypeName.GetChars());
|
||||
Printf(TEXTCOLOR_RED "State %s.%d in %s not flagged for use as an actor sprite\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates), GetClass()->TypeName.GetChars());
|
||||
state = nullptr;
|
||||
Destroy();
|
||||
return false;
|
||||
|
|
|
@ -300,11 +300,22 @@ void DPSprite::SetState(FState *newstate, bool pending)
|
|||
if (!(newstate->UseFlags & (SUF_OVERLAY|SUF_WEAPON))) // Weapon and overlay are mostly the same, the main difference is that weapon states restrict the self pointer to class Actor.
|
||||
{
|
||||
auto so = FState::StaticFindStateOwner(newstate);
|
||||
Printf("State %s.%d not flagged for use in overlays or weapons\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates));
|
||||
Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in overlays or weapons\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates));
|
||||
State = nullptr;
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
else if (!(newstate->UseFlags & SUF_WEAPON))
|
||||
{
|
||||
if (Caller->IsKindOf(RUNTIME_CLASS(AWeapon)))
|
||||
{
|
||||
auto so = FState::StaticFindStateOwner(newstate);
|
||||
Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in weapons\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates));
|
||||
State = nullptr;
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
State = newstate;
|
||||
|
||||
|
|
|
@ -913,7 +913,7 @@ bool FStateDefinitions::SetLoop()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int FStateDefinitions::AddStates(FState *state, const char *framechars)
|
||||
int FStateDefinitions::AddStates(FState *state, const char *framechars, const FScriptPosition &sc)
|
||||
{
|
||||
bool error = false;
|
||||
int frame = 0;
|
||||
|
@ -939,6 +939,7 @@ int FStateDefinitions::AddStates(FState *state, const char *framechars)
|
|||
state->Frame = frame;
|
||||
state->SameFrame = noframe;
|
||||
StateArray.Push(*state);
|
||||
SourceLines.Push(sc);
|
||||
++count;
|
||||
|
||||
// NODELAY flag is not carried past the first state
|
||||
|
@ -968,6 +969,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
|
|||
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!
|
||||
|
@ -1012,6 +1014,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
|
|||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Prints all state label info to the logfile
|
||||
|
|
|
@ -5119,13 +5119,14 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
delete this;
|
||||
return newex->Resolve(ctx);
|
||||
}
|
||||
else if (ctx.FromDecorate && ctx.Class->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) && sym->IsKindOf(RUNTIME_CLASS(PField)))
|
||||
// Do this check for ZScript as well, so that a clearer error message can be printed. MSG_OPTERROR will default to MSG_ERROR there.
|
||||
else if (ctx.Function->Variants[0].SelfClass != ctx.Class && sym->IsKindOf(RUNTIME_CLASS(PField)))
|
||||
{
|
||||
FxExpression *self = new FxSelf(ScriptPosition, true);
|
||||
self = self->Resolve(ctx);
|
||||
newex = ResolveMember(ctx, ctx.Class, self, ctx.Class);
|
||||
ABORT(newex);
|
||||
ScriptPosition.Message(MSG_DEBUGWARN, "Self pointer used in ambiguous context; VM execution may abort!");
|
||||
ScriptPosition.Message(MSG_OPTERROR, "Self pointer used in ambiguous context; VM execution may abort!");
|
||||
ctx.Unsafe = true;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -103,8 +103,8 @@ IMPLEMENT_CLASS(AFakeInventory, false, false, false, false)
|
|||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray);
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScanner &sc);
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray, TArray<FScriptPosition> &SourceLines);
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, TArray<FScriptPosition> &SourceLines, FScanner &sc);
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
|
@ -147,6 +147,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
|
|||
{
|
||||
Baggage bag;
|
||||
TArray<FState> StateArray;
|
||||
TArray<FScriptPosition> SourceLines;
|
||||
FExtraInfo extra;
|
||||
PClassActor *type;
|
||||
PClassActor *parent;
|
||||
|
@ -168,7 +169,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
|
|||
sc.MustGetStringName("{");
|
||||
|
||||
memset (&extra, 0, sizeof(extra));
|
||||
ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc, StateArray);
|
||||
ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc, StateArray, SourceLines);
|
||||
|
||||
bag.Info->NumOwnedStates = StateArray.Size();
|
||||
if (bag.Info->NumOwnedStates == 0)
|
||||
|
@ -192,11 +193,14 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
|
|||
{
|
||||
// Make a copy of the final frozen frame for A_FreezeDeathChunks
|
||||
FState icecopy = StateArray[extra.IceDeathEnd-1];
|
||||
FScriptPosition icepos = SourceLines[extra.IceDeathEnd - 1];
|
||||
StateArray.Push (icecopy);
|
||||
SourceLines.Push(icepos);
|
||||
type->NumOwnedStates += 1;
|
||||
}
|
||||
|
||||
type->OwnedStates = new FState[type->NumOwnedStates];
|
||||
SaveStateSourceLines(type->OwnedStates, SourceLines);
|
||||
memcpy (type->OwnedStates, &StateArray[0], type->NumOwnedStates * sizeof(type->OwnedStates[0]));
|
||||
if (type->NumOwnedStates == 1)
|
||||
{
|
||||
|
@ -353,7 +357,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
|
|||
//==========================================================================
|
||||
|
||||
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray)
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray, TArray<FScriptPosition> &SourceLines)
|
||||
{
|
||||
AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults);
|
||||
char sprite[5] = "TNT1";
|
||||
|
@ -403,7 +407,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
|||
{
|
||||
sc.MustGetString ();
|
||||
extra.SpawnStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
ParseSpriteFrames (bag.Info, StateArray, SourceLines, sc);
|
||||
extra.SpawnEnd = StateArray.Size();
|
||||
}
|
||||
else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) &&
|
||||
|
@ -411,21 +415,21 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
|||
{
|
||||
sc.MustGetString ();
|
||||
extra.DeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
ParseSpriteFrames (bag.Info, StateArray, SourceLines, sc);
|
||||
extra.DeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.IceDeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
ParseSpriteFrames (bag.Info, StateArray, SourceLines, sc);
|
||||
extra.IceDeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.FireDeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
ParseSpriteFrames (bag.Info, StateArray, SourceLines, sc);
|
||||
extra.FireDeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("GenericIceDeath"))
|
||||
|
@ -650,12 +654,13 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
|||
// "10:A, 15:B, 8:C, 6:B"
|
||||
//==========================================================================
|
||||
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScanner &sc)
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, TArray<FScriptPosition> &SourceLines, FScanner &sc)
|
||||
{
|
||||
FState state;
|
||||
char *token = strtok (sc.String, ",\t\n\r");
|
||||
|
||||
memset (&state, 0, sizeof(state));
|
||||
state.UseFlags = info->DefaultStateUsage;
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
|
@ -705,6 +710,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScann
|
|||
if (!firstState)
|
||||
{
|
||||
states.Push (state);
|
||||
SourceLines.Push(sc);
|
||||
}
|
||||
firstState = false;
|
||||
state.Frame = *token-'A';
|
||||
|
@ -714,6 +720,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScann
|
|||
if (!firstState)
|
||||
{
|
||||
states.Push (state);
|
||||
SourceLines.Push(sc);
|
||||
}
|
||||
|
||||
token = strtok (NULL, ",\t\n\r");
|
||||
|
|
|
@ -145,6 +145,7 @@ void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage &
|
|||
FxExpression *ScriptCode;
|
||||
FArgumentList *args = nullptr;
|
||||
int flagdef = actor->DefaultStateUsage;
|
||||
FScriptPosition scp;
|
||||
|
||||
if (sc.CheckString("("))
|
||||
{
|
||||
|
@ -238,6 +239,7 @@ do_stop:
|
|||
sc.ScriptError ("Sprite names must be exactly 4 characters\n");
|
||||
}
|
||||
|
||||
scp = sc;
|
||||
state.sprite = GetSpriteIndex(statestring);
|
||||
state.Misc1 = state.Misc2 = 0;
|
||||
sc.MustGetString();
|
||||
|
@ -341,10 +343,10 @@ do_stop:
|
|||
endofstate:
|
||||
if (ScriptCode != nullptr)
|
||||
{
|
||||
auto funcsym = CreateAnonymousFunction(actor, nullptr, VARF_Method | VARF_Action);
|
||||
auto funcsym = CreateAnonymousFunction(actor, nullptr, state.UseFlags);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true);
|
||||
}
|
||||
int count = bag.statedef.AddStates(&state, statestring);
|
||||
int count = bag.statedef.AddStates(&state, statestring, scp);
|
||||
if (count < 0)
|
||||
{
|
||||
sc.ScriptError("Invalid frame character string '%s'", statestring.GetChars());
|
||||
|
|
|
@ -72,6 +72,30 @@ void InitThingdef();
|
|||
// STATIC FUNCTION PROTOTYPES --------------------------------------------
|
||||
PClassActor *QuestItemClasses[31];
|
||||
|
||||
|
||||
static TMap<FState *, FScriptPosition> StateSourceLines;
|
||||
static FScriptPosition unknownstatesource("unknown file", 0);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Saves the state's source lines for error messages during postprocessing
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void SaveStateSourceLines(FState *firststate, TArray<FScriptPosition> &positions)
|
||||
{
|
||||
for (unsigned i = 0; i < positions.Size(); i++)
|
||||
{
|
||||
StateSourceLines[firststate + i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
FScriptPosition & GetStateSource(FState *state)
|
||||
{
|
||||
auto check = StateSourceLines.CheckKey(state);
|
||||
return check ? *check : unknownstatesource;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SetImplicitArgs
|
||||
|
@ -80,7 +104,7 @@ PClassActor *QuestItemClasses[31];
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags)
|
||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags)
|
||||
{
|
||||
// Must be called before adding any other arguments.
|
||||
assert(args == nullptr || args->Size() == 0);
|
||||
|
@ -98,8 +122,8 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
|
|||
// implied caller and callingstate pointers
|
||||
if (args != nullptr)
|
||||
{
|
||||
// Special treatment for weapons and CustomInventorys: 'self' is not the defining class but the actual user of the item, so this pointer must be of type 'Actor'
|
||||
if (cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
|
||||
// Special treatment for weapons and CustomInventory flagged functions: 'self' is not the defining class but the actual user of the item, so this pointer must be of type 'Actor'
|
||||
if (useflags & (SUF_WEAPON|SUF_ITEM))
|
||||
{
|
||||
args->Insert(0, NewPointer(RUNTIME_CLASS(AActor))); // this must go in before the real pointer to the containing class.
|
||||
}
|
||||
|
@ -139,11 +163,14 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i
|
|||
TArray<uint32_t> argflags;
|
||||
TArray<FName> argnames;
|
||||
|
||||
// Functions that only get flagged for actors do not need the additional two context parameters.
|
||||
int fflags = (flags& (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM)) ? VARF_Action | VARF_Method : VARF_Method;
|
||||
|
||||
rets[0] = returntype != nullptr? returntype : TypeError; // Use TypeError as placeholder if we do not know the return type yet.
|
||||
SetImplicitArgs(&args, &argflags, &argnames, containingclass, flags);
|
||||
SetImplicitArgs(&args, &argflags, &argnames, containingclass, fflags, flags);
|
||||
|
||||
PFunction *sym = new PFunction(containingclass, NAME_None); // anonymous functions do not have names.
|
||||
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, flags);
|
||||
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, fflags);
|
||||
return sym;
|
||||
}
|
||||
|
||||
|
@ -201,7 +228,7 @@ void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id,
|
|||
else
|
||||
{
|
||||
auto dmg = new FxReturnStatement(new FxIntCast(id, true), id->ScriptPosition);
|
||||
auto funcsym = CreateAnonymousFunction(info, TypeSInt32, VARF_Method);
|
||||
auto funcsym = CreateAnonymousFunction(info, TypeSInt32, 0);
|
||||
defaults->DamageFunc = FunctionBuildList.AddFunction(funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate);
|
||||
}
|
||||
}
|
||||
|
@ -215,25 +242,24 @@ void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id,
|
|||
// For such cases a runtime check in the relevant places is also present.
|
||||
//
|
||||
//==========================================================================
|
||||
static int CheckForUnsafeStates(PClassActor *obj)
|
||||
static void CheckForUnsafeStates(PClassActor *obj)
|
||||
{
|
||||
static ENamedName weaponstates[] = { NAME_Ready, NAME_Deselect, NAME_Select, NAME_Fire, NAME_AltFire, NAME_Hold, NAME_AltHold, NAME_Flash, NAME_AltFlash, NAME_None };
|
||||
static ENamedName pickupstates[] = { NAME_Pickup, NAME_Drop, NAME_Use, NAME_None };
|
||||
TMap<FState *, bool> checked;
|
||||
ENamedName *test;
|
||||
int errors = 0;
|
||||
|
||||
if (obj->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
||||
{
|
||||
if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return 0; // This class cannot have user variables.
|
||||
if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return; // This class cannot have user variables.
|
||||
test = weaponstates;
|
||||
}
|
||||
else if (obj->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
|
||||
{
|
||||
if (obj->Size == RUNTIME_CLASS(ACustomInventory)->Size) return 0; // This class cannot have user variables.
|
||||
if (obj->Size == RUNTIME_CLASS(ACustomInventory)->Size) return; // This class cannot have user variables.
|
||||
test = pickupstates;
|
||||
}
|
||||
else return 0; // something else derived from AStateProvider. We do not know what this may be.
|
||||
else return; // something else derived from AStateProvider. We do not know what this may be.
|
||||
|
||||
for (; *test != NAME_None; test++)
|
||||
{
|
||||
|
@ -245,14 +271,87 @@ static int CheckForUnsafeStates(PClassActor *obj)
|
|||
{
|
||||
// If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash.
|
||||
auto owner = FState::StaticFindStateOwner(state);
|
||||
Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s, reached by %s.%s which accesses user variables.\n",
|
||||
owner->TypeName.GetChars(), state - owner->OwnedStates, static_cast<VMScriptFunction *>(state->ActionFunc)->PrintableName.GetChars(), obj->TypeName.GetChars(), FName(*test).GetChars());
|
||||
errors++;
|
||||
GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "Unsafe state call in state %s.%d which accesses user variables, reached by %s.%s.\n",
|
||||
owner->TypeName.GetChars(), state - owner->OwnedStates, obj->TypeName.GetChars(), FName(*test).GetChars());
|
||||
}
|
||||
state = state->NextState;
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CheckStates
|
||||
//
|
||||
// Checks if states link to ones with proper restrictions
|
||||
// Checks that all base labels refer a string with proper restrictions.
|
||||
// For these cases a runtime check in the relevant places is also present.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void CheckLabel(PClassActor *obj, FStateLabel *slb, int useflag, FName statename, const char *descript)
|
||||
{
|
||||
auto state = slb->State;
|
||||
if (state != nullptr)
|
||||
{
|
||||
if (!(state->UseFlags & useflag))
|
||||
{
|
||||
auto owner = FState::StaticFindStateOwner(state);
|
||||
GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "%s references state %s.%d as %s state, but this state is not flagged for use as %s.\n",
|
||||
obj->TypeName.GetChars(), owner->TypeName.GetChars(), int(state - owner->OwnedStates), statename.GetChars(), descript);
|
||||
}
|
||||
}
|
||||
if (slb->Children != nullptr)
|
||||
{
|
||||
for (int i = 0; i < slb->Children->NumLabels; i++)
|
||||
{
|
||||
auto state = slb->Children->Labels[i].State;
|
||||
CheckLabel(obj, &slb->Children->Labels[i], useflag, statename, descript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckStateLabels(PClassActor *obj, ENamedName *test, int useflag, const char *descript)
|
||||
{
|
||||
FStateLabels *labels = obj->StateList;
|
||||
|
||||
for (; *test != NAME_None; test++)
|
||||
{
|
||||
auto label = labels->FindLabel(*test);
|
||||
if (label != nullptr)
|
||||
{
|
||||
CheckLabel(obj, label, useflag, *test, descript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CheckStates(PClassActor *obj)
|
||||
{
|
||||
static ENamedName actorstates[] = { NAME_Spawn, NAME_See, NAME_Melee, NAME_Missile, NAME_Pain, NAME_Death, NAME_Wound, NAME_Raise, NAME_Yes, NAME_No, NAME_Greetings, NAME_None };
|
||||
static ENamedName weaponstates[] = { NAME_Ready, NAME_Deselect, NAME_Select, NAME_Fire, NAME_AltFire, NAME_Hold, NAME_AltHold, NAME_Flash, NAME_AltFlash, NAME_None };
|
||||
static ENamedName pickupstates[] = { NAME_Pickup, NAME_Drop, NAME_Use, NAME_None };
|
||||
TMap<FState *, bool> checked;
|
||||
|
||||
CheckStateLabels(obj, actorstates, SUF_ACTOR, "actor sprites");
|
||||
|
||||
if (obj->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
||||
{
|
||||
CheckStateLabels(obj, weaponstates, SUF_WEAPON, "weapon sprites");
|
||||
}
|
||||
else if (obj->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
|
||||
{
|
||||
CheckStateLabels(obj, pickupstates, SUF_ITEM, "CustomInventory state chain");
|
||||
}
|
||||
for (int i = 0; i < obj->NumOwnedStates; i++)
|
||||
{
|
||||
auto state = obj->OwnedStates + i;
|
||||
if (state->NextState && (state->UseFlags & state->NextState->UseFlags) != state->UseFlags)
|
||||
{
|
||||
GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "State %s.%d links to a state with incompatible restrictions.\n",
|
||||
obj->TypeName.GetChars(), int(state - obj->OwnedStates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -285,41 +384,44 @@ void LoadActors ()
|
|||
{
|
||||
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
|
||||
}
|
||||
FScriptPosition::ResetErrorCounter();
|
||||
|
||||
int errorcount = 0;
|
||||
for (auto ti : PClassActor::AllActorClasses)
|
||||
{
|
||||
if (ti->Size == TentativeClass)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
|
||||
errorcount++;
|
||||
FScriptPosition::ErrorCounter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetDefaultByType(ti) == nullptr)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars());
|
||||
errorcount++;
|
||||
FScriptPosition::ErrorCounter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
CheckStates(ti);
|
||||
|
||||
if (ti->bDecorateClass && ti->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
|
||||
{
|
||||
// either a DECORATE based weapon or CustomInventory.
|
||||
// These are subject to relaxed rules for user variables in states.
|
||||
// Although there is a runtime check for bogus states, let's do a quick analysis if any of the known entry points
|
||||
// hits an unsafe state. If we can find something here it can be handled wuth a compile error rather than a runtime error.
|
||||
errorcount += CheckForUnsafeStates(ti);
|
||||
CheckForUnsafeStates(ti);
|
||||
}
|
||||
|
||||
}
|
||||
if (errorcount > 0)
|
||||
if (FScriptPosition::ErrorCounter > 0)
|
||||
{
|
||||
I_Error("%d errors during actor postprocessing", errorcount);
|
||||
I_Error("%d errors during actor postprocessing", FScriptPosition::ErrorCounter);
|
||||
}
|
||||
|
||||
timer.Unclock();
|
||||
if (!batchrun) Printf("DECORATE parsing took %.2f ms\n", timer.TimeMS());
|
||||
// Base time: ~52 ms
|
||||
if (!batchrun) Printf("script parsing took %.2f ms\n", timer.TimeMS());
|
||||
|
||||
// Since these are defined in DECORATE now the table has to be initialized here.
|
||||
for (int i = 0; i < 31; i++)
|
||||
|
@ -328,4 +430,5 @@ void LoadActors ()
|
|||
mysnprintf(fmt, countof(fmt), "QuestItem%d", i + 1);
|
||||
QuestItemClasses[i] = PClass::FindActor(fmt);
|
||||
}
|
||||
StateSourceLines.Clear();
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ class FStateDefinitions
|
|||
FState *laststatebeforelabel;
|
||||
intptr_t lastlabel;
|
||||
TArray<FState> StateArray;
|
||||
TArray<FScriptPosition> SourceLines;
|
||||
|
||||
static FStateDefine *FindStateLabelInList(TArray<FStateDefine> &list, FName name, bool create);
|
||||
static FStateLabels *CreateStateLabelList(TArray<FStateDefine> &statelist);
|
||||
|
@ -97,10 +98,14 @@ public:
|
|||
bool SetStop();
|
||||
bool SetWait();
|
||||
bool SetLoop();
|
||||
int AddStates(FState *state, const char *framechars);
|
||||
int AddStates(FState *state, const char *framechars, const FScriptPosition &sc);
|
||||
int GetStateCount() const { return StateArray.Size(); }
|
||||
};
|
||||
|
||||
|
||||
void SaveStateSourceLines(FState *firststate, TArray<FScriptPosition> &positions);
|
||||
FScriptPosition & GetStateSource(FState *state);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Extra info maintained while defining an actor.
|
||||
|
@ -152,7 +157,7 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
|
|||
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
|
||||
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
|
||||
FName CheckCastKludges(FName in);
|
||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags);
|
||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags);
|
||||
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
|
||||
PFunction *FindClassMemberFunction(PClass *cls, PClass *funccls, FName name, FScriptPosition &sc, bool *error);
|
||||
void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate);
|
||||
|
|
|
@ -2096,7 +2096,9 @@ void ZCCCompiler::InitFunctions()
|
|||
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
|
||||
}
|
||||
}
|
||||
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
|
||||
// Todo: parse these values from the definition
|
||||
int tempuseflags = (varflags & VARF_Action) ? SUF_WEAPON | SUF_ITEM | SUF_OVERLAY | SUF_ACTOR : SUF_ACTOR;
|
||||
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, tempuseflags);
|
||||
argdefaults.Resize(argnames.Size());
|
||||
auto p = f->Params;
|
||||
bool hasoptionals = false;
|
||||
|
@ -2518,12 +2520,12 @@ void ZCCCompiler::CompileStates()
|
|||
auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action);
|
||||
if (code != nullptr)
|
||||
{
|
||||
auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, VARF_Method | VARF_Action);
|
||||
auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
|
||||
}
|
||||
}
|
||||
|
||||
int count = statedef.AddStates(&state, sl->Frames->GetChars());
|
||||
int count = statedef.AddStates(&state, sl->Frames->GetChars(), *sl);
|
||||
if (count < 0)
|
||||
{
|
||||
Error(sl, "Invalid frame character string '%s'", sl->Frames->GetChars());
|
||||
|
|
Loading…
Reference in a new issue