mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 14:51:51 +00:00
- scriptified PowerProtection and PowerDamage.
- made ModifyDamage calls iterative instead of recursive. With going through the VM they'd be too costly otherwise. - small optimization: Detect empty VM functions right when entering the VM and shortcut them. This is to reduce the overhead of virtual placeholders, which in a few cases (e.g. CanCollideWith and ModifyDamage) can be called quite frequently.
This commit is contained in:
parent
1d3afce59b
commit
9948189193
10 changed files with 206 additions and 179 deletions
|
@ -1608,140 +1608,6 @@ void APowerTimeFreezer::EndEffect()
|
|||
}
|
||||
}
|
||||
|
||||
// Damage powerup ------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerDamage, false, false)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
// Use sound channel 5 to avoid interference with other actions.
|
||||
if (Owner != NULL) S_Sound(Owner, 5, SeeSound, 1.0f, ATTN_NONE);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
// Use sound channel 5 to avoid interference with other actions.
|
||||
if (Owner != NULL) S_Sound(Owner, 5, DeathSound, 1.0f, ATTN_NONE);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerDamage :: ModifyDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerDamage::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (!passive && damage > 0)
|
||||
{
|
||||
int newdam;
|
||||
DmgFactors *df = GetClass()->DamageFactors;
|
||||
if (df != NULL && df->CountUsed() != 0)
|
||||
{
|
||||
newdam = MAX(1, df->Apply(damageType, damage));// don't allow zero damage as result of an underflow
|
||||
}
|
||||
else
|
||||
{
|
||||
newdam = damage * 4;
|
||||
}
|
||||
if (Owner != NULL && newdam > damage) S_Sound(Owner, 5, ActiveSound, 1.0f, ATTN_NONE);
|
||||
newdamage = damage = newdam;
|
||||
}
|
||||
if (Inventory != NULL) Inventory->ModifyDamage(damage, damageType, newdamage, passive);
|
||||
}
|
||||
|
||||
// Quarter damage powerup ------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(APowerProtection, false, false)
|
||||
|
||||
#define PROTECTION_FLAGS3 (MF3_NORADIUSDMG | MF3_DONTMORPH | MF3_DONTSQUASH | MF3_DONTBLAST | MF3_NOTELEOTHER)
|
||||
#define PROTECTION_FLAGS5 (MF5_NOPAIN | MF5_DONTRIP)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::InitEffect( )
|
||||
{
|
||||
Super::InitEffect();
|
||||
|
||||
if (Owner != NULL)
|
||||
{
|
||||
S_Sound(Owner, CHAN_AUTO, SeeSound, 1.0f, ATTN_NONE);
|
||||
|
||||
// Transfer various protection flags if owner does not already have them.
|
||||
// If the owner already has the flag, clear it from the powerup.
|
||||
// If the powerup still has a flag set, add it to the owner.
|
||||
flags3 &= ~(Owner->flags3 & PROTECTION_FLAGS3);
|
||||
Owner->flags3 |= flags3 & PROTECTION_FLAGS3;
|
||||
|
||||
flags5 &= ~(Owner->flags5 & PROTECTION_FLAGS5);
|
||||
Owner->flags5 |= flags5 & PROTECTION_FLAGS5;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::EndEffect( )
|
||||
{
|
||||
Super::EndEffect();
|
||||
if (Owner != NULL)
|
||||
{
|
||||
S_Sound(Owner, CHAN_AUTO, DeathSound, 1.0f, ATTN_NONE);
|
||||
Owner->flags3 &= ~(flags3 & PROTECTION_FLAGS3);
|
||||
Owner->flags5 &= ~(flags5 & PROTECTION_FLAGS5);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// APowerProtection :: AbsorbDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void APowerProtection::ModifyDamage(int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (passive && damage > 0)
|
||||
{
|
||||
int newdam;
|
||||
DmgFactors *df = GetClass()->DamageFactors;
|
||||
if (df != NULL && df->CountUsed() != 0)
|
||||
{
|
||||
newdam = MAX(0, df->Apply(damageType, damage));
|
||||
}
|
||||
else
|
||||
{
|
||||
newdam = damage / 4;
|
||||
}
|
||||
if (Owner != NULL && newdam < damage) S_Sound(Owner, CHAN_AUTO, ActiveSound, 1.0f, ATTN_NONE);
|
||||
newdamage = damage = newdam;
|
||||
}
|
||||
if (Inventory != NULL)
|
||||
{
|
||||
Inventory->ModifyDamage(damage, damageType, newdamage, passive);
|
||||
}
|
||||
}
|
||||
|
||||
// Morph powerup ------------------------------------------------------
|
||||
|
||||
|
|
|
@ -203,24 +203,6 @@ protected:
|
|||
virtual void EndEffect() override;
|
||||
};
|
||||
|
||||
class APowerDamage : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerDamage, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
|
||||
};
|
||||
|
||||
class APowerProtection : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerProtection, APowerup )
|
||||
protected:
|
||||
virtual void InitEffect () override;
|
||||
virtual void EndEffect () override;
|
||||
virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
|
||||
};
|
||||
|
||||
class APowerMorph : public APowerup
|
||||
{
|
||||
DECLARE_CLASS( APowerMorph, APowerup )
|
||||
|
|
|
@ -724,9 +724,10 @@ void AInventory::AbsorbDamage (int damage, FName damageType, int &newdamage)
|
|||
|
||||
void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, bool passive)
|
||||
{
|
||||
if (Inventory != NULL)
|
||||
IFVIRTUAL(AInventory, ModifyDamage)
|
||||
{
|
||||
Inventory->ModifyDamage (damage, damageType, newdamage, passive);
|
||||
VMValue params[5] = { (DObject*)this, damage, int(damageType), &newdamage, passive };
|
||||
GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public:
|
|||
|
||||
// still need to be done.
|
||||
virtual void AbsorbDamage(int damage, FName damageType, int &newdamage);
|
||||
virtual void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive);
|
||||
void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive);
|
||||
|
||||
// visual stuff is for later. Right now the VM has not yet access to the needed functionality.
|
||||
virtual bool DrawPowerup(int x, int y);
|
||||
|
|
|
@ -91,12 +91,6 @@ TArray<spechit_t> portalhit;
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, CanCollideWith)
|
||||
{
|
||||
// No need to check the parameters, as they are not even used.
|
||||
ACTION_RETURN_BOOL(true);
|
||||
}
|
||||
|
||||
bool P_CanCollideWith(AActor *tmthing, AActor *thing)
|
||||
{
|
||||
static unsigned VIndex = ~0u;
|
||||
|
|
|
@ -7420,8 +7420,12 @@ DEFINE_ACTION_FUNCTION(AActor, ClearCounters)
|
|||
|
||||
int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive)
|
||||
{
|
||||
if (Inventory != nullptr)
|
||||
Inventory->ModifyDamage(damage, damagetype, damage, passive);
|
||||
auto inv = Inventory;
|
||||
while (inv != nullptr)
|
||||
{
|
||||
inv->ModifyDamage(damage, damagetype, damage, passive);
|
||||
inv = inv->Inventory;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
@ -7847,6 +7851,25 @@ DEFINE_ACTION_FUNCTION(AActor, CountsAsKill)
|
|||
ACTION_RETURN_FLOAT(self->CountsAsKill());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, ApplyDamageFactors)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_CLASS(itemcls, AInventory);
|
||||
PARAM_NAME(damagetype);
|
||||
PARAM_INT(damage);
|
||||
PARAM_INT(defdamage);
|
||||
|
||||
DmgFactors *df = itemcls->DamageFactors;
|
||||
if (df != nullptr && df->CountUsed() != 0)
|
||||
{
|
||||
ACTION_RETURN_INT(df->Apply(damagetype, damage));
|
||||
}
|
||||
else
|
||||
{
|
||||
ACTION_RETURN_INT(defdamage);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// DropItem handling
|
||||
|
|
|
@ -466,18 +466,33 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur
|
|||
return static_cast<VMNativeFunction *>(func)->NativeCall(params, func->DefaultArgs, numparams, results, numresults);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto code = static_cast<VMScriptFunction *>(func)->Code;
|
||||
// handle empty functions consisting of a single return explicitly so that empty virtual callbacks do not need to set up an entire VM frame.
|
||||
if (code->word == 0x0080804e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (code->word == 0x0004804e)
|
||||
{
|
||||
if (numresults == 0) return 0;
|
||||
results[0].SetInt(static_cast<VMScriptFunction *>(func)->KonstD[0]);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
VMCycles[0].Clock();
|
||||
VMCalls[0]++;
|
||||
AllocFrame(static_cast<VMScriptFunction *>(func));
|
||||
allocated = true;
|
||||
VMFillParams(params, TopFrame(), numparams);
|
||||
int numret = VMExec(this, static_cast<VMScriptFunction *>(func)->Code, results, numresults);
|
||||
int numret = VMExec(this, code, results, numresults);
|
||||
PopFrame();
|
||||
VMCycles[0].Unclock();
|
||||
return numret;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (VMException *exception)
|
||||
{
|
||||
if (allocated)
|
||||
|
|
|
@ -270,16 +270,22 @@ class Actor : Thinker native
|
|||
virtual native void Die(Actor source, Actor inflictor, int dmgflags = 0);
|
||||
virtual native bool Slam(Actor victim);
|
||||
virtual native void Touch(Actor toucher);
|
||||
virtual native bool CanCollideWith(Actor other, bool passive); // This is an empty native function, it's native for the sole reason of performance as this is in a performance critical spot.
|
||||
|
||||
// Called when an actor is to be reflected by a disc of repulsion.
|
||||
// Returns true to continue normal blast processing.
|
||||
virtual bool SpecialBlastHandling (Actor source, double strength) // this is entirely on the script side with no native part at all.
|
||||
// Called by PIT_CheckThing to check if two actos actually can collide.
|
||||
virtual bool CanCollideWith(Actor other, bool passive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int SpecialMissileHit (Actor victim) // for this no native version exists
|
||||
// Called when an actor is to be reflected by a disc of repulsion.
|
||||
// Returns true to continue normal blast processing.
|
||||
virtual bool SpecialBlastHandling (Actor source, double strength)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is called before a missile gets exploded.
|
||||
virtual int SpecialMissileHit (Actor victim)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -288,6 +294,7 @@ class Actor : Thinker native
|
|||
native static int GetSpriteIndex(name sprt);
|
||||
native static double GetDefaultSpeed(class<Actor> type);
|
||||
native static class<Actor> GetSpawnableType(int spawnnum);
|
||||
native static int ApplyDamageFactors(class<Inventory> itemcls, Name damagetype, int damage, int defdamage);
|
||||
native void RemoveFromHash();
|
||||
native void ChangeTid(int newtid);
|
||||
native static int FindUniqueTid(int start = 0, int limit = 0);
|
||||
|
|
|
@ -40,6 +40,7 @@ class Inventory : Actor native
|
|||
|
||||
virtual double GetSpeedFactor() { return 1; }
|
||||
virtual bool GetNoTeleportFreeze() { return false; }
|
||||
virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {}
|
||||
|
||||
native void GoAwayAndDie();
|
||||
native void BecomeItem();
|
||||
|
|
|
@ -236,20 +236,158 @@ class PowerTimeFreezer : Powerup native
|
|||
}
|
||||
}
|
||||
|
||||
class PowerDamage : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// Damage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerDamage : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -25;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.A_PlaySound(SeeSound, CHAN_5, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
class PowerProtection : Powerup native
|
||||
//===========================================================================
|
||||
//
|
||||
// EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.A_PlaySound(DeathSound, CHAN_5, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ModifyDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
|
||||
{
|
||||
if (!passive && damage > 0)
|
||||
{
|
||||
newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage * 4));
|
||||
if (Owner != null && newdamage > damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Protection
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class PowerProtection : Powerup
|
||||
{
|
||||
Default
|
||||
{
|
||||
Powerup.Duration -25;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// InitEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void InitEffect()
|
||||
{
|
||||
Super.InitEffect();
|
||||
|
||||
let o = Owner; // copy to a local variable for quicker access.
|
||||
if (o != null)
|
||||
{
|
||||
o.A_PlaySound(SeeSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
|
||||
// Transfer various protection flags if owner does not already have them.
|
||||
// If the owner already has the flag, clear it from the powerup.
|
||||
// If the powerup still has a flag set, add it to the owner.
|
||||
bNoRadiusDmg &= !o.bNoRadiusDmg;
|
||||
o.bNoRadiusDmg |= bNoRadiusDmg;
|
||||
|
||||
bDontMorph &= !o.bDontMorph;
|
||||
o.bDontMorph |= bDontMorph;
|
||||
|
||||
bDontSquash &= !o.bDontSquash;
|
||||
o.bDontSquash |= bDontSquash;
|
||||
|
||||
bDontBlast &= !o.bDontBlast;
|
||||
o.bDontBlast |= bDontBlast;
|
||||
|
||||
bNoTeleOther &= !o.bNoTeleOther;
|
||||
o.bNoTeleOther |= bNoTeleOther;
|
||||
|
||||
bNoPain &= !o.bNoPain;
|
||||
o.bNoPain |= bNoPain;
|
||||
|
||||
bDontRip &= !o.bDontRip;
|
||||
o.bDontRip |= bDontRip;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// EndEffect
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void EndEffect()
|
||||
{
|
||||
Super.EndEffect();
|
||||
let o = Owner; // copy to a local variable for quicker access.
|
||||
if (o != null)
|
||||
{
|
||||
o.A_PlaySound(DeathSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
|
||||
o.bNoRadiusDmg &= !bNoRadiusDmg;
|
||||
o.bDontMorph &= !bDontMorph;
|
||||
o.bDontSquash &= !bDontSquash;
|
||||
o.bDontBlast &= !bDontBlast;
|
||||
o.bNoTeleOther &= !bNoTeleOther;
|
||||
o.bNoPain &= !bNoPain;
|
||||
o.bDontRip &= !bDontRip;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AbsorbDamage
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
override void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive)
|
||||
{
|
||||
if (passive && damage > 0)
|
||||
{
|
||||
newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage / 4));
|
||||
if (Owner != null && newdamage < damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
Loading…
Reference in a new issue