From 86544086df642e9c29fbc58931db4208caab079d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 30 Nov 2016 17:15:01 +0100 Subject: [PATCH] - allow the VM to run on one global stack per thread. It is utterly pointless to require every function that wants to make a VM call to allocate a new stack first. The allocation overhead doubles the time to set up the call. With one stack, previously allocated memory can be reused. The only important thing is, if this ever gets used in a multithreaded environment to have the stack being declared as thread_local, although for ZDoom this is of no consequence. - eliminated all cases where native code was calling other native code through the VM interface. After scriptifying the game code, only 5 places were left which were quickly eliminated. This was mostly to ensure that the native VM function parameters do not need to be propagated further than absolutely necessary. --- src/actor.h | 2 + src/d_dehacked.cpp | 2 - src/dthinker.cpp | 6 +- src/g_inventory/a_pickups.cpp | 105 ++++------------------- src/g_inventory/a_weapons.cpp | 18 ++-- src/g_shared/a_action.cpp | 30 +++---- src/g_shared/a_bridge.cpp | 20 +++-- src/g_shared/a_fastprojectile.cpp | 3 +- src/info.cpp | 5 +- src/m_cheat.cpp | 3 +- src/p_acs.cpp | 6 +- src/p_actionfunctions.cpp | 7 +- src/p_enemy.cpp | 24 ++---- src/p_enemy.h | 10 +-- src/p_interaction.cpp | 3 +- src/p_mobj.cpp | 91 ++++++++++++++++---- src/p_user.cpp | 15 ++-- src/scripting/codegeneration/codegen.cpp | 12 +-- src/scripting/vm/vm.h | 14 +-- src/scripting/vm/vmexec.cpp | 6 ++ src/scripting/vm/vmframe.cpp | 11 +-- src/scripting/zscript/zcc_compile.cpp | 6 +- wadsrc/static/zscript/actor.txt | 6 +- 23 files changed, 176 insertions(+), 229 deletions(-) diff --git a/src/actor.h b/src/actor.h index 18b128215..6718e0931 100644 --- a/src/actor.h +++ b/src/actor.h @@ -667,6 +667,8 @@ public: // Plays the actor's ActiveSound if its voice isn't already making noise. void PlayActiveSound (); + void RestoreSpecialPosition(); + // Called by PIT_CheckThing() and needed for some Hexen things. // Returns -1 for normal behavior, 0 to return false, and 1 to return true. // I'm not sure I like it this way, but it will do for now. diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index e2c5a6ea3..6f9687aea 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -174,8 +174,6 @@ struct AmmoPerAttack VMFunction *ptr; }; -DECLARE_ACTION(A_Punch) - // Default ammo use of the various weapon attacks static AmmoPerAttack AmmoPerAttacks[] = { { NAME_A_Punch, 0}, diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 7c8bf1bf5..fd20a4e79 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -313,8 +313,7 @@ void DThinker::CallPostBeginPlay() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else { @@ -557,8 +556,7 @@ void DThinker::CallTick() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else Tick(); } diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 237de2ce9..91b88a053 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -22,7 +22,6 @@ #include "virtual.h" #include "a_ammo.h" -static FRandom pr_restore ("RestorePos"); EXTERN_CVAR(Bool, sv_unlimited_pickup) IMPLEMENT_CLASS(PClassInventory, false, false) @@ -137,63 +136,6 @@ DEFINE_ACTION_FUNCTION(AInventory, A_RestoreSpecialDoomThing) return 0; } -//--------------------------------------------------------------------------- -// -// PROP A_RestoreSpecialPosition -// -//--------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) -{ - PARAM_SELF_PROLOGUE(AActor); - - // Move item back to its original location - DVector2 sp = self->SpawnPoint; - - self->UnlinkFromWorld(); - self->SetXY(sp); - self->LinkToWorld(true); - self->SetZ(self->Sector->floorplane.ZatPoint(sp)); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no portal checks here so that things get spawned in this sector. - - if (self->flags & MF_SPAWNCEILING) - { - self->SetZ(self->ceilingz - self->Height - self->SpawnPoint.Z); - } - else if (self->flags2 & MF2_SPAWNFLOAT) - { - double space = self->ceilingz - self->Height - self->floorz; - if (space > 48) - { - space -= 40; - self->SetZ((space * pr_restore()) / 256. + self->floorz + 40); - } - else - { - self->SetZ(self->floorz); - } - } - else - { - self->SetZ(self->SpawnPoint.Z + self->floorz); - } - // Redo floor/ceiling check, in case of 3D floors and portals - P_FindFloorCeiling(self, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); - if (self->Z() < self->floorz) - { // Do not reappear under the floor, even if that's where we were for the - // initial spawn. - self->SetZ(self->floorz); - } - if ((self->flags & MF_SOLID) && (self->Top() > self->ceilingz)) - { // Do the same for the ceiling. - self->SetZ(self->ceilingz - self->Height); - } - // Do not interpolate from the position the actor was at when it was - // picked up, in case that is different from where it is now. - self->ClearInterpolation(); - return 0; -} - int AInventory::StaticLastMessageTic; FString AInventory::StaticLastMessage; @@ -322,10 +264,9 @@ bool AInventory::CallSpecialDropAction(AActor *dropper) { VMValue params[2] = { (DObject*)this, (DObject*)dropper }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return !!retval; } return SpecialDropAction(dropper); @@ -412,7 +353,7 @@ void AInventory::CallDoEffect() { VMValue params[1] = { (DObject*)this }; VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else DoEffect(); } @@ -495,10 +436,9 @@ bool AInventory::CallHandlePickup(AInventory *item) // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)self, (DObject*)item }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); if (retval) return true; } else if (self->HandlePickup(item)) return true; @@ -604,10 +544,9 @@ AInventory *AInventory::CallCreateCopy(AActor *other) { VMValue params[2] = { (DObject*)this, (DObject*)other }; VMReturn ret; - VMFrameStack stack; AInventory *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return retval; } else return CreateCopy(other); @@ -670,10 +609,9 @@ AInventory *AInventory::CallCreateTossable() { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; AInventory *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } else return CreateTossable(); @@ -805,10 +743,9 @@ double AInventory::GetSpeedFactor() { VMValue params[2] = { (DObject*)self }; VMReturn ret; - VMFrameStack stack; double retval; ret.FloatAt(&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); factor *= retval; } self = self->Inventory; @@ -831,10 +768,9 @@ bool AInventory::GetNoTeleportFreeze () { VMValue params[2] = { (DObject*)self }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); if (retval) return true; } self = self->Inventory; @@ -884,10 +820,9 @@ bool AInventory::CallUse(bool pickup) { VMValue params[2] = { (DObject*)this, pickup }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return !!retval; } @@ -1090,10 +1025,9 @@ FString AInventory::GetPickupMessage() { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; FString retval; ret.StringAt(&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } else return PickupMessage(); @@ -1150,8 +1084,7 @@ void AInventory::CallPlayPickupSound(AActor *other) IFVIRTUAL(AInventory, PlayPickupSound) { VMValue params[2] = { (DObject*)this, (DObject*)other }; - VMFrameStack stack; - stack.Call(func, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } else PlayPickupSound(other); } @@ -1182,10 +1115,9 @@ bool AInventory::CallShouldStay() { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return !!retval; } else return ShouldStay(); @@ -1264,10 +1196,9 @@ PalEntry AInventory::CallGetBlend() { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } else return GetBlend(); @@ -1521,10 +1452,9 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) { VMValue params[2] = { (DObject*)this, (void*)&toucher }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); res = !!retval; } else res = TryPickup(toucher); @@ -1536,10 +1466,9 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) { VMValue params[2] = { (DObject*)this, (void*)&toucher }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); res = !!retval; } else res = TryPickupRestricted(toucher); @@ -1687,8 +1616,7 @@ void AInventory::CallAttachToOwner(AActor *other) IFVIRTUAL(AInventory, AttachToOwner) { VMValue params[2] = { (DObject*)this, (DObject*)other }; - VMFrameStack stack; - stack.Call(func, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } else AttachToOwner(other); } @@ -1719,8 +1647,7 @@ void AInventory::CallDetachFromOwner() IFVIRTUAL(AInventory, DetachFromOwner) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else DetachFromOwner(); } diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 3fd4bc939..19a30dcd8 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -845,8 +845,7 @@ void AWeapon::CallEndPowerup() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else EndPowerup(); } @@ -864,10 +863,9 @@ FState *AWeapon::GetUpState () { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; FState *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } return nullptr; @@ -885,10 +883,9 @@ FState *AWeapon::GetDownState () { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; FState *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } return nullptr; @@ -906,10 +903,9 @@ FState *AWeapon::GetReadyState () { VMValue params[1] = { (DObject*)this }; VMReturn ret; - VMFrameStack stack; FState *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return retval; } return nullptr; @@ -927,10 +923,9 @@ FState *AWeapon::GetAtkState (bool hold) { VMValue params[2] = { (DObject*)this, hold }; VMReturn ret; - VMFrameStack stack; FState *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return retval; } return nullptr; @@ -948,10 +943,9 @@ FState *AWeapon::GetAltAtkState (bool hold) { VMValue params[2] = { (DObject*)this, hold }; VMReturn ret; - VMFrameStack stack; FState *retval; ret.PointerAt((void**)&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return retval; } return nullptr; diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index afe188019..2e4597617 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -121,35 +121,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeath) return 0; } -//========================================================================== -// -// A_GenericFreezeDeath -// -//========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_GenericFreezeDeath) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->Translation = TRANSLATION(TRANSLATION_Standard, 7); - CALL_ACTION(A_FreezeDeath, self); - return 0; -} - //============================================================================ // // A_IceSetTics // //============================================================================ -DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics) +void IceSetTics(AActor *self) { - PARAM_SELF_PROLOGUE(AActor); int floor; - self->tics = 70+(pr_icesettics()&63); - floor = P_GetThingFloorType (self); + self->tics = 70 + (pr_icesettics() & 63); + floor = P_GetThingFloorType(self); if (Terrains[floor].DamageMOD == NAME_Fire) { self->tics >>= 2; @@ -158,6 +142,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics) { self->tics <<= 1; } +} + +DEFINE_ACTION_FUNCTION(AActor, A_IceSetTics) +{ + PARAM_SELF_PROLOGUE(AActor); + IceSetTics(self); return 0; } @@ -203,7 +193,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeathChunks) mo->Vel.X = pr_freeze.Random2() / 128.; mo->Vel.Y = pr_freeze.Random2() / 128.; mo->Vel.Z = (mo->Z() - self->Z()) / self->Height * 4; - CALL_ACTION(A_IceSetTics, mo); // set a random tic wait + IceSetTics(mo); // set a random tic wait mo->RenderStyle = self->RenderStyle; mo->Alpha = self->Alpha; } diff --git a/src/g_shared/a_bridge.cpp b/src/g_shared/a_bridge.cpp index 556767f37..138210061 100644 --- a/src/g_shared/a_bridge.cpp +++ b/src/g_shared/a_bridge.cpp @@ -89,24 +89,22 @@ void ACustomBridge::Destroy() // target pointer to center mobj // angle angle of ball -DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) +static void BridgeOrbit(AActor *self) { - PARAM_SELF_PROLOGUE(AActor); - if (self->target == NULL) { // Don't crash if somebody spawned this into the world // independantly of a Bridge actor. - return 0; + return; } // Set default values // Every five tics, Hexen moved the ball 3/256th of a revolution. - DAngle rotationspeed = 45./32*3/5; + DAngle rotationspeed = 45. / 32 * 3 / 5; double rotationradius = ORBIT_RADIUS; // If the bridge is custom, set non-default values if any. // Set angular speed; 1--128: counterclockwise rotation ~=1--180°; 129--255: clockwise rotation ~= 180--1° - if (self->target->args[3] > 128) rotationspeed = 45./32 * (self->target->args[3]-256) / TICRATE; - else if (self->target->args[3] > 0) rotationspeed = 45./32 * (self->target->args[3]) / TICRATE; + if (self->target->args[3] > 128) rotationspeed = 45. / 32 * (self->target->args[3] - 256) / TICRATE; + else if (self->target->args[3] > 0) rotationspeed = 45. / 32 * (self->target->args[3]) / TICRATE; // Set rotation radius if (self->target->args[4]) rotationradius = ((self->target->args[4] * self->target->radius) / 100); @@ -114,6 +112,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) self->SetOrigin(self->target->Vec3Angle(rotationradius, self->Angles.Yaw, 0), true); self->floorz = self->target->floorz; self->ceilingz = self->target->ceilingz; +} + +DEFINE_ACTION_FUNCTION(AActor, A_BridgeOrbit) +{ + PARAM_SELF_PROLOGUE(AActor); + BridgeOrbit(self); return 0; } @@ -140,7 +144,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BridgeInit) ball = Spawn(balltype, self->Pos(), ALLOW_REPLACE); ball->Angles.Yaw = startangle + (45./32) * (256/ballcount) * i; ball->target = self; - CALL_ACTION(A_BridgeOrbit, ball); + BridgeOrbit(ball); } return 0; } diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index c68fa800c..fb0faef0f 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -139,8 +139,7 @@ void AFastProjectile::Tick () { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } } diff --git a/src/info.cpp b/src/info.cpp index 69605d454..b8bf3acde 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -75,7 +75,6 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, { ActionCycles.Clock(); - VMFrameStack stack; VMValue params[3] = { self, stateowner, VMValue(info, ATAG_GENERIC) }; // If the function returns a state, store it at *stateret. // If it doesn't return a state but stateret is non-NULL, we need @@ -92,13 +91,13 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, } if (stateret == NULL) { - stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); } else { VMReturn ret; ret.PointerAt((void **)stateret); - stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); } ActionCycles.Unclock(); return true; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 53405017a..53ae789c6 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -489,11 +489,10 @@ void cht_DoCheat (player_t *player, int cheat) if (gsp) { VMValue params[1] = { player->mo }; - VMFrameStack stack; VMReturn ret; int oldpieces = 1; ret.IntAt(&oldpieces); - stack.Call(gsp, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(gsp, params, 1, &ret, 1, nullptr); item = player->mo->FindInventory(PClass::FindActor(NAME_Sigil)); if (item != NULL) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index ba086da3a..6a7c9d10c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -6116,8 +6116,7 @@ static void SetMarineWeapon(AActor *marine, int weapon) if (smw) { VMValue params[2] = { marine, weapon }; - VMFrameStack stack; - stack.Call(smw, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(smw, params, 2, nullptr, 0, nullptr); } } @@ -6128,8 +6127,7 @@ static void SetMarineSprite(AActor *marine, PClassActor *source) if (sms) { VMValue params[2] = { marine, source }; - VMFrameStack stack; - stack.Call(sms, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(sms, params, 2, nullptr, 0, nullptr); } } diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index bc217f4ae..477ed37f2 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -148,7 +148,6 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) state->ActionFunc = nullptr; } - VMFrameStack stack; PPrototype *proto = state->ActionFunc->Proto; VMReturn *wantret; FStateParamInfo stp = { state, STATE_StateChain, PSP_WEAPON }; @@ -184,7 +183,7 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) numret = 2; } } - stack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); + GlobalVMStack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); // As long as even one state succeeds, the whole chain succeeds unless aborted below. // A state that wants to jump does not count as "succeeded". if (nextstate == NULL) @@ -3802,8 +3801,6 @@ static void CheckStopped(AActor *self) // //=========================================================================== -DECLARE_ACTION(A_RestoreSpecialPosition) - enum RS_Flags { RSF_FOG=1, @@ -3822,7 +3819,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Respawn) self->flags |= MF_SOLID; self->Height = self->GetDefault()->Height; self->radius = self->GetDefault()->radius; - CALL_ACTION(A_RestoreSpecialPosition, self); + self->RestoreSpecialPosition(); if (flags & RSF_TELEFRAG) { diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 29128c8b7..751f92a30 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2285,12 +2285,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_Wander) return 0; } -// [MC] I had to move this out from within A_Wander in order to allow flags to -// pass into it. That meant replacing the CALL_ACTION(A_Wander) functions with -// just straight up defining A_Wander in order to compile. Looking around though, -// actors from the games themselves just do a straight A_Chase call itself so -// I saw no harm in it. - void A_Wander(AActor *self, int flags) { // [RH] Strife probably clears this flag somewhere, but I couldn't find where. @@ -2403,7 +2397,7 @@ nosee: //============================================================================= #define CLASS_BOSS_STRAFE_RANGE 64*10 -void A_DoChase (VMFrameStack *stack, AActor *actor, bool fastchase, FState *meleestate, FState *missilestate, bool playactive, bool nightmarefast, bool dontmove, int flags) +void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missilestate, bool playactive, bool nightmarefast, bool dontmove, int flags) { if (actor->flags5 & MF5_INCONVERSATION) @@ -2533,7 +2527,7 @@ void A_DoChase (VMFrameStack *stack, AActor *actor, bool fastchase, FState *mele { if (actor->flags & MF_FRIENDLY) { - //CALL_ACTION(A_Look, actor); + //A_Look(actor); if (actor->target == NULL) { if (!dontmove) A_Wander(actor); @@ -2931,12 +2925,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_Chase) if ((flags & CHF_RESURRECT) && P_CheckForResurrection(self, false)) return 0; - A_DoChase(stack, self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), + A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), !!(flags&CHF_NIGHTMAREFAST), !!(flags&CHF_DONTMOVE), flags); } else // this is the old default A_Chase { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, flags); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, flags); } return 0; } @@ -2944,7 +2938,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Chase) DEFINE_ACTION_FUNCTION(AActor, A_FastChase) { PARAM_SELF_PROLOGUE(AActor); - A_DoChase(stack, self, true, self->MeleeState, self->MissileState, true, true, false, 0); + A_DoChase(self, true, self->MeleeState, self->MissileState, true, true, false, 0); return 0; } @@ -2953,7 +2947,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_VileChase) PARAM_SELF_PROLOGUE(AActor); if (!P_CheckForResurrection(self, true)) { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } return 0; } @@ -2967,16 +2961,16 @@ DEFINE_ACTION_FUNCTION(AActor, A_ExtChase) PARAM_BOOL_DEF (nightmarefast); // Now that A_Chase can handle state label parameters, this function has become rather useless... - A_DoChase(stack, self, false, + A_DoChase(self, false, domelee ? self->MeleeState : NULL, domissile ? self->MissileState : NULL, playactive, nightmarefast, false, 0); return 0; } // for internal use -void A_Chase(VMFrameStack *stack, AActor *self) +void A_Chase(AActor *self) { - A_DoChase(stack, self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); + A_DoChase(self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false, 0); } //============================================================================= diff --git a/src/p_enemy.h b/src/p_enemy.h index a9aa40dc2..a0e829c71 100644 --- a/src/p_enemy.h +++ b/src/p_enemy.h @@ -60,18 +60,10 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params); void A_Weave(AActor *self, int xyspeed, int zspeed, double xydist, double zdist); void A_Unblock(AActor *self, bool drop); -DECLARE_ACTION(A_Look) -DECLARE_ACTION(A_BossDeath) -DECLARE_ACTION(A_Pain) -DECLARE_ACTION(A_MonsterRail) -DECLARE_ACTION(A_NoBlocking) -DECLARE_ACTION(A_Scream) -DECLARE_ACTION(A_FreezeDeath) -DECLARE_ACTION(A_FreezeDeathChunks) void A_BossDeath(AActor *self); void A_Wander(AActor *self, int flags = 0); -void A_Chase(VMFrameStack *stack, AActor *self); +void A_Chase(AActor *self); void A_FaceTarget(AActor *actor); void A_Face(AActor *self, AActor *other, DAngle max_turn = 0., DAngle max_pitch = 270., DAngle ang_offset = 0., DAngle pitch_offset = 0., int flags = 0, double z_add = 0); diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index eae7edb00..e6729096c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -777,8 +777,7 @@ void AActor::CallDie(AActor *source, AActor *inflictor, int dmgflags) IFVIRTUAL(AActor, Die) { VMValue params[4] = { (DObject*)this, source, inflictor, dmgflags }; - VMFrameStack stack; - stack.Call(func, params, 4, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 4, nullptr, 0, nullptr); } else return Die(source, inflictor, dmgflags); } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 572c7656f..f1f33425a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1494,8 +1494,7 @@ void AActor::CallTouch(AActor *toucher) IFVIRTUAL(AActor, Touch) { VMValue params[2] = { (DObject*)this, toucher }; - VMFrameStack stack; - stack.Call(func, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } else Touch(toucher); } @@ -3360,7 +3359,6 @@ int AActor::GetMissileDamage (int mask, int add) assert(false && "No damage function found"); return 0; } - VMFrameStack stack; VMValue param = this; VMReturn result; @@ -3368,7 +3366,7 @@ int AActor::GetMissileDamage (int mask, int add) result.IntAt(&amount); - if (stack.Call(DamageFunc, ¶m, 1, &result, 1) < 1) + if (GlobalVMStack.Call(DamageFunc, ¶m, 1, &result, 1) < 1) { // No results return 0; } @@ -3431,10 +3429,9 @@ bool AActor::CallSlam(AActor *thing) { VMValue params[2] = { (DObject*)this, thing }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return !!retval; } @@ -3451,9 +3448,8 @@ int AActor::SpecialMissileHit (AActor *victim) VMValue params[2] = { (DObject*)this, victim }; VMReturn ret; int retval; - VMFrameStack stack; ret.IntAt(&retval); - stack.Call(func, params, 2, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return retval; } else return -1; @@ -4777,8 +4773,7 @@ void AActor::CallBeginPlay() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } else BeginPlay(); } @@ -4859,8 +4854,7 @@ void AActor::CallActivate(AActor *activator) { // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; - VMFrameStack stack; - stack.Call(func, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } else Activate(activator); } @@ -4906,8 +4900,7 @@ void AActor::CallDeactivate(AActor *activator) { // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; - VMFrameStack stack; - stack.Call(func, params, 2, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } else Deactivate(activator); } @@ -7076,10 +7069,9 @@ int AActor::CallDoSpecialDamage(AActor *target, int damage, FName damagetype) // Without the type cast this picks the 'void *' assignment... VMValue params[4] = { (DObject*)this, (DObject*)target, damage, damagetype.GetIndex() }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 4, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr); return retval; } else return DoSpecialDamage(target, damage, damagetype); @@ -7142,10 +7134,9 @@ int AActor::CallTakeSpecialDamage(AActor *inflictor, AActor *source, int damage, { VMValue params[5] = { (DObject*)this, inflictor, source, damage, damagetype.GetIndex() }; VMReturn ret; - VMFrameStack stack; int retval; ret.IntAt(&retval); - stack.Call(func, params, 5, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, 5, &ret, 1, nullptr); return retval; } else return TakeSpecialDamage(inflictor, source, damage, damagetype); @@ -7468,6 +7459,70 @@ void AActor::SetTranslation(FName trname) // silently ignore if the name does not exist, this would create some insane message spam otherwise. } +//--------------------------------------------------------------------------- +// +// PROP A_RestoreSpecialPosition +// +//--------------------------------------------------------------------------- +static FRandom pr_restore("RestorePos"); + +void AActor::RestoreSpecialPosition() +{ + // Move item back to its original location + DVector2 sp = SpawnPoint; + + UnlinkFromWorld(); + SetXY(sp); + LinkToWorld(true); + SetZ(Sector->floorplane.ZatPoint(sp)); + P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS); // no portal checks here so that things get spawned in this sector. + + if (flags & MF_SPAWNCEILING) + { + SetZ(ceilingz - Height - SpawnPoint.Z); + } + else if (flags2 & MF2_SPAWNFLOAT) + { + double space = ceilingz - Height - floorz; + if (space > 48) + { + space -= 40; + SetZ((space * pr_restore()) / 256. + floorz + 40); + } + else + { + SetZ(floorz); + } + } + else + { + SetZ(SpawnPoint.Z + floorz); + } + // Redo floor/ceiling check, in case of 3D floors and portals + P_FindFloorCeiling(this, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT); + if (Z() < floorz) + { // Do not reappear under the floor, even if that's where we were for the + // initial spawn. + SetZ(floorz); + } + if ((flags & MF_SOLID) && (Top() > ceilingz)) + { // Do the same for the ceiling. + SetZ(ceilingz - Height); + } + // Do not interpolate from the position the actor was at when it was + // picked up, in case that is different from where it is now. + ClearInterpolation(); +} + +DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition) +{ + PARAM_SELF_PROLOGUE(AActor); + self->RestoreSpecialPosition(); + return 0; +} + + + class DActorIterator : public DObject, public NActorIterator { diff --git a/src/p_user.cpp b/src/p_user.cpp index 11857631a..74b07e0ca 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1305,8 +1305,7 @@ void APlayerPawn::PlayIdle () IFVIRTUAL(APlayerPawn, PlayIdle) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } @@ -1315,8 +1314,7 @@ void APlayerPawn::PlayRunning () IFVIRTUAL(APlayerPawn, PlayRunning) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } @@ -1325,8 +1323,7 @@ void APlayerPawn::PlayAttacking () IFVIRTUAL(APlayerPawn, PlayAttacking) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } @@ -1335,8 +1332,7 @@ void APlayerPawn::PlayAttacking2 () IFVIRTUAL(APlayerPawn, PlayAttacking2) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } @@ -1426,8 +1422,7 @@ void APlayerPawn::MorphPlayerThink () IFVIRTUAL(APlayerPawn, MorphPlayerThink) { VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - stack.Call(func, params, 1, nullptr, 0, nullptr); + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index eaeca6e9d..28b20e289 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -4228,7 +4228,7 @@ PPrototype *FxTypeCheck::ReturnProto() // //========================================================================== -int BuiltinTypeCheck(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinTypeCheck(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 2); PARAM_POINTER_AT(0, obj, DObject); @@ -5030,7 +5030,7 @@ FxExpression *FxRandom::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinRandom(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinRandom(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam >= 1 && numparam <= 3); FRandom *rng = reinterpret_cast(param[0].a); @@ -5284,7 +5284,7 @@ FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScri // //========================================================================== -int BuiltinFRandom(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinFRandom(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 1 || numparam == 3); FRandom *rng = reinterpret_cast(param[0].a); @@ -7558,7 +7558,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) // //========================================================================== -int BuiltinCallLineSpecial(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinCallLineSpecial(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam > 2 && numparam < 8); assert(param[0].Type == REGT_INT); @@ -9518,7 +9518,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinNameToClass(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinNameToClass(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { assert(numparam == 2); assert(numret == 1); @@ -9650,7 +9650,7 @@ FxExpression *FxClassPtrCast::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinClassCast(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +int BuiltinClassCast(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) { PARAM_PROLOGUE; PARAM_CLASS(from, DObject); diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index fdee430e1..9bd0a4bc9 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -867,7 +867,7 @@ class VMNativeFunction : public VMFunction { DECLARE_CLASS(VMNativeFunction, VMFunction); public: - typedef int (*NativeCallType)(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); + typedef int (*NativeCallType)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); VMNativeFunction() : NativeCall(NULL) { Native = true; } VMNativeFunction(NativeCallType call) : NativeCall(call) { Native = true; } @@ -930,6 +930,9 @@ enum EVMEngine VMEngine_Checked }; +extern thread_local VMFrameStack GlobalVMStack; + + void VMSelectEngine(EVMEngine engine); extern int (*VMExec)(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret); void VMFillParams(VMValue *params, VMFrame *callee, int numparam); @@ -938,8 +941,8 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func); void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func); // Use this in the prototype for a native function. -#define VM_ARGS VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret -#define VM_ARGS_NAMES stack, param, defaultparam, numparam, ret, numret +#define VM_ARGS VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret +#define VM_ARGS_NAMES param, defaultparam, numparam, ret, numret // Use these to collect the parameters in a native function. // variable name at position

@@ -1012,7 +1015,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction #define PARAM_OBJECT_DEF(x,type) ++paramnum; PARAM_OBJECT_DEF_AT(paramnum,x,type) #define PARAM_CLASS_DEF(x,base) ++paramnum; PARAM_CLASS_DEF_AT(paramnum,x,base) -typedef int(*actionf_p)(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ +typedef int(*actionf_p)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ struct FieldDesc { @@ -1048,7 +1051,6 @@ struct AFuncDesc // Macros to handle action functions. These are here so that I don't have to // change every single use in case the parameters change. -#define DECLARE_ACTION(name) extern VMNativeFunction *AActor_##name##_VMPtr; #define DEFINE_ACTION_FUNCTION(cls, name) \ static int AF_##cls##_##name(VM_ARGS); \ @@ -1090,8 +1092,6 @@ struct AFuncDesc MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname; class AActor; -void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self); -#define CALL_ACTION(name, self) CallAction(stack, AActor_##name##_VMPtr, self); #define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0; } while(0) diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 750c384e0..f51038f36 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -146,6 +146,12 @@ VMExec_Checked::Exec #endif ; +// Note: If the VM is being used in multiple threads, this should be declared as thread_local. +// ZDoom doesn't need this at the moment so this is disabled. + +thread_local VMFrameStack GlobalVMStack; + + //=========================================================================== // // VMSelectEngine diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 428266d0f..d3ce5d5f9 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -422,12 +422,13 @@ VMFrame *VMFrameStack::PopFrame() int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap) { + assert(this == VMGlobalStack); // why would anyone even want to create a local stack? bool allocated = false; try { if (func->Native) { - return static_cast(func)->NativeCall(this, params, func->DefaultArgs, numparams, results, numresults); + return static_cast(func)->NativeCall(params, func->DefaultArgs, numparams, results, numresults); } else { @@ -503,11 +504,3 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur throw; } } - -class AActor; -void CallAction(VMFrameStack *stack, VMFunction *vmfunc, AActor *self) -{ - // Without the type cast this picks the 'void *' assignment... - VMValue params[3] = { (DObject*)self, (DObject*)self, VMValue(nullptr, ATAG_GENERIC) }; - stack->Call(vmfunc, params, vmfunc->ImplicitArgs, nullptr, 0, nullptr); -} diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ae666a2f7..2b86e58e1 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2362,7 +2362,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { if (vindex != -1) { - Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars()); + Error(f, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars()); } sym->Variants[0].Implementation->VirtualIndex = clstype->Virtuals.Push(sym->Variants[0].Implementation); } @@ -3267,3 +3267,7 @@ FArgumentList &ZCCCompiler::ConvertNodeList(FArgumentList &args, ZCC_TreeNode *h } return args; } + +void func() +{ +} \ No newline at end of file diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index abcd58b50..bd00ffc98 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -666,7 +666,11 @@ class Actor : Thinker native native void A_FastChase(); native void A_FreezeDeath(); native void A_FreezeDeathChunks(); - native void A_GenericFreezeDeath(); + void A_GenericFreezeDeath() + { + A_SetTranslation('Ice'); + A_FreezeDeath(); + } native void A_PlayerScream(); native void A_SkullPop(class skulltype = "BloodySkull"); native void A_CheckPlayerDone();