This commit is contained in:
Christoph Oelckers 2016-09-19 09:01:34 +02:00
commit 475077f1de
18 changed files with 115 additions and 207 deletions

View file

@ -111,6 +111,8 @@ struct FPortalGroupArray;
// Any questions? // Any questions?
// //
// --- mobj.flags --- // --- mobj.flags ---
enum ActorFlag enum ActorFlag
{ {
@ -1014,7 +1016,9 @@ public:
SDWORD tics; // state tic counter SDWORD tics; // state tic counter
FState *state; FState *state;
VMFunction *Damage; // For missiles and monster railgun //VMFunction *Damage; // For missiles and monster railgun
int DamageVal;
VMFunction *DamageFunc;
int projectileKickback; int projectileKickback;
ActorFlags flags; ActorFlags flags;
ActorFlags2 flags2; // Heretic flags ActorFlags2 flags2; // Heretic flags
@ -1201,6 +1205,23 @@ public:
FState *GetRaiseState(); FState *GetRaiseState();
void Revive(); void Revive();
void SetDamage(int dmg)
{
DamageVal = dmg;
DamageFunc = nullptr;
}
bool IsZeroDamage() const
{
return DamageVal == 0 && DamageFunc == nullptr;
}
void RestoreDamage()
{
DamageVal = GetDefault()->DamageVal;
DamageFunc = GetDefault()->DamageFunc;
}
FState *FindState (FName label) const FState *FindState (FName label) const
{ {
return GetClass()->FindState(1, &label); return GetClass()->FindState(1, &label);

View file

@ -915,7 +915,7 @@ static int PatchThing (int thingy)
} }
else if (linelen == 14 && stricmp (Line1, "Missile damage") == 0) else if (linelen == 14 && stricmp (Line1, "Missile damage") == 0)
{ {
info->Damage = CreateDamageFunction(val); info->SetDamage(val);
} }
else if (linelen == 5) else if (linelen == 5)
{ {

View file

@ -47,7 +47,7 @@ static void BrainishExplosion (const DVector3 &pos)
boom->SetState (state); boom->SetState (state);
} }
boom->effects = 0; boom->effects = 0;
boom->Damage = NULL; // disables collision detection which is not wanted here boom->SetDamage(0); // disables collision detection which is not wanted here
boom->tics -= pr_brainscream() & 7; boom->tics -= pr_brainscream() & 7;
if (boom->tics < 1) if (boom->tics < 1)
boom->tics = 1; boom->tics = 1;

View file

@ -115,7 +115,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack)
fire->target = baseFire->target; fire->target = baseFire->target;
fire->Angles.Yaw = baseFire->Angles.Yaw; fire->Angles.Yaw = baseFire->Angles.Yaw;
fire->Vel = baseFire->Vel; fire->Vel = baseFire->Vel;
fire->Damage = NULL; fire->SetDamage(0);
fire->health = (i+1) * 2; fire->health = (i+1) * 2;
P_CheckMissileSpawn (fire, self->radius); P_CheckMissileSpawn (fire, self->radius);
} }
@ -205,7 +205,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichFireGrow)
self->AddZ(9.); self->AddZ(9.);
if (self->health == 0) if (self->health == 0)
{ {
self->Damage = self->GetDefault()->Damage; self->RestoreDamage();
self->SetState (self->FindState("NoGrow")); self->SetState (self->FindState("NoGrow"));
} }
return 0; return 0;

View file

@ -340,7 +340,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LastZap)
{ {
mo->SetState (mo->FindState (NAME_Death)); mo->SetState (mo->FindState (NAME_Death));
mo->Vel.Z = 40; mo->Vel.Z = 40;
mo->Damage = NULL; mo->SetDamage(0);
} }
return 0; return 0;
} }

View file

@ -344,7 +344,7 @@ size_t PClassActor::PropagateMark()
// Mark damage function // Mark damage function
if (Defaults != NULL) if (Defaults != NULL)
{ {
GC::Mark(((AActor *)Defaults)->Damage); GC::Mark(((AActor *)Defaults)->DamageFunc);
} }
// marked += ActorInfo->NumOwnedStates * sizeof(FState); // marked += ActorInfo->NumOwnedStates * sizeof(FState);

View file

@ -3791,7 +3791,7 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value)
break; break;
case APROP_Damage: case APROP_Damage:
actor->Damage = CreateDamageFunction(value); actor->SetDamage(value);
break; break;
case APROP_Alpha: case APROP_Alpha:

View file

@ -1320,7 +1320,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch
// [RH] What is the point of this check, again? In Hexen, it is unconditional, // [RH] What is the point of this check, again? In Hexen, it is unconditional,
// but here we only do it if the missile's damage is 0. // but here we only do it if the missile's damage is 0.
// MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either. // MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either.
if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->Damage == 0 || !(tm.thing->flags & MF_MISSILE))) if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->IsZeroDamage() || !(tm.thing->flags & MF_MISSILE)))
{ {
return (tm.thing->target == thing || !(thing->flags & MF_SOLID)); return (tm.thing->target == thing || !(thing->flags & MF_SOLID));
} }

View file

@ -139,7 +139,8 @@ IMPLEMENT_POINTY_CLASS (AActor)
DECLARE_POINTER (LastHeard) DECLARE_POINTER (LastHeard)
DECLARE_POINTER (master) DECLARE_POINTER (master)
DECLARE_POINTER (Poisoner) DECLARE_POINTER (Poisoner)
DECLARE_POINTER (Damage) DECLARE_POINTER (DamageFunc)
DECLARE_POINTER (alternative)
END_POINTERS END_POINTERS
AActor::~AActor () AActor::~AActor ()
@ -148,73 +149,6 @@ AActor::~AActor ()
// Use Destroy() instead. // Use Destroy() instead.
} }
//==========================================================================
//
// CalcDamageValue
//
// Given a script function, returns an integer to represent it in a
// savegame. This encoding is compatible with previous incarnations
// where damage was an integer.
//
// 0 : use null function
// 0x40000000 : use default function
// anything else : use function that returns this number
//
//==========================================================================
static int CalcDamageValue(VMFunction *func)
{
if (func == NULL)
{
return 0;
}
VMScriptFunction *sfunc = dyn_cast<VMScriptFunction>(func);
if (sfunc == NULL)
{
return 0x40000000;
}
VMOP *op = sfunc->Code;
// If the function was created by CreateDamageFunction(), extract
// the value used to create it and return that. Otherwise, return
// indicating to use the default function.
if (op->op == OP_RETI && op->a == 0)
{
return op->i16;
}
if (op->op == OP_RET && op->a == 0 && op->b == (REGT_INT | REGT_KONST))
{
return sfunc->KonstD[op->c];
}
return 0x40000000;
}
//==========================================================================
//
// UncalcDamageValue
//
// Given a damage integer, returns a script function for it.
//
//==========================================================================
static VMFunction *UncalcDamageValue(int dmg, VMFunction *def)
{
if (dmg == 0)
{
return NULL;
}
if ((dmg & 0xC0000000) == 0x40000000)
{
return def;
}
// Does the default version return this? If so, use it. Otherwise,
// create a new function.
if (CalcDamageValue(def) == dmg)
{
return def;
}
return CreateDamageFunction(dmg);
}
//========================================================================== //==========================================================================
// //
// AActor :: Serialize // AActor :: Serialize
@ -262,18 +196,16 @@ void AActor::Serialize(FArchive &arc)
<< projectilepassheight << projectilepassheight
<< Vel << Vel
<< tics << tics
<< state; << state
if (arc.IsStoring()) << DamageVal;
if (DamageVal == 0x40000000 || DamageVal == -1)
{ {
int dmg; DamageVal = -1;
dmg = CalcDamageValue(Damage); DamageFunc = GetDefault()->DamageFunc;
arc << dmg;
} }
else else
{ {
int dmg; DamageFunc = nullptr;
arc << dmg;
Damage = UncalcDamageValue(dmg, GetDefault()->Damage);
} }
P_SerializeTerrain(arc, floorterrain); P_SerializeTerrain(arc, floorterrain);
arc << projectileKickback arc << projectileKickback
@ -2974,8 +2906,21 @@ CCMD(utid)
int AActor::GetMissileDamage (int mask, int add) int AActor::GetMissileDamage (int mask, int add)
{ {
if (Damage == NULL) if (DamageVal >= 0)
{ {
if (mask == 0)
{
return add * DamageVal;
}
else
{
return ((pr_missiledamage() & mask) + add) * DamageVal;
}
}
if (DamageFunc == nullptr)
{
// This should never happen
assert(false && "No damage function found");
return 0; return 0;
} }
VMFrameStack stack; VMFrameStack stack;
@ -2987,22 +2932,11 @@ int AActor::GetMissileDamage (int mask, int add)
results[0].IntAt(&amount); results[0].IntAt(&amount);
results[1].IntAt(&calculated); results[1].IntAt(&calculated);
if (stack.Call(Damage, &param, 1, results, 2) < 1) if (stack.Call(DamageFunc, &param, 1, results, 2) < 1)
{ // No results { // No results
return 0; return 0;
} }
if (calculated) return amount;
{
return amount;
}
else if (mask == 0)
{
return add * amount;
}
else
{
return ((pr_missiledamage() & mask) + add) * amount;
}
} }
void AActor::Howl () void AActor::Howl ()
@ -3694,7 +3628,7 @@ void AActor::Tick ()
// still have missiles that go straight up and down through actors without // still have missiles that go straight up and down through actors without
// damaging anything. // damaging anything.
// (for backwards compatibility this must check for lack of damage function, not for zero damage!) // (for backwards compatibility this must check for lack of damage function, not for zero damage!)
if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && Damage != NULL) if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage())
{ {
Vel.X = MinVel; Vel.X = MinVel;
} }

View file

@ -496,8 +496,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
else if (def == DEF_Projectile && sc.Compare ("Damage")) else if (def == DEF_Projectile && sc.Compare ("Damage"))
{ {
sc.MustGetNumber (); sc.MustGetNumber ();
FxDamageValue *x = new FxDamageValue(new FxConstant(sc.Number, sc), false); defaults->SetDamage(sc.Number);
defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(x) + 1);
} }
else if (def == DEF_Projectile && sc.Compare ("DamageType")) else if (def == DEF_Projectile && sc.Compare ("DamageType"))
{ {

View file

@ -348,12 +348,12 @@ static void FinishThingdef()
continue; continue;
} }
if (def->Damage != NULL) if (def->DamageFunc != nullptr)
{ {
FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->Damage - 1]; FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->DamageFunc - 1];
VMScriptFunction *sfunc; VMScriptFunction *sfunc;
sfunc = dmg->GetFunction(); sfunc = dmg->GetFunction();
if (sfunc == NULL) if (sfunc == nullptr)
{ {
FCompileContext ctx(ti); FCompileContext ctx(ti);
dmg = static_cast<FxDamageValue *>(dmg->Resolve(ctx)); dmg = static_cast<FxDamageValue *>(dmg->Resolve(ctx));
@ -365,15 +365,15 @@ static void FinishThingdef()
dmg->Emit(&buildit); dmg->Emit(&buildit);
sfunc = buildit.MakeFunction(); sfunc = buildit.MakeFunction();
sfunc->NumArgs = 1; sfunc->NumArgs = 1;
sfunc->Proto = NULL; ///FIXME: Need a proper prototype here sfunc->Proto = nullptr; ///FIXME: Need a proper prototype here
// Save this function in case this damage value was reused // Save this function in case this damage value was reused
// (which happens quite easily with inheritance). // (which happens quite easily with inheritance).
dmg->SetFunction(sfunc); dmg->SetFunction(sfunc);
} }
} }
def->Damage = sfunc; def->DamageFunc = sfunc;
if (dump != NULL && sfunc != NULL) if (dump != nullptr && sfunc != nullptr)
{ {
char label[64]; char label[64];
int labellen = mysnprintf(label, countof(label), "Function %s.Damage", int labellen = mysnprintf(label, countof(label), "Function %s.Damage",

View file

@ -231,6 +231,38 @@ DEFINE_ACTION_FUNCTION(AActor, CheckClass)
return 0; return 0;
} }
//==========================================================================
//
// CheckClass
//
// NON-ACTION function to calculate missile damage for the given actor
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, GetMissileDamage)
{
if (numret > 0)
{
assert(ret != NULL);
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(mask);
PARAM_INT(add)
PARAM_INT_OPT(pick_pointer) { pick_pointer = AAPTR_DEFAULT; }
self = COPY_AAPTR(self, pick_pointer);
if (self == NULL)
{
ret->SetInt(0);
}
else
{
ret->SetInt(self->GetMissileDamage(mask, add));
}
return 1;
}
return 0;
}
//========================================================================== //==========================================================================
// //
// IsPointerEqual // IsPointerEqual

View file

@ -666,4 +666,5 @@ void InitThingdef()
symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed))); symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed)));
symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold))); symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold)));
symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold))); symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold)));
symt.AddSymbol(new PField(NAME_Damage, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DamageVal)));
} }

View file

@ -854,20 +854,6 @@ public:
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
}; };
//==========================================================================
//
// FxDamage
//
//==========================================================================
class FxDamage : public FxExpression
{
public:
FxDamage(const FScriptPosition&);
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//========================================================================== //==========================================================================
// //
// FxArrayElement // FxArrayElement
@ -1188,12 +1174,11 @@ public:
class FxDamageValue : public FxExpression class FxDamageValue : public FxExpression
{ {
FxExpression *val; FxExpression *val;
bool Calculated;
VMScriptFunction *MyFunction; VMScriptFunction *MyFunction;
public: public:
FxDamageValue(FxExpression *v, bool calc); FxDamageValue(FxExpression *v);
~FxDamageValue(); ~FxDamageValue();
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);

View file

@ -3218,11 +3218,6 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars()); ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars());
} }
} }
// the damage property needs special handling
else if (Identifier == NAME_Damage)
{
newex = new FxDamage(ScriptPosition);
}
// now check the global identifiers. // now check the global identifiers.
else if ((sym = ctx.FindGlobal(Identifier)) != NULL) else if ((sym = ctx.FindGlobal(Identifier)) != NULL)
{ {
@ -3316,65 +3311,6 @@ ExpEmit FxSelf::Emit(VMFunctionBuilder *build)
} }
//==========================================================================
//
//
//
//==========================================================================
FxDamage::FxDamage(const FScriptPosition &pos)
: FxExpression(pos)
{
}
//==========================================================================
//
// FxDamage :: Resolve
//
//==========================================================================
FxExpression *FxDamage::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
ValueType = TypeSInt32;
return this;
}
//==========================================================================
//
// FxDamage :: Emit
//
// Call this actor's damage function, if it has one
//
//==========================================================================
ExpEmit FxDamage::Emit(VMFunctionBuilder *build)
{
ExpEmit dmgval(build, REGT_INT);
// Get damage function
ExpEmit dmgfunc(build, REGT_POINTER);
build->Emit(OP_LO, dmgfunc.RegNum, 0/*self*/, build->GetConstantInt(myoffsetof(AActor, Damage)));
// If it's non-null...
build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC));
size_t nulljump = build->Emit(OP_JMP, 0);
// ...call it
build->Emit(OP_PARAM, 0, REGT_POINTER, 0/*self*/);
build->Emit(OP_CALL, dmgfunc.RegNum, 1, 1);
build->Emit(OP_RESULT, 0, REGT_INT, dmgval.RegNum);
size_t notnulljump = build->Emit(OP_JMP, 0);
// Otherwise, use 0
build->BackpatchToHere(nulljump);
build->EmitLoadInt(dmgval.RegNum, 0);
build->BackpatchToHere(notnulljump);
return dmgval;
}
//========================================================================== //==========================================================================
// //
// //
@ -5225,18 +5161,12 @@ ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build)
// //
//========================================================================== //==========================================================================
FxDamageValue::FxDamageValue(FxExpression *v, bool calc) FxDamageValue::FxDamageValue(FxExpression *v)
: FxExpression(v->ScriptPosition) : FxExpression(v->ScriptPosition)
{ {
val = v; val = v;
ValueType = TypeVoid; ValueType = TypeVoid;
Calculated = calc;
MyFunction = NULL; MyFunction = NULL;
if (!calc)
{
assert(v->isConstant() && "Non-calculated damage must be constant");
}
} }
FxDamageValue::~FxDamageValue() FxDamageValue::~FxDamageValue()
@ -5272,7 +5202,7 @@ ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build)
assert(emitval.RegType == REGT_INT); assert(emitval.RegType == REGT_INT);
build->Emit(OP_RET, 0, REGT_INT | (emitval.Konst ? REGT_KONST : 0), emitval.RegNum); build->Emit(OP_RET, 0, REGT_INT | (emitval.Konst ? REGT_KONST : 0), emitval.RegNum);
} }
build->Emit(OP_RETI, 1 | RET_FINAL, Calculated); build->Emit(OP_RETI, 1 | RET_FINAL, true);
return ExpEmit(); return ExpEmit();
} }

View file

@ -865,19 +865,21 @@ static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defau
if (sc.CheckString ("(")) if (sc.CheckString ("("))
{ {
x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info)), true); conv.i = -1;
params.Push(conv);
x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info)));
sc.MustGetStringName(")"); sc.MustGetStringName(")");
conv.exp = x;
params.Push(conv);
} }
else else
{ {
sc.MustGetNumber(); sc.MustGetNumber();
if (sc.Number != 0) conv.i = sc.Number;
{ params.Push(conv);
x = new FxDamageValue(new FxConstant(sc.Number, bag.ScriptPosition), false); conv.exp = nullptr;
}
} }
conv.exp = x;
params.Push(conv);
} }
break; break;

View file

@ -636,7 +636,8 @@ DEFINE_PROPERTY(threshold, I, Actor)
//========================================================================== //==========================================================================
DEFINE_PROPERTY(damage, X, Actor) DEFINE_PROPERTY(damage, X, Actor)
{ {
PROP_EXP_PARM(id, 0); PROP_INT_PARM(dmgval, 0);
PROP_EXP_PARM(id, 1);
// Damage can either be a single number, in which case it is subject // Damage can either be a single number, in which case it is subject
// to the original damage calculation rules. Or, it can be an expression // to the original damage calculation rules. Or, it can be an expression
@ -646,13 +647,15 @@ DEFINE_PROPERTY(damage, X, Actor)
// Store this expression here for now. It will be converted to a function // Store this expression here for now. It will be converted to a function
// later once all actors have been processed. // later once all actors have been processed.
if (id == NULL) defaults->DamageVal = dmgval;
if (id == nullptr)
{ {
defaults->Damage = NULL; defaults->DamageFunc = nullptr;
} }
else else
{ {
defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1); defaults->DamageFunc = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1);
} }
} }

View file

@ -53,6 +53,7 @@ ACTOR Actor native //: Thinker
native int CountProximity(class<Actor> classname, float distance, int flags = 0, int ptr = AAPTR_DEFAULT); native int CountProximity(class<Actor> classname, float distance, int flags = 0, int ptr = AAPTR_DEFAULT);
native float GetSpriteAngle(int ptr = AAPTR_DEFAULT); native float GetSpriteAngle(int ptr = AAPTR_DEFAULT);
native float GetSpriteRotation(int ptr = AAPTR_DEFAULT); native float GetSpriteRotation(int ptr = AAPTR_DEFAULT);
native int GetMissileDamage(int mask, int add, int ptr = AAPTR_DEFAULT);
// Action functions // Action functions
// Meh, MBF redundant functions. Only for DeHackEd support. // Meh, MBF redundant functions. Only for DeHackEd support.