- 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.
This commit is contained in:
Christoph Oelckers 2016-11-16 19:18:21 +01:00
parent 633da6e5d8
commit 76a74e0364
5 changed files with 327 additions and 347 deletions

View file

@ -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;
}

View file

@ -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"

View file

@ -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;
}

View file

@ -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;

View file

@ -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 --------------------------------------