diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 1c0f846b9..d59b45256 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -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; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c7f181838..ce74684cc 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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; diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 1d39d907e..9ae7b530d 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -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; diff --git a/src/p_states.cpp b/src/p_states.cpp index 2c1b88eaf..f7f237c77 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -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 diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 24da14321..626dcde24 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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 diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index d1b79328f..a4cf20de4 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -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 &StateArray); -static void ParseSpriteFrames (PClassActor *info, TArray &states, FScanner &sc); + FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray &StateArray, TArray &SourceLines); +static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray &SourceLines, FScanner &sc); // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -147,6 +147,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) { Baggage bag; TArray StateArray; + TArray 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 &StateArray) + FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray &StateArray, TArray &SourceLines) { AFakeInventory *const inv = static_cast(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 &states, FScanner &sc) +static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray &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 &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 &states, FScann if (!firstState) { states.Push (state); + SourceLines.Push(sc); } token = strtok (NULL, ",\t\n\r"); diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index db424ed44..cad542d1b 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -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()); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d163ffb38..bf6b08ea7 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -72,6 +72,30 @@ void InitThingdef(); // STATIC FUNCTION PROTOTYPES -------------------------------------------- PClassActor *QuestItemClasses[31]; + +static TMap StateSourceLines; +static FScriptPosition unknownstatesource("unknown file", 0); + +//========================================================================== +// +// Saves the state's source lines for error messages during postprocessing +// +//========================================================================== + +void SaveStateSourceLines(FState *firststate, TArray &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 *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags) +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *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 *args, TArray *argflags, TArrayIsDescendantOf(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 argflags; TArray 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 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(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 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(); } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index eeea42f87..7218a84e6 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -63,6 +63,7 @@ class FStateDefinitions FState *laststatebeforelabel; intptr_t lastlabel; TArray StateArray; + TArray SourceLines; static FStateDefine *FindStateLabelInList(TArray &list, FName name, bool create); static FStateLabels *CreateStateLabelList(TArray &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 &positions); +FScriptPosition & GetStateSource(FState *state); + //========================================================================== // // Extra info maintained while defining an actor. @@ -152,7 +157,7 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags); +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *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); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 48be582cf..57475c50e 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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(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());