From 76a74e0364fe12171ad7ae307f54161f777faa69 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 16 Nov 2016 19:18:21 +0100 Subject: [PATCH] - scriptified Hexen's Dragon. - fixed several places in the code generator that did not consider locked registers for local variables: array indices, abs and floating point builtin functions. - added some debug aids to the bounds opcode. Just triggering an exception here which loses all relevant info is perfectly useless in a debug situation. --- src/g_hexen/a_dragon.cpp | 311 ----------------------- src/g_hexen/a_hexenmisc.cpp | 1 - src/scripting/codegeneration/codegen.cpp | 78 ++++-- src/scripting/vm/vmexec.h | 2 + wadsrc/static/zscript/hexen/dragon.txt | 282 +++++++++++++++++++- 5 files changed, 327 insertions(+), 347 deletions(-) delete mode 100644 src/g_hexen/a_dragon.cpp diff --git a/src/g_hexen/a_dragon.cpp b/src/g_hexen/a_dragon.cpp deleted file mode 100644 index b79d92b36..000000000 --- a/src/g_hexen/a_dragon.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* -#include "actor.h" -#include "info.h" -#include "p_enemy.h" -#include "p_local.h" -#include "a_action.h" -#include "m_random.h" -#include "s_sound.h" -#include "vm.h" -*/ - -static FRandom pr_dragonseek ("DragonSeek"); -static FRandom pr_dragonflight ("DragonFlight"); -static FRandom pr_dragonflap ("DragonFlap"); -static FRandom pr_dragonfx2 ("DragonFX2"); - -DECLARE_ACTION(A_DragonFlight) - -//============================================================================ -// -// DragonSeek -// -//============================================================================ - -static void DragonSeek (AActor *actor, DAngle thresh, DAngle turnMax) -{ - int dir; - double dist; - DAngle delta; - AActor *target; - int i; - DAngle bestAngle; - DAngle angleToSpot, angleToTarget; - AActor *mo; - - target = actor->tracer; - if(target == NULL) - { - return; - } - dir = P_FaceMobj (actor, target, &delta); - if (delta > thresh) - { - delta /= 2; - if (delta > turnMax) - { - delta = turnMax; - } - } - if (dir) - { // Turn clockwise - actor->Angles.Yaw += delta; - } - else - { // Turn counter clockwise - actor->Angles.Yaw -= delta; - } - actor->VelFromAngle(); - - dist = actor->DistanceBySpeed(target, actor->Speed); - if (actor->Top() < target->Z() || - target->Top() < actor->Z()) - { - actor->Vel.Z = (target->Z() - actor->Z()) / dist; - } - if (target->flags&MF_SHOOTABLE && pr_dragonseek() < 64) - { // attack the destination mobj if it's attackable - AActor *oldTarget; - - if (absangle(actor->Angles.Yaw, actor->AngleTo(target)) < 22.5) - { - oldTarget = actor->target; - actor->target = target; - if (actor->CheckMeleeRange ()) - { - int damage = pr_dragonseek.HitDice (10); - int newdam = P_DamageMobj (actor->target, actor, actor, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, actor->target, actor); - S_Sound (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM); - } - else if (pr_dragonseek() < 128 && P_CheckMissileRange(actor)) - { - P_SpawnMissile(actor, target, PClass::FindActor("DragonFireball")); - S_Sound (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM); - } - actor->target = oldTarget; - } - } - if (dist < 4) - { // Hit the target thing - if (actor->target && pr_dragonseek() < 200) - { - AActor *bestActor = NULL; - bestAngle = 360.; - angleToTarget = actor->AngleTo(actor->target); - for (i = 0; i < 5; i++) - { - if (!target->args[i]) - { - continue; - } - FActorIterator iterator (target->args[i]); - mo = iterator.Next (); - if (mo == NULL) - { - continue; - } - angleToSpot = actor->AngleTo(mo); - DAngle diff = absangle(angleToSpot, angleToTarget); - if (diff < bestAngle) - { - bestAngle = diff; - bestActor = mo; - } - } - if (bestActor != NULL) - { - actor->tracer = bestActor; - } - } - else - { - // [RH] Don't lock up if the dragon doesn't have any - // targets defined - for (i = 0; i < 5; ++i) - { - if (target->args[i] != 0) - { - break; - } - } - if (i < 5) - { - do - { - i = (pr_dragonseek()>>2)%5; - } while(!target->args[i]); - FActorIterator iterator (target->args[i]); - actor->tracer = iterator.Next (); - } - } - } -} - -//============================================================================ -// -// A_DragonInitFlight -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonInitFlight) -{ - PARAM_SELF_PROLOGUE(AActor); - - FActorIterator iterator (self->tid); - - do - { // find the first tid identical to the dragon's tid - self->tracer = iterator.Next (); - if (self->tracer == NULL) - { - self->SetState (self->SpawnState); - return 0; - } - } while (self->tracer == self); - self->RemoveFromHash (); - return 0; -} - -//============================================================================ -// -// A_DragonFlight -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonFlight) -{ - PARAM_SELF_PROLOGUE(AActor); - - DAngle angle; - - DragonSeek (self, 4., 8.); - if (self->target) - { - if(!(self->target->flags&MF_SHOOTABLE)) - { // target died - self->target = NULL; - return 0; - } - angle = absangle(self->Angles.Yaw, self->AngleTo(self->target)); - if (angle <22.5 && self->CheckMeleeRange()) - { - int damage = pr_dragonflight.HitDice (8); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - } - else if (angle <= 20) - { - self->SetState (self->MissileState); - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - } - } - else - { - P_LookForPlayers (self, true, NULL); - } - return 0; -} - -//============================================================================ -// -// A_DragonFlap -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonFlap) -{ - PARAM_SELF_PROLOGUE(AActor); - - CALL_ACTION(A_DragonFlight, self); - if (pr_dragonflap() < 240) - { - S_Sound (self, CHAN_BODY, "DragonWingflap", 1, ATTN_NORM); - } - else - { - self->PlayActiveSound (); - } - return 0; -} - -//============================================================================ -// -// A_DragonAttack -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - P_SpawnMissile (self, self->target, PClass::FindActor("DragonFireball")); - return 0; -} - -//============================================================================ -// -// A_DragonFX2 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonFX2) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - int i; - int delay; - - delay = 16+(pr_dragonfx2()>>3); - for (i = 1+(pr_dragonfx2()&3); i; i--) - { - double xo = (pr_dragonfx2() - 128) / 4.; - double yo = (pr_dragonfx2() - 128) / 4.; - double zo = (pr_dragonfx2() - 128) / 16.; - - mo = Spawn ("DragonExplosion", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE); - if (mo) - { - mo->tics = delay+(pr_dragonfx2()&3)*i*2; - mo->target = self->target; - } - } - return 0; -} - -//============================================================================ -// -// A_DragonPain -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonPain) -{ - PARAM_SELF_PROLOGUE(AActor); - - CALL_ACTION(A_Pain, self); - if (!self->tracer) - { // no destination spot yet - self->SetState (self->SeeState); - } - return 0; -} - -//============================================================================ -// -// A_DragonCheckCrash -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_DragonCheckCrash) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->Z() <= self->floorz) - { - self->SetState (self->FindState ("Crash")); - } - return 0; -} diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 1a635ff71..f1f4a86fe 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -33,7 +33,6 @@ #include "a_clericholy.cpp" #include "a_clericmace.cpp" #include "a_clericstaff.cpp" -#include "a_dragon.cpp" #include "a_fighteraxe.cpp" #include "a_fighterhammer.cpp" #include "a_fighterplayer.cpp" diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index e33986550..44d7cda88 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -1670,19 +1670,23 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build) { assert(ValueType == Operand->ValueType); ExpEmit from = Operand->Emit(build); + ExpEmit to; assert(from.Konst == 0); assert(ValueType->GetRegCount() == from.RegCount); // Do it in-place, unless a local variable if (from.Fixed) { - ExpEmit to = ExpEmit(build, from.RegType, from.RegCount); - build->Emit(Operand->ValueType->GetMoveOp(), to.RegNum, from.RegNum); - from = to; + to = ExpEmit(build, from.RegType, from.RegCount); + from.Free(build); + } + else + { + to = from; } if (ValueType->GetRegType() == REGT_INT) { - build->Emit(OP_NEG, from.RegNum, from.RegNum, 0); + build->Emit(OP_NEG, to.RegNum, from.RegNum, 0); } else { @@ -1690,20 +1694,20 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build) switch (from.RegCount) { case 1: - build->Emit(OP_FLOP, from.RegNum, from.RegNum, FLOP_NEG); + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FLOP_NEG); break; case 2: - build->Emit(OP_NEGV2, from.RegNum, from.RegNum); + build->Emit(OP_NEGV2, to.RegNum, from.RegNum); break; case 3: - build->Emit(OP_NEGV3, from.RegNum, from.RegNum); + build->Emit(OP_NEGV3, to.RegNum, from.RegNum); break; } } - return from; + return to; } //========================================================================== @@ -4255,19 +4259,31 @@ FxExpression *FxAbs::Resolve(FCompileContext &ctx) ExpEmit FxAbs::Emit(VMFunctionBuilder *build) { - ExpEmit absofsteal = val->Emit(build); - assert(!absofsteal.Konst); - ExpEmit out(build, absofsteal.RegType); - if (absofsteal.RegType == REGT_INT) + assert(ValueType == val->ValueType); + ExpEmit from = val->Emit(build); + ExpEmit to; + assert(from.Konst == 0); + assert(ValueType->GetRegCount() == 1); + // Do it in-place, unless a local variable + if (from.Fixed) { - build->Emit(OP_ABS, out.RegNum, absofsteal.RegNum, 0); + to = ExpEmit(build, from.RegType); + from.Free(build); } else { - assert(absofsteal.RegType == REGT_FLOAT); - build->Emit(OP_FLOP, out.RegNum, absofsteal.RegNum, FLOP_ABS); + to = from; } - return out; + + if (ValueType->GetRegType() == REGT_INT) + { + build->Emit(OP_ABS, to.RegNum, from.RegNum, 0); + } + else + { + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FLOP_ABS); + } + return to; } //========================================================================== @@ -5898,6 +5914,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) else { ExpEmit indexv(index->Emit(build)); + ExpEmit indexwork = indexv.Fixed ? ExpEmit(build, indexv.RegType) : indexv; int shiftbits = 0; while (1u << shiftbits < arraytype->ElementSize) { @@ -5907,19 +5924,19 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount); if (shiftbits > 0) { - build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); + build->Emit(OP_SLL_RI, indexwork.RegNum, indexv.RegNum, shiftbits); } if (AddressRequested) { - build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexv.RegNum); + build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexwork.RegNum); } else { build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that - dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + dest.RegNum, start.RegNum, indexwork.RegNum); // takes the offset from a register } - indexv.Free(build); + indexwork.Free(build); } if (AddressRequested) { @@ -6955,13 +6972,26 @@ FxExpression *FxFlopFunctionCall::Resolve(FCompileContext& ctx) ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build) { - ExpEmit v = ArgList[0]->Emit(build); - assert(!v.Konst && v.RegType == REGT_FLOAT); + assert(ValueType == ArgList[0]->ValueType); + ExpEmit from = ArgList[0]->Emit(build); + ExpEmit to; + assert(from.Konst == 0); + assert(ValueType->GetRegCount() == 1); + // Do it in-place, unless a local variable + if (from.Fixed) + { + to = ExpEmit(build, from.RegType); + from.Free(build); + } + else + { + to = from; + } - build->Emit(OP_FLOP, v.RegNum, v.RegNum, FxFlops[Index].Flop); + build->Emit(OP_FLOP, to.RegNum, from.RegNum, FxFlops[Index].Flop); ArgList.Clear(); ArgList.ShrinkToFit(); - return v; + return to; } diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 02e0db3de..fcdb2eb8b 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -696,6 +696,8 @@ begin: OP(BOUND): if (reg.d[a] >= BC) { + assert(false); + Printf("Array access out of bounds: Max. index = %u, current index = %u\n", BC, reg.d[a]); THROW(X_ARRAY_OUT_OF_BOUNDS); } NEXTOP; diff --git a/wadsrc/static/zscript/hexen/dragon.txt b/wadsrc/static/zscript/hexen/dragon.txt index 6df3da943..ed6e86d4a 100644 --- a/wadsrc/static/zscript/hexen/dragon.txt +++ b/wadsrc/static/zscript/hexen/dragon.txt @@ -1,6 +1,4 @@ - // Dragon ------------------------------------------------------------------- - class Dragon : Actor { Default @@ -23,13 +21,6 @@ class Dragon : Actor Obituary "$OB_DRAGON"; } - native void A_DragonInitFlight(); - native void A_DragonFlap(); - native void A_DragonFlight(); - native void A_DragonPain(); - native void A_DragonAttack(); - native void A_DragonCheckCrash(); - States { Spawn: @@ -58,6 +49,253 @@ class Dragon : Actor DRAG M -1; Stop; } + + //============================================================================ + // + // DragonSeek + // + //============================================================================ + + private void DragonSeek (double thresh, double turnMax) + { + double dist; + double delta; + Actor targ; + int i; + double bestAngle; + double angleToSpot, angleToTarget; + Actor mo; + + targ = tracer; + if(targ == null) + { + return; + } + + double diff = deltaangle(angle, AngleTo(targ)); + delta = abs(diff); + + if (delta > thresh) + { + delta /= 2; + if (delta > turnMax) + { + delta = turnMax; + } + } + if (diff > 0) + { // Turn clockwise + angle = angle + delta; + } + else + { // Turn counter clockwise + angle = angle - delta; + } + VelFromAngle(); + + dist = DistanceBySpeed(targ, Speed); + if (pos.z + height < targ.pos.z || targ.pos.z + targ.height < pos.z) + { + Vel.Z = (targ.pos.z - pos.z) / dist; + } + if (targ.bShootable && random[DragonSeek]() < 64) + { // attack the destination mobj if it's attackable + Actor oldTarget; + + if (absangle(angle, AngleTo(targ)) < 22.5) + { + oldTarget = target; + target = targ; + if (CheckMeleeRange ()) + { + int damage = random[DragonSeek](1, 8) * 10; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + else if (random[DragonSeek]() < 128 && CheckMissileRange()) + { + SpawnMissile(targ, "DragonFireball"); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + target = oldTarget; + } + } + if (dist < 4) + { // Hit the target thing + if (target && random[DragonSeek]() < 200) + { + Actor bestActor = null; + bestAngle = 360.; + angleToTarget = AngleTo(target); + for (i = 0; i < 5; i++) + { + if (!targ.args[i]) + { + continue; + } + ActorIterator iter = ActorIterator.Create(targ.args[i]); + mo = iter.Next (); + if (mo == null) + { + continue; + } + angleToSpot = AngleTo(mo); + double diff = absangle(angleToSpot, angleToTarget); + if (diff < bestAngle) + { + bestAngle = diff; + bestActor = mo; + } + } + if (bestActor != null) + { + tracer = bestActor; + } + } + else + { + // [RH] Don't lock up if the dragon doesn't have any + // targs defined + for (i = 0; i < 5; ++i) + { + if (targ.args[i] != 0) + { + break; + } + } + if (i < 5) + { + do + { + i = (random[DragonSeek]() >> 2) % 5; + } while(!targ.args[i]); + ActorIterator iter = ActorIterator.Create(targ.args[i]); + tracer = iter.Next (); + } + } + } + } + + //============================================================================ + // + // A_DragonInitFlight + // + //============================================================================ + + void A_DragonInitFlight() + { + ActorIterator iter = ActorIterator.Create(tid); + + do + { // find the first tid identical to the dragon's tid + tracer = iter.Next (); + if (tracer == null) + { + SetState (SpawnState); + return; + } + } while (tracer == self); + RemoveFromHash (); + } + + //============================================================================ + // + // A_DragonFlight + // + //============================================================================ + + void A_DragonFlight() + { + double ang; + + DragonSeek (4., 8.); + if (target) + { + if(!target.bShootable) + { // target died + target = null; + return; + } + ang = absangle(angle, AngleTo(target)); + if (ang <22.5 && CheckMeleeRange()) + { + int damage = random[DragonFlight](1, 8) * 8; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + else if (ang <= 20) + { + SetState (MissileState); + A_PlaySound (AttackSound, CHAN_WEAPON); + } + } + else + { + LookForPlayers (true); + } + } + + //============================================================================ + // + // A_DragonFlap + // + //============================================================================ + + void A_DragonFlap() + { + A_DragonFlight(); + if (random[DragonFlight]() < 240) + { + A_PlaySound ("DragonWingflap", CHAN_BODY); + } + else + { + PlayActiveSound (); + } + } + + //============================================================================ + // + // A_DragonAttack + // + //============================================================================ + + void A_DragonAttack() + { + SpawnMissile (target, "DragonFireball"); + } + + + //============================================================================ + // + // A_DragonPain + // + //============================================================================ + + void A_DragonPain() + { + A_Pain(); + if (!tracer) + { // no destination spot yet + SetState (SeeState); + } + } + + //============================================================================ + // + // A_DragonCheckCrash + // + //============================================================================ + + void A_DragonCheckCrash() + { + if (pos.z < floorz) + { + SetStateLabel ("Crash"); + } + } } // Dragon Fireball ---------------------------------------------------------- @@ -77,8 +315,6 @@ class DragonFireball : Actor DeathSound "DragonFireballExplode"; } - native void A_DragonFX2(); - States { Spawn: @@ -90,6 +326,30 @@ class DragonFireball : Actor DRFX KL 3 Bright; Stop; } + + //============================================================================ + // + // A_DragonFX2 + // + //============================================================================ + + void A_DragonFX2() + { + int delay = 16+(random[DragonFX2]()>>3); + for (int i = random[DragonFX2](1, 4); i; i--) + { + double xo = (random[DragonFX2]() - 128) / 4.; + double yo = (random[DragonFX2]() - 128) / 4.; + double zo = (random[DragonFX2]() - 128) / 16.; + + Actor mo = Spawn ("DragonExplosion", Vec3Offset(xo, yo, zo), ALLOW_REPLACE); + if (mo) + { + mo.tics = delay + (random[DragonFX2](0, 3)) * i*2; + mo.target = target; + } + } + } } // Dragon Fireball Secondary Explosion --------------------------------------