diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 5973f90b4..4c22eae16 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -75,7 +75,6 @@ AActor *SingleActorFromTID(int tid, AActor *defactor); static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_cabullet ("CustomBullet"); -static FRandom pr_cajump ("CustomJump"); static FRandom pr_cwbullet ("CustomWpBullet"); static FRandom pr_cwjump ("CustomWpJump"); static FRandom pr_cwpunch ("CustomWpPunch"); @@ -87,6 +86,7 @@ static FRandom pr_burst ("Burst"); static FRandom pr_monsterrefire ("MonsterRefire"); static FRandom pr_teleport("A_Teleport"); static FRandom pr_bfgselfdamage("BFGSelfDamage"); +FRandom pr_cajump("CustomJump"); //========================================================================== // @@ -1129,13 +1129,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_Jump) { PARAM_ACTION_PROLOGUE(AActor); PARAM_INT(maxchance); + PARAM_STATE_ACTION(jumpto); - paramnum++; // Increment paramnum to point at the first jump target - int count = numparam - paramnum; - if (count > 0 && (maxchance >= 256 || pr_cajump() < maxchance)) + if (maxchance >= 256 || pr_cajump() < maxchance) { - int jumpnum = (count == 1 ? 0 : (pr_cajump() % count)); - PARAM_STATE_ACTION_AT(paramnum + jumpnum, jumpto); ACTION_RETURN_STATE(jumpto); } ACTION_RETURN_STATE(NULL); diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index cda7ec4fa..07a3f1758 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5660,7 +5660,7 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) ExpEmit resultreg(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, resultreg.RegNum); - build->Emit(OP_IJMP, resultreg.RegNum, 0); + build->Emit(OP_IJMP, resultreg.RegNum, choices.Size()); // Free the result register now. The simple code generation algorithm should // automatically pick it as the destination register for each case. @@ -8781,6 +8781,41 @@ VMFunction *FxVMFunctionCall::GetDirectFunction(PFunction *callingfunc, const Ve return nullptr; } +//========================================================================== +// +// FxVMFunctionCall :: UnravelVarArgAJump +// +// Converts A_Jump(chance, a, b, c, d) -> A_Jump(chance, RandomPick[cajump](a, b, c, d)) +// so that varargs are restricted to either text formatting or graphics drawing. +// +//========================================================================== +extern FRandom pr_cajump; + +bool FxVMFunctionCall::UnravelVarArgAJump(FCompileContext &ctx) +{ + FArgumentList rplist; + + for (unsigned i = 1; i < ArgList.Size(); i++) + { + // This needs a bit of casting voodoo because RandomPick wants integer parameters. + auto x = new FxIntCast(new FxTypeCast(ArgList[i], TypeStateLabel, true, true), true, true); + rplist.Push(x->Resolve(ctx)); + ArgList[i] = nullptr; + if (rplist[i - 1] == nullptr) + { + return false; + } + } + FxExpression *x = new FxRandomPick(&pr_cajump, rplist, false, ScriptPosition, true); + x = x->Resolve(ctx); + // This cannot be done with a cast because that interprets the value as an index. + // All we want here is to take the literal value and change its type. + if (x) x->ValueType = TypeStateLabel; + ArgList[1] = x; + ArgList.Clamp(2); + return x != nullptr; +} + //========================================================================== // // FxVMFunctionCall :: Resolve @@ -8813,9 +8848,18 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) return nullptr; } - if (Function->Variants[0].Implementation->PrintableName.CompareNoCase("CustomStatusBar.DrawTexture") == 0) + // Unfortunately the PrintableName is the only safe thing to catch this special case here. + if (Function->Variants[0].Implementation->PrintableName.CompareNoCase("Actor.A_Jump [Native]") == 0) { - int a = 0; + // Unravel the varargs part of this function here so that the VM->native interface does not have to deal with it anymore. + if (ArgList.Size() > 2) + { + auto ret = UnravelVarArgAJump(ctx); + if (!ret) + { + return nullptr; + } + } } CallingFunction = ctx.Function; diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index bf4ac8c7b..9e93ec6f3 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -1764,6 +1764,7 @@ class FxVMFunctionCall : public FxExpression PFunction *CallingFunction; bool CheckAccessibility(const VersionInfo &ver); + bool UnravelVarArgAJump(FCompileContext&); public: FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual); diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index c5426936e..1b9f64fdd 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1062,7 +1062,7 @@ class Actor : Thinker native deprecated("2.3") native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0); deprecated("2.3") native void A_StopSoundEx(name slot); native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10); - native action state A_Jump(int chance, statelabel label, ...); + native action state A_Jump(int chance, statelabel label); native Actor A_SpawnProjectile(class missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET); native void A_CustomBulletAttack(double spread_xy, double spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", double range = 0, int flags = 0, int ptr = AAPTR_TARGET, class missile = null, double Spawnheight = 32, double Spawnofs_xy = 0); native void A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = 0, color color2 = 0, int flags = 0, int aim = 0, double maxdiff = 0, class pufftype = "BulletPuff", double spread_xy = 0, double spread_z = 0, double range = 0, int duration = 0, double sparsity = 1.0, double driftspeed = 1.0, class spawnclass = null, double spawnofs_z = 0, int spiraloffset = 270, int limit = 0, double veleffect = 3);