diff --git a/src/actor.h b/src/actor.h index 4c0f33dde..578b094ed 100644 --- a/src/actor.h +++ b/src/actor.h @@ -667,7 +667,7 @@ public: // Called by PIT_CheckThing() and needed for some Hexen things. // Returns -1 for normal behavior, 0 to return false, and 1 to return true. // I'm not sure I like it this way, but it will do for now. - virtual int SpecialMissileHit (AActor *victim); + int SpecialMissileHit (AActor *victim); // Returns true if it's okay to switch target to "other" after being attacked by it. virtual bool OkayToSwitchTarget (AActor *other); diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 503ac6fbc..e6f27c434 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -25,7 +25,6 @@ // Include all the Hexen stuff here to reduce compile time #include "a_heresiarch.cpp" -#include "a_magecone.cpp" #include "a_magelightning.cpp" #include "a_magestaff.cpp" #include "a_serpent.cpp" diff --git a/src/g_hexen/a_magecone.cpp b/src/g_hexen/a_magecone.cpp deleted file mode 100644 index 102f68155..000000000 --- a/src/g_hexen/a_magecone.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* -#include "actor.h" -#include "gi.h" -#include "m_random.h" -#include "s_sound.h" -#include "d_player.h" -#include "a_action.h" -#include "p_local.h" -#include "a_action.h" -#include "p_pspr.h" -#include "gstrings.h" -#include "a_hexenglobal.h" -#include "vm.h" -*/ - -const int SHARDSPAWN_LEFT = 1; -const int SHARDSPAWN_RIGHT = 2; -const int SHARDSPAWN_UP = 4; -const int SHARDSPAWN_DOWN = 8; - -static FRandom pr_cone ("FireConePL1"); - -void A_FireConePL1 (AActor *actor); -void A_ShedShard (AActor *); - -// Frost Missile ------------------------------------------------------------ - -class AFrostMissile : public AActor -{ - DECLARE_CLASS (AFrostMissile, AActor) -public: - int DoSpecialDamage (AActor *victim, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AFrostMissile, false, false) - -int AFrostMissile::DoSpecialDamage (AActor *victim, int damage, FName damagetype) -{ - if (special2 > 0) - { - damage <<= special2; - } - return damage; -} - -//============================================================================ -// -// A_FireConePL1 -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_FireConePL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle angle; - int damage; - DAngle slope; - int i; - AActor *mo; - bool conedone=false; - player_t *player; - FTranslatedLineTarget t; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - S_Sound (self, CHAN_WEAPON, "MageShardsFire", 1, ATTN_NORM); - - damage = 90+(pr_cone()&15); - for (i = 0; i < 16; i++) - { - angle = self->Angles.Yaw + i*(45./16); - slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0., ALF_CHECK3D); - if (t.linetarget) - { - P_DamageMobj (t.linetarget, self, self, damage, NAME_Ice, DMG_USEANGLE, t.angleFromSource.Degrees); - conedone = true; - break; - } - } - - // didn't find any creatures, so fire projectiles - if (!conedone) - { - mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(AFrostMissile)); - if (mo) - { - mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP - |SHARDSPAWN_RIGHT; - mo->special2 = 3; // Set sperm count (levels of reproductivity) - mo->target = self; - mo->args[0] = 3; // Mark Initial shard as super damage - } - } - return 0; -} - -//============================================================================ -// -// A_ShedShard -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_ShedShard) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - int spawndir = self->special1; - int spermcount = self->special2; - - if (spermcount <= 0) - { - return 0; // No sperm left - } - self->special2 = 0; - spermcount--; - - // every so many calls, spawn a new missile in its set directions - if (spawndir & SHARDSPAWN_LEFT) - { - mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile), - self->Angles.Yaw + 5, 0, (20. + 2 * spermcount), self->target); - if (mo) - { - mo->special1 = SHARDSPAWN_LEFT; - mo->special2 = spermcount; - mo->Vel.Z = self->Vel.Z; - mo->args[0] = (spermcount==3)?2:0; - } - } - if (spawndir & SHARDSPAWN_RIGHT) - { - mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile), - self->Angles.Yaw - 5, 0, (20. + 2 * spermcount), self->target); - if (mo) - { - mo->special1 = SHARDSPAWN_RIGHT; - mo->special2 = spermcount; - mo->Vel.Z = self->Vel.Z; - mo->args[0] = (spermcount==3)?2:0; - } - } - if (spawndir & SHARDSPAWN_UP) - { - mo = P_SpawnMissileAngleZSpeed(self, self->Z() + 8., RUNTIME_CLASS(AFrostMissile), - self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target); - if (mo) - { - mo->Vel.Z = self->Vel.Z; - if (spermcount & 1) // Every other reproduction - mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; - else - mo->special1 = SHARDSPAWN_UP; - mo->special2 = spermcount; - mo->args[0] = (spermcount==3)?2:0; - } - } - if (spawndir & SHARDSPAWN_DOWN) - { - mo = P_SpawnMissileAngleZSpeed(self, self->Z() - 4., RUNTIME_CLASS(AFrostMissile), - self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target); - if (mo) - { - mo->Vel.Z = self->Vel.Z; - if (spermcount & 1) // Every other reproduction - mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; - else - mo->special1 = SHARDSPAWN_DOWN; - mo->special2 = spermcount; - mo->target = self->target; - mo->args[0] = (spermcount==3)?2:0; - } - } - return 0; -} diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp index d5fcef880..794e6660b 100644 --- a/src/g_hexen/a_magelightning.cpp +++ b/src/g_hexen/a_magelightning.cpp @@ -25,99 +25,6 @@ static FRandom pr_hit ("LightningHit"); DECLARE_ACTION(A_LightningClip) DECLARE_ACTION(A_LightningZap) -// Lightning ---------------------------------------------------------------- - -class ALightning : public AActor -{ - DECLARE_CLASS (ALightning, AActor) -public: - int SpecialMissileHit (AActor *victim); -}; - -IMPLEMENT_CLASS(ALightning, false, false) - -int ALightning::SpecialMissileHit (AActor *thing) -{ - if (thing->flags&MF_SHOOTABLE && thing != target) - { - if (thing->Mass != INT_MAX) - { - thing->Vel.X += Vel.X / 16; - thing->Vel.Y += Vel.Y / 16; - } - if ((!thing->player && !(thing->flags2&MF2_BOSS)) - || !(level.time&1)) - { - P_DamageMobj(thing, this, target, 3, NAME_Electric); - if (!(S_IsActorPlayingSomething (this, CHAN_WEAPON, -1))) - { - S_Sound (this, CHAN_WEAPON, this->AttackSound, 1, ATTN_NORM); - } - if (thing->flags3&MF3_ISMONSTER && pr_hit() < 64) - { - thing->Howl (); - } - } - health--; - if (health <= 0 || thing->health <= 0) - { - return 0; - } - if (flags3 & MF3_FLOORHUGGER) - { - if (lastenemy && ! lastenemy->tracer) - { - lastenemy->tracer = thing; - } - } - else if (!tracer) - { - tracer = thing; - } - } - return 1; // lightning zaps through all sprites -} - -// Lightning Zap ------------------------------------------------------------ - -class ALightningZap : public AActor -{ - DECLARE_CLASS (ALightningZap, AActor) -public: - int SpecialMissileHit (AActor *thing); -}; - -IMPLEMENT_CLASS(ALightningZap, false, false) - -int ALightningZap::SpecialMissileHit (AActor *thing) -{ - AActor *lmo; - - if (thing->flags&MF_SHOOTABLE && thing != target) - { - lmo = lastenemy; - if (lmo) - { - if (lmo->flags3 & MF3_FLOORHUGGER) - { - if (lmo->lastenemy && !lmo->lastenemy->tracer) - { - lmo->lastenemy->tracer = thing; - } - } - else if (!lmo->tracer) - { - lmo->tracer = thing; - } - if (!(level.time&3)) - { - lmo->health--; - } - } - } - return -1; -} - //============================================================================ // // A_LightningReady diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp index 27e9f5732..035744aba 100644 --- a/src/g_hexen/a_magestaff.cpp +++ b/src/g_hexen/a_magestaff.cpp @@ -72,24 +72,11 @@ class AMageStaffFX2 : public AActor { DECLARE_CLASS(AMageStaffFX2, AActor) public: - int SpecialMissileHit (AActor *victim); bool SpecialBlastHandling (AActor *source, double strength); }; IMPLEMENT_CLASS(AMageStaffFX2, false, false) -int AMageStaffFX2::SpecialMissileHit (AActor *victim) -{ - if (victim != target && - !victim->player && - !(victim->flags2 & MF2_BOSS)) - { - P_DamageMobj (victim, this, target, 10, NAME_Fire); - return 1; // Keep going - } - return -1; -} - bool AMageStaffFX2::SpecialBlastHandling (AActor *source, double strength) { // Reflect to originator diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 358e46e8c..250927a5a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3357,6 +3357,7 @@ bool AActor::SpecialBlastHandling (AActor *source, double strength) } // This only gets called from the script side so we do not need a native wrapper like for the other virtual methods. +// This will be removed, once all actors overriding this method are exported. DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling) { PARAM_SELF_PROLOGUE(AActor); @@ -3365,10 +3366,20 @@ DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling) ACTION_RETURN_BOOL(self->SpecialBlastHandling(source, strength)); } - +// This virtual method only exists on the script side. int AActor::SpecialMissileHit (AActor *victim) { - return -1; + IFVIRTUAL(AActor, SpecialMissileHit) + { + VMValue params[2] = { (DObject*)this, victim }; + VMReturn ret; + int retval; + VMFrameStack stack; + ret.IntAt(&retval); + stack.Call(func, params, 2, &ret, 1, nullptr); + return retval; + } + else return -1; } bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 17dfea393..189f70090 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -6,6 +6,7 @@ class Actor : Thinker native const FLOATRANDZ = ONCEILINGZ-1; const TELEFRAG_DAMAGE = 1000000; const MinVel = 1./65536; + const LARGE_MASS = 10000000; // not INT_MAX on purpose // flags are not defined here, the native fields for those get synthesized from the internal tables. @@ -268,6 +269,11 @@ class Actor : Thinker native virtual native bool UseInventory(Inventory item); virtual native bool SpecialBlastHandling (Actor source, double strength); + virtual int SpecialMissileHit (Actor victim) // for this no native version exists + { + return -1; + } + native static double GetDefaultSpeed(class type); native static class GetSpawnableType(int spawnnum); diff --git a/wadsrc/static/zscript/hexen/magecone.txt b/wadsrc/static/zscript/hexen/magecone.txt index a9f2d29dc..2bbd58f3d 100644 --- a/wadsrc/static/zscript/hexen/magecone.txt +++ b/wadsrc/static/zscript/hexen/magecone.txt @@ -17,8 +17,6 @@ class MWeapFrost : MageWeapon Tag "$TAG_MWEAPFROST"; } - action native void A_FireConePL1(); - States { Spawn: @@ -45,12 +43,68 @@ class MWeapFrost : MageWeapon CONE A 10 A_ReFire; Goto Ready; } + + //============================================================================ + // + // A_FireConePL1 + // + //============================================================================ + + action void A_FireConePL1() + { + bool conedone=false; + FTranslatedLineTarget t; + + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + A_PlaySound ("MageShardsFire", CHAN_WEAPON); + + int damage = 90+(random[MageCone]() & 15); + for (int i = 0; i < 16; i++) + { + double ang = angle + i*(45./16); + double slope = AimLineAttack (ang, MELEERANGE, t, 0., ALF_CHECK3D); + if (t.linetarget) + { + t.linetarget.DamageMobj (self, self, damage, 'Ice', DMG_USEANGLE, t.angleFromSource); + conedone = true; + break; + } + } + + // didn't find any creatures, so fire projectiles + if (!conedone) + { + Actor mo = SpawnPlayerMissile ("FrostMissile"); + if (mo) + { + mo.special1 = FrostMissile.SHARDSPAWN_LEFT|FrostMissile.SHARDSPAWN_DOWN|FrostMissile.SHARDSPAWN_UP|FrostMissile.SHARDSPAWN_RIGHT; + mo.special2 = 3; // Set sperm count (levels of reproductivity) + mo.target = self; + mo.args[0] = 3; // Mark Initial shard as super damage + } + } + } } // Frost Missile ------------------------------------------------------------ -class FrostMissile : Actor native +class FrostMissile : Actor { + const SHARDSPAWN_LEFT = 1; + const SHARDSPAWN_RIGHT = 2; + const SHARDSPAWN_UP = 4; + const SHARDSPAWN_DOWN = 8; + Default { Speed 25; @@ -63,8 +117,6 @@ class FrostMissile : Actor native Obituary "$OB_MPMWEAPFROST"; } - native void A_ShedShard(); - States { Spawn: @@ -77,6 +129,88 @@ class FrostMissile : Actor native SHEX ABCDE 5 Bright; Stop; } + + override int DoSpecialDamage (Actor victim, int damage, Name damagetype) + { + if (special2 > 0) + { + damage <<= special2; + } + return damage; + } + + //============================================================================ + // + // A_ShedShard + // + //============================================================================ + + void A_ShedShard() + { + int spawndir = special1; + int spermcount = special2; + Actor mo; + + if (spermcount <= 0) + { + return; // No sperm left + } + special2 = 0; + spermcount--; + + // every so many calls, spawn a new missile in its set directions + if (spawndir & SHARDSPAWN_LEFT) + { + mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle + 5, 0, (20. + 2 * spermcount), target); + if (mo) + { + mo.special1 = SHARDSPAWN_LEFT; + mo.special2 = spermcount; + mo.Vel.Z = Vel.Z; + mo.args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_RIGHT) + { + mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle - 5, 0, (20. + 2 * spermcount), target); + if (mo) + { + mo.special1 = SHARDSPAWN_RIGHT; + mo.special2 = spermcount; + mo.Vel.Z = Vel.Z; + mo.args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_UP) + { + mo = SpawnMissileAngleZSpeed(pos.z + 8., "FrostMissile", angle, 0, (15. + 2 * spermcount), target); + if (mo) + { + mo.Vel.Z = Vel.Z; + if (spermcount & 1) // Every other reproduction + mo.special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; + else + mo.special1 = SHARDSPAWN_UP; + mo.special2 = spermcount; + mo.args[0] = (spermcount==3)?2:0; + } + } + if (spawndir & SHARDSPAWN_DOWN) + { + mo = SpawnMissileAngleZSpeed(pos.z - 4., "FrostMissile", angle, 0, (15. + 2 * spermcount), target); + if (mo) + { + mo.Vel.Z = Vel.Z; + if (spermcount & 1) // Every other reproduction + mo.special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; + else + mo.special1 = SHARDSPAWN_DOWN; + mo.special2 = spermcount; + mo.target = target; + mo.args[0] = (spermcount==3)?2:0; + } + } + } } // Ice Shard ---------------------------------------------------------------- diff --git a/wadsrc/static/zscript/hexen/magelightning.txt b/wadsrc/static/zscript/hexen/magelightning.txt index 154776d15..b778451ac 100644 --- a/wadsrc/static/zscript/hexen/magelightning.txt +++ b/wadsrc/static/zscript/hexen/magelightning.txt @@ -54,7 +54,7 @@ class MWeapLightning : MageWeapon // Ceiling Lightning -------------------------------------------------------- -class Lightning : Actor native +class Lightning : Actor { Default { @@ -63,6 +63,45 @@ class Lightning : Actor native ActiveSound "MageLightningContinuous"; Obituary "$OB_MPMWEAPLIGHTNING"; } + + override int SpecialMissileHit (Actor thing) + { + if (thing.bShootable && thing != target) + { + if (thing.Mass < LARGE_MASS) + { + thing.Vel.X += Vel.X / 16; + thing.Vel.Y += Vel.Y / 16; + } + if ((!thing.player && !thing.bBoss) || !(level.time&1)) + { + thing.DamageMobj(self, target, 3, 'Electric'); + A_PlaySound(self.AttackSound, CHAN_WEAPON, 1, true); + if (thing.bIsMonster && random[LightningHit]() < 64) + { + thing.Howl (); + } + } + health--; + if (health <= 0 || thing.health <= 0) + { + return 0; + } + if (bFloorHugger) + { + if (lastenemy && ! lastenemy.tracer) + { + lastenemy.tracer = thing; + } + } + else if (!tracer) + { + tracer = thing; + } + } + return 1; // lightning zaps through all sprites + } + } class LightningCeiling : Lightning @@ -141,7 +180,7 @@ class LightningFloor : LightningCeiling // Lightning Zap ------------------------------------------------------------ -class LightningZap : Actor native +class LightningZap : Actor { Default { @@ -166,4 +205,34 @@ class LightningZap : Actor native MLFX NOPQRSTU 2 Bright; Stop; } + + override int SpecialMissileHit (Actor thing) + { + Actor lmo; + + if (thing.bShootable && thing != target) + { + lmo = lastenemy; + if (lmo) + { + if (lmo.bFloorHugger) + { + if (lmo.lastenemy && !lmo.lastenemy.tracer) + { + lmo.lastenemy.tracer = thing; + } + } + else if (!lmo.tracer) + { + lmo.tracer = thing; + } + if (!(level.time&3)) + { + lmo.health--; + } + } + } + return -1; + } + } diff --git a/wadsrc/static/zscript/hexen/magestaff.txt b/wadsrc/static/zscript/hexen/magestaff.txt index c11542834..d485a72fa 100644 --- a/wadsrc/static/zscript/hexen/magestaff.txt +++ b/wadsrc/static/zscript/hexen/magestaff.txt @@ -157,4 +157,16 @@ class MageStaffFX2 : Actor native MSP2 I 4 Bright; Stop; } + + override int SpecialMissileHit (Actor victim) + { + if (victim != target && !victim.player && !victim.bBoss) + { + victim.DamageMobj (self, target, 10, 'Fire'); + return 1; // Keep going + } + return -1; + } + + }