diff --git a/src/dobjtype.h b/src/dobjtype.h index db468870f6..30e22e8680 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -718,6 +718,7 @@ public: }; TArray Variants; PClass *OwningClass = nullptr; + int StateCount = 0; // needed to process state indices later. unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags); int GetImplicitArgs() diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 9cbc1c2b16..9e86fff8f7 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -1416,6 +1416,26 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) delete this; return x; } + else if (basex->IsNumeric() && basex->ValueType != TypeSound && basex->ValueType != TypeColor) + { + if (ctx.Function->SymbolName != NAME_None || !(ctx.Function->Variants[0].Flags & VARF_Action)) + { + ScriptPosition.Message(MSG_ERROR, "State jumps with index can only be used in anonymous state functions."); + delete this; + return nullptr; + } + if (ctx.Function->StateCount != 1) + { + ScriptPosition.Message(MSG_ERROR, "State jumps with index cannot be used on multistate definitions"); + delete this; + return nullptr; + } + FxExpression *x = new FxRuntimeStateIndex(basex); + x = x->Resolve(ctx); + basex = nullptr; + delete this; + return x; + } } else if (ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { @@ -7859,6 +7879,12 @@ FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx) delete this; return nullptr; } + else if (Index->isConstant() && static_cast(Index) < 0) + { + ScriptPosition.Message(MSG_ERROR, "State index must be positive"); + delete this; + return nullptr; + } else if (Index->ValueType->GetRegType() != REGT_INT) { // Float. Index = new FxIntCast(Index, ctx.FromDecorate); @@ -7912,8 +7938,7 @@ static int BuiltinHandleRuntimeState(VMFrameStack *stack, VMValue *param, TArray ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) { - // This code can only be called from DECORATE, not ZSCRIPT so any function going through here - // is an anoynmous one which are always marked as 'action'. + // This can only be called from inline state functions which must be VARF_Action. assert(build->NumImplicits >= NAP && build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits && "FxRuntimeStateIndex is only valid inside action functions"); diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index dda25f9265..59cf309386 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -322,7 +322,7 @@ do_stop: endofstate: if (ScriptCode != nullptr) { - auto funcsym = CreateAnonymousFunction(actor, nullptr, VARF_Method | VARF_Action); + auto funcsym = CreateAnonymousFunction(actor, nullptr, VARF_Method | VARF_Action, (int)statestring.Len()); state.ActionFunc = FunctionBuildList.AddFunction(funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true); } int count = bag.statedef.AddStates(&state, statestring); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 8c42e8987d..6f30f8add7 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -132,7 +132,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArray rets(1); TArray args; @@ -143,6 +143,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i SetImplicitArgs(&args, &argflags, &argnames, containingclass, flags); PFunction *sym = new PFunction(containingclass, NAME_None); // anonymous functions do not have names. + sym->StateCount = statecount; sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, flags); return sym; } @@ -201,7 +202,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, VARF_Method, 0); defaults->DamageFunc = FunctionBuildList.AddFunction(funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate); } } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index b2d34231de..3511d1ee1d 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -153,7 +153,7 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); FName CheckCastKludges(FName in); void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags); -PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags); +PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags, int statecount); 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 8555432c07..f296a91b6c 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2248,6 +2248,7 @@ void ZCCCompiler::CompileStates() FString statename; // The state builder wants the label as one complete string, not separated into tokens. FStateDefinitions statedef; statedef.MakeStateDefines(dyn_cast(c->Type()->ParentClass)); + int numframes = 0; for (auto s : c->States) { @@ -2279,13 +2280,15 @@ void ZCCCompiler::CompileStates() // It is important to call CheckRandom before Simplify, because Simplify will resolve the function's name to nonsense if (CheckRandom(sl->Duration)) { - auto func = static_cast(Simplify(sl->Duration, &c->Type()->Symbols, true)); + auto func = static_cast(sl->Duration); if (func->Parameters == func->Parameters->SiblingNext || func->Parameters != func->Parameters->SiblingNext->SiblingNext) { Error(sl, "Random duration requires exactly 2 parameters"); } - int v1 = GetInt(func->Parameters->Value); - int v2 = GetInt(static_cast(func->Parameters->SiblingNext)->Value); + auto p1 = Simplify(func->Parameters->Value, &c->Type()->Symbols, true); + auto p2 = Simplify(static_cast(func->Parameters->SiblingNext)->Value, &c->Type()->Symbols, true); + int v1 = GetInt(p1); + int v2 = GetInt(p2); if (v1 > v2) std::swap(v1, v2); state.Tics = (int16_t)clamp(v1, 0, INT16_MAX); state.TicRange = (uint16_t)clamp(v2 - v1, 0, UINT16_MAX); @@ -2346,7 +2349,7 @@ 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, VARF_Method | VARF_Action, (int)sl->Frames->Len()); state.ActionFunc = FunctionBuildList.AddFunction(funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false); } }