diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index f78c6875e..335565efd 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -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 ------------------------------------------------------ diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index 90670abb5..746d22589 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -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 ) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 3c1d32d42..c963fd918 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -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); } } diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 32914e3e5..ea87cfe85 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -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); diff --git a/src/p_map.cpp b/src/p_map.cpp index 434db69f1..5bbb9c3f9 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -91,12 +91,6 @@ TArray 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; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8d31035a2..0919cfa3b 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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 diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index c500c9359..08559445e 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -467,15 +467,30 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur } else { - VMCycles[0].Clock(); - VMCalls[0]++; - AllocFrame(static_cast(func)); - allocated = true; - VMFillParams(params, TopFrame(), numparams); - int numret = VMExec(this, static_cast(func)->Code, results, numresults); - PopFrame(); - VMCycles[0].Unclock(); - return numret; + auto code = static_cast(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(func)->KonstD[0]); + return 1; + } + else + { + VMCycles[0].Clock(); + VMCalls[0]++; + AllocFrame(static_cast(func)); + allocated = true; + VMFillParams(params, TopFrame(), numparams); + int numret = VMExec(this, code, results, numresults); + PopFrame(); + VMCycles[0].Unclock(); + return numret; + } } } catch (VMException *exception) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 2ced709ae..bbd4b3073 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -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 type); native static class GetSpawnableType(int spawnnum); + native static int ApplyDamageFactors(class 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); diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index 6d034e789..d33b5e112 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -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(); diff --git a/wadsrc/static/zscript/shared/powerups.txt b/wadsrc/static/zscript/shared/powerups.txt index c6064d676..1ef333f60 100644 --- a/wadsrc/static/zscript/shared/powerups.txt +++ b/wadsrc/static/zscript/shared/powerups.txt @@ -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); + } + } + + //=========================================================================== + // + // 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); + } + } } -class PowerProtection : Powerup native +//=========================================================================== +// +// 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); + } + } } //===========================================================================