diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd44a6761d..d4edae81e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -857,7 +857,6 @@ set( NOT_COMPILED_SOURCE_FILES ${OTHER_SYSTEM_SOURCES} sc_man_scanner.h sc_man_scanner.re - g_heretic/a_dsparil.cpp g_heretic/a_hereticartifacts.cpp g_heretic/a_hereticweaps.cpp g_hexen/a_blastradius.cpp diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp deleted file mode 100644 index 5ab4067d39..0000000000 --- a/src/g_heretic/a_dsparil.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* -#include "actor.h" -#include "info.h" -#include "p_local.h" -#include "p_enemy.h" -#include "a_action.h" -#include "s_sound.h" -#include "m_random.h" -#include "a_sharedglobal.h" -#include "gstrings.h" -#include "a_specialspot.h" -#include "vm.h" -#include "g_level.h" -*/ - -static FRandom pr_s2fx1 ("S2FX1"); -static FRandom pr_scrc1atk ("Srcr1Attack"); -static FRandom pr_dst ("D'SparilTele"); -static FRandom pr_s2d ("Srcr2Decide"); -static FRandom pr_s2a ("Srcr2Attack"); -static FRandom pr_bluespark ("BlueSpark"); - -//---------------------------------------------------------------------------- -// -// PROC A_Sor1Pain -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Sor1Pain) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->special1 = 20; // Number of steps to walk fast - CALL_ACTION(A_Pain, self); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_Sor1Chase -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Sor1Chase) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->special1) - { - self->special1--; - self->tics -= 3; - } - A_Chase(stack, self); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_Srcr1Attack -// -// Sorcerer demon attack. -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Srcr1Attack) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - DAngle angle; - - if (!self->target) - { - return 0; - } - S_Sound (self, CHAN_BODY, self->AttackSound, 1, ATTN_NORM); - if (self->CheckMeleeRange ()) - { - int damage = pr_scrc1atk.HitDice (8); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - return 0; - } - - PClassActor *fx = PClass::FindActor("SorcererFX1"); - if (self->health > (self->SpawnHealth()/3)*2) - { // Spit one fireball - P_SpawnMissileZ (self, self->Z() + 48, self->target, fx ); - } - else - { // Spit three fireballs - mo = P_SpawnMissileZ (self, self->Z() + 48, self->target, fx); - if (mo != NULL) - { - angle = mo->Angles.Yaw; - P_SpawnMissileAngleZ(self, self->Z() + 48, fx, angle - 3, mo->Vel.Z); - P_SpawnMissileAngleZ(self, self->Z() + 48, fx, angle + 3, mo->Vel.Z); - } - if (self->health < self->SpawnHealth()/3) - { // Maybe attack again - if (self->special1) - { // Just attacked, so don't attack again - self->special1 = 0; - } - else - { // Set state to attack again - self->special1 = 1; - self->SetState (self->FindState("Missile2")); - } - } - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_SorcererRise -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_SorcererRise) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - - self->flags &= ~MF_SOLID; - mo = Spawn("Sorcerer2", self->Pos(), ALLOW_REPLACE); - mo->Translation = self->Translation; - mo->SetState (mo->FindState("Rise")); - mo->Angles.Yaw = self->Angles.Yaw; - mo->CopyFriendliness (self, true); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC P_DSparilTeleport -// -//---------------------------------------------------------------------------- - -void P_DSparilTeleport (AActor *actor) -{ - DVector3 prev; - AActor *mo; - AActor *spot; - - DSpotState *state = DSpotState::GetSpotState(); - if (state == NULL) return; - - spot = state->GetSpotWithMinMaxDistance(PClass::FindActor("BossSpot"), actor->X(), actor->Y(), 128, 0); - if (spot == NULL) return; - - prev = actor->Pos(); - if (P_TeleportMove (actor, spot->Pos(), false)) - { - mo = Spawn("Sorcerer2Telefade", prev, ALLOW_REPLACE); - if (mo) mo->Translation = actor->Translation; - S_Sound (mo, CHAN_BODY, "misc/teleport", 1, ATTN_NORM); - actor->SetState (actor->FindState("Teleport")); - S_Sound (actor, CHAN_BODY, "misc/teleport", 1, ATTN_NORM); - actor->SetZ(actor->floorz); - actor->Angles.Yaw = spot->Angles.Yaw; - actor->Vel.Zero(); - } -} - -//---------------------------------------------------------------------------- -// -// PROC A_Srcr2Decide -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Srcr2Decide) -{ - PARAM_SELF_PROLOGUE(AActor); - - static const int chance[] = - { - 192, 120, 120, 120, 64, 64, 32, 16, 0 - }; - - unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); - if (chanceindex >= countof(chance)) - { - chanceindex = countof(chance) - 1; - } - - if (pr_s2d() < chance[chanceindex]) - { - P_DSparilTeleport (self); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_Srcr2Attack -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Srcr2Attack) -{ - PARAM_SELF_PROLOGUE(AActor); - - int chance; - - if (!self->target) - { - return 0; - } - S_Sound (self, CHAN_BODY, self->AttackSound, 1, ATTN_NONE); - if (self->CheckMeleeRange()) - { - int damage = pr_s2a.HitDice (20); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - return 0; - } - chance = self->health < self->SpawnHealth()/2 ? 96 : 48; - if (pr_s2a() < chance) - { // Wizard spawners - - PClassActor *fx = PClass::FindActor("Sorcerer2FX2"); - if (fx) - { - P_SpawnMissileAngle(self, fx, self->Angles.Yaw - 45, 0.5); - P_SpawnMissileAngle(self, fx, self->Angles.Yaw + 45, 0.5); - } - } - else - { // Blue bolt - P_SpawnMissile (self, self->target, PClass::FindActor("Sorcerer2FX1")); - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_BlueSpark -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_BlueSpark) -{ - PARAM_SELF_PROLOGUE(AActor); - - int i; - AActor *mo; - - for (i = 0; i < 2; i++) - { - mo = Spawn("Sorcerer2FXSpark", self->Pos(), ALLOW_REPLACE); - mo->Vel.X = pr_bluespark.Random2() / 128.; - mo->Vel.Y = pr_bluespark.Random2() / 128.; - mo->Vel.Z = 1. + pr_bluespark() / 256.; - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_GenWizard -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_GenWizard) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - - mo = Spawn("Wizard", self->Pos(), ALLOW_REPLACE); - if (mo != NULL) - { - mo->AddZ(-mo->GetDefault()->Height / 2, false); - if (!P_TestMobjLocation (mo)) - { // Didn't fit - mo->ClearCounters(); - mo->Destroy (); - } - else - { // [RH] Make the new wizards inherit D'Sparil's target - mo->CopyFriendliness (self->target, true); - - self->Vel.Zero(); - self->SetState (self->FindState(NAME_Death)); - self->flags &= ~MF_MISSILE; - mo->master = self->target; - P_SpawnTeleportFog(self, self->Pos(), false, true); - } - } - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_Sor2DthInit -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Sor2DthInit) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->special1 = 7; // Animation loop counter - P_Massacre (); // Kill monsters early - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_Sor2DthLoop -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_Sor2DthLoop) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (--self->special1) - { // Need to loop - self->SetState (self->FindState("DeathLoop")); - } - return 0; -} - diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp index 18093b752d..5929c1d865 100644 --- a/src/g_heretic/a_hereticmisc.cpp +++ b/src/g_heretic/a_hereticmisc.cpp @@ -19,6 +19,5 @@ #include "serializer.h" // Include all the other Heretic stuff here to reduce compile time -#include "a_dsparil.cpp" #include "a_hereticartifacts.cpp" #include "a_hereticweaps.cpp" diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index 4d3a0b0e82..dc34fd2068 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -27,7 +27,6 @@ static FRandom pr_bfx1 ("BlasterFX1"); static FRandom pr_ripd ("RipperD"); static FRandom pr_fb1 ("FireBlasterPL1"); static FRandom pr_bfx1t ("BlasterFX1Tick"); -static FRandom pr_hrfx2 ("HornRodFX2"); static FRandom pr_rp ("RainPillar"); static FRandom pr_fsr1 ("FireSkullRodPL1"); static FRandom pr_storm ("SkullRodStorm"); @@ -819,27 +818,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnRippers) // --- Skull rod ------------------------------------------------------------ -// Horn Rod FX 2 ------------------------------------------------------------ - -class AHornRodFX2 : public AActor -{ - DECLARE_CLASS (AHornRodFX2, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(AHornRodFX2, false, false, false, false) - -int AHornRodFX2::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96) - { // D'Sparil teleports away - P_DSparilTeleport (target); - return -1; - } - return damage; -} - // Rain pillar 1 ------------------------------------------------------------ class ARainPillar : public AActor @@ -940,7 +918,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL2) if (!weapon->DepleteAmmo (weapon->bAltFire)) return 0; } - P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AHornRodFX2), self->Angles.Yaw, &t, &MissileActor); + P_SpawnPlayerMissile (self, 0,0,0, PClass::FindActor("HornRodFX2"), self->Angles.Yaw, &t, &MissileActor); // Use MissileActor instead of the return value from // P_SpawnPlayerMissile because we need to give info to the mobj // even if it exploded immediately. @@ -1170,25 +1148,6 @@ void APhoenixRodPowered::EndPowerup () P_SetPsprite(Owner->player, PSP_WEAPON, SisterWeapon->GetReadyState()); } -class APhoenixFX1 : public AActor -{ - DECLARE_CLASS (APhoenixFX1, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(APhoenixFX1, false, false, false, false) - -int APhoenixFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96) - { // D'Sparil teleports away - P_DSparilTeleport (target); - return -1; - } - return damage; -} - // Phoenix FX 2 ------------------------------------------------------------- class APhoenixFX2 : public AActor @@ -1232,7 +1191,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL1) if (!weapon->DepleteAmmo (weapon->bAltFire)) return 0; } - P_SpawnPlayerMissile (self, RUNTIME_CLASS(APhoenixFX1)); + P_SpawnPlayerMissile (self, PClass::FindActor("PhoenixFX1")); self->Thrust(self->Angles.Yaw + 180, 4); return 0; } diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index e96f5a9036..935333a5ce 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -344,6 +344,18 @@ ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(PClassActor *type, double x, return NULL; } +DEFINE_ACTION_FUNCTION(DSpotState, GetSpotWithMinMaxDistance) +{ + PARAM_SELF_PROLOGUE(DSpotState); + PARAM_CLASS(type, AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(mindist); + PARAM_FLOAT(maxdist); + ACTION_RETURN_OBJECT(self->GetSpotWithMinMaxDistance(type, x, y, mindist, maxdist)); +} + + //---------------------------------------------------------------------------- // // diff --git a/src/p_map.cpp b/src/p_map.cpp index db0a129c6d..af5ab100af 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1777,6 +1777,11 @@ bool P_TestMobjLocation(AActor *mobj) return false; } +DEFINE_ACTION_FUNCTION(AActor, TestMobjLocation) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_TestMobjLocation(self)); +} //============================================================================= // diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a79fc0f16d..8784dc0753 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6781,6 +6781,12 @@ int AActor::SpawnHealth() const } } +DEFINE_ACTION_FUNCTION(AActor, SpawnHealth) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(self->SpawnHealth()); +} + FState *AActor::GetRaiseState() { if (!(flags & MF_CORPSE)) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 0e39e259e0..ca305d677b 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -1449,19 +1449,6 @@ DEFINE_ACTION_FUNCTION(AActor, AimTarget) ACTION_RETURN_OBJECT(P_AimTarget(self)); } - -DEFINE_ACTION_FUNCTION(AActor, A_Light) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT(light); - - if (self->player != NULL) - { - self->player->extralight = clamp(light, -20, 20); - } - return 0; -} - //------------------------------------------------------------------------ // // PROC P_SetupPsprites diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 01595ff659..80bfd4f7fb 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -96,6 +96,18 @@ void P_SpawnTeleportFog(AActor *mobj, const DVector3 &pos, bool beforeTele, bool mo->target = mobj; } +DEFINE_ACTION_FUNCTION(AActor, SpawnTeleportFog) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_BOOL(before); + PARAM_BOOL(settarget); + P_SpawnTeleportFog(self, DVector3(x, y, z), before, settarget); + return 0; +} + // // TELEPORTATION // diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 1cc16214ce..ba4eab9e3b 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -5593,6 +5593,22 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct PSymbol *sym; PSymbolTable *symtbl; bool isclass = objtype->IsKindOf(RUNTIME_CLASS(PClass)); + + if (Identifier == NAME_Default) + { + if (!objtype->IsKindOf(RUNTIME_CLASS(PClassActor))) + { + ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); + delete this; + return nullptr; + } + + FxExpression * x = new FxClassDefaults(object, ScriptPosition); + object = nullptr; + delete this; + return x->Resolve(ctx); + } + if ((sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index e7179bd47e..f4489f575c 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -249,6 +249,7 @@ class Actor : Thinker native native bool GiveBody (int num, int max=0); native void RestoreDamage(); + native int SpawnHealth(); native void SetDamage(int dmg); native double Distance2D(Actor other); native void SetOrigin(vector3 newpos, bool moving); @@ -259,17 +260,19 @@ class Actor : Thinker native native Actor AimTarget(); native bool CheckMissileSpawn(double maxdist); native bool CheckPosition(Vector2 pos, bool actorsonly = false); + native bool TestMobjLocation(); native static Actor Spawn(class type, vector3 pos = (0,0,0), int replace = NO_REPLACE); native Actor SpawnMissile(Actor dest, class type, Actor owner = null); native Actor SpawnMissileZ (double z, Actor dest, class type); native Actor SpawnMissileAngleZSpeed (double z, class type, double angle, double vz, double speed, Actor owner = null, bool checkspawn = true); native Actor, Actor SpawnPlayerMissile(class type, double angle = 0, double x = 0, double y = 0, double z = 0, out FTranslatedLineTarget pLineTarget = null, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0); + native void SpawnTeleportFog(Vector3 pos, bool beforeTele, bool setTarget); - native void A_Light(int extralight); - void A_Light0() { A_Light(0); } - void A_Light1() { A_Light(1); } - void A_Light2() { A_Light(2); } - void A_LightInverse() { A_Light(0x80000000); } + void A_Light(int extralight) { if (player) player.extralight = clamp(extralight, -20, 20); } + void A_Light0() { if (player) player.extralight = 0; } + void A_Light1() { if (player) player.extralight = 1; } + void A_Light2() { if (player) player.extralight = 2; } + void A_LightInverse() { if (player) player.extralight = 0x80000000; } native Actor OldSpawnMissile(Actor dest, class type, Actor owner = null); native Actor SpawnPuff(class pufftype, vector3 pos, double hitdir, double particledir, int updown, int flags = 0, Actor vict = null); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 11d565ae90..a63020bbf5 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -49,6 +49,8 @@ class SpotState : Object native { native static SpotState GetSpotState(); native SpecialSpot GetNextInList(class type, int skipcounter); + native SpecialSpot GetSpotWithMinMaxDistance(Class type, double x, double y, double mindist, double maxdist); + } struct LevelLocals native diff --git a/wadsrc/static/zscript/heretic/dsparil.txt b/wadsrc/static/zscript/heretic/dsparil.txt index 8cd60c8bb4..e2cfca558d 100644 --- a/wadsrc/static/zscript/heretic/dsparil.txt +++ b/wadsrc/static/zscript/heretic/dsparil.txt @@ -38,10 +38,6 @@ class Sorcerer1 : Actor HitObituary "$OB_DSPARIL1HIT"; } - native void A_Sor1Pain (); - native void A_Sor1Chase (); - native void A_Srcr1Attack (); - native void A_SorcererRise (); States { @@ -79,6 +75,104 @@ class Sorcerer1 : Actor SRCR L 12; SRCR P -1 A_SorcererRise; } + + + //---------------------------------------------------------------------------- + // + // PROC A_Sor1Pain + // + //---------------------------------------------------------------------------- + + void A_Sor1Pain () + { + special1 = 20; // Number of steps to walk fast + A_Pain(); + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor1Chase + // + //---------------------------------------------------------------------------- + + void A_Sor1Chase () + { + if (special1) + { + special1--; + tics -= 3; + } + A_Chase(); + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr1Attack + // + // Sorcerer demon attack. + // + //---------------------------------------------------------------------------- + + void A_Srcr1Attack () + { + if (!target) + { + return; + } + A_PlaySound (AttackSound, CHAN_BODY); + if (CheckMeleeRange ()) + { + int damage = random[Srcr1Attack](1,8) * 8; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + + if (health > (SpawnHealth()/3)*2) + { // Spit one fireball + SpawnMissileZ (pos.z + 48, target, "SorcererFX1"); + } + else + { // Spit three fireballs + Actor mo = SpawnMissileZ (pos.z + 48, target, "SorcererFX1"); + if (mo != null) + { + double ang = mo.angle; + SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang - 3, mo.Vel.Z); + SpawnMissileAngleZ(pos.z + 48, "SorcererFX1", ang + 3, mo.Vel.Z); + } + if (health < SpawnHealth()/3) + { // Maybe attack again + if (special1) + { // Just attacked, so don't attack again + special1 = 0; + } + else + { // Set state to attack again + special1 = 1; + SetStateLabel("Missile2"); + } + } + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_SorcererRise + // + //---------------------------------------------------------------------------- + + void A_SorcererRise () + { + bSolid = false; + Actor mo = Spawn("Sorcerer2", Pos, ALLOW_REPLACE); + mo.Translation = Translation; + mo.SetStateLabel("Rise"); + mo.angle = angle; + mo.CopyFriendliness (self, true); + } + + } @@ -142,10 +236,6 @@ class Sorcerer2 : Actor HitObituary "$OB_DSPARIL2HIT"; } - native void A_Srcr2Decide (); - native void A_Srcr2Attack (); - native void A_Sor2DthInit (); - native void A_Sor2DthLoop (); States { @@ -189,6 +279,118 @@ class Sorcerer2 : Actor SDTH O -1 A_BossDeath; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC P_DSparilTeleport + // + //---------------------------------------------------------------------------- + + void DSparilTeleport () + { + SpotState state = SpotState.GetSpotState(); + if (state == null) return; + + Actor spot = state.GetSpotWithMinMaxDistance("BossSpot", pos.x, pos.y, 128, 0); + if (spot == null) return; + + Vector3 prev = Pos; + if (TeleportMove (spot.Pos, false)) + { + Actor mo = Spawn("Sorcerer2Telefade", prev, ALLOW_REPLACE); + if (mo) + { + mo.Translation = Translation; + mo.A_PlaySound("misc/teleport", CHAN_BODY); + } + SetStateLabel ("Teleport"); + A_PlaySound ("misc/teleport", CHAN_BODY); + SetZ(floorz); + angle = spot.angle; + vel = (0,0,0); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr2Decide + // + //---------------------------------------------------------------------------- + + void A_Srcr2Decide () + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + int health8 = max(1, SpawnHealth() / 8); + int chanceindex = min(8, health / health8); + + if (random[Srcr2Decide]() < chance[chanceindex]) + { + DSparilTeleport (); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Srcr2Attack + // + //---------------------------------------------------------------------------- + + void A_Srcr2Attack () + { + if (!target) + { + return; + } + A_PlaySound (AttackSound, CHAN_BODY); + if (CheckMeleeRange()) + { + int damage = random[Srcr2Atk](1, 8) * 20; + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + return; + } + int chance = health < SpawnHealth()/2 ? 96 : 48; + if (random[Srcr2Atk]() < chance) + { // Wizard spawners + + SpawnMissileAngle("Sorcerer2FX2", Angle - 45, 0.5); + SpawnMissileAngle("Sorcerer2FX2", Angle + 45, 0.5); + } + else + { // Blue bolt + SpawnMissile (target, "Sorcerer2FX1"); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor2DthInit + // + //---------------------------------------------------------------------------- + + void A_Sor2DthInit () + { + special1 = 7; // Animation loop counter + Thing_Destroy(0); // Kill monsters early + } + + //---------------------------------------------------------------------------- + // + // PROC A_Sor2DthLoop + // + //---------------------------------------------------------------------------- + + void A_Sor2DthLoop () + { + if (--special1) + { // Need to loop + SetStateLabel("DeathLoop"); + } + } } @@ -210,8 +412,6 @@ class Sorcerer2FX1 : Actor RenderStyle "Add"; } - native void A_BlueSpark (); - States { Spawn: @@ -222,6 +422,23 @@ class Sorcerer2FX1 : Actor FX16 HIJKL 5 BRIGHT; Stop; } + + //---------------------------------------------------------------------------- + // + // PROC A_BlueSpark + // + //---------------------------------------------------------------------------- + + void A_BlueSpark () + { + for (int i = 0; i < 2; i++) + { + Actor mo = Spawn("Sorcerer2FXSpark", pos, ALLOW_REPLACE); + mo.Vel.X = Random2[BlueSpark]() / 128.; + mo.Vel.Y = Random2[BlueSpark]() / 128.; + mo.Vel.Z = 1. + Random[BlueSpark]() / 256.; + } + } } // Sorcerer 2 FX Spark ------------------------------------------------------ @@ -244,7 +461,7 @@ class Sorcerer2FXSpark : Actor Spawn: FX16 DEF 12 BRIGHT; Stop; - } + } } // Sorcerer 2 FX 2 ---------------------------------------------------------- @@ -263,8 +480,6 @@ class Sorcerer2FX2 : Actor RenderStyle "Add"; } - native void A_GenWizard (); - States { Spawn: @@ -277,7 +492,38 @@ class Sorcerer2FX2 : Actor Stop; } + +//---------------------------------------------------------------------------- +// +// PROC A_GenWizard +// +//---------------------------------------------------------------------------- + + void A_GenWizard () + { + Actor mo = Spawn("Wizard", pos, ALLOW_REPLACE); + if (mo != null) + { + mo.AddZ(-mo.Default.Height / 2, false); + if (!mo.TestMobjLocation ()) + { // Didn't fit + mo.ClearCounters(); + mo.Destroy (); + } + else + { // [RH] Make the new wizards inherit D'Sparil's target + mo.CopyFriendliness (self.target, true); + + Vel = (0,0,0); + SetStateLabel('Death'); + bMissile = false; + mo.master = target; + SpawnTeleportFog(pos, false, true); + } + } + } } + // Sorcerer 2 Telefade ------------------------------------------------------ class Sorcerer2Telefade : Actor diff --git a/wadsrc/static/zscript/heretic/hereticweaps.txt b/wadsrc/static/zscript/heretic/hereticweaps.txt index 4c254500b9..e309ba0956 100644 --- a/wadsrc/static/zscript/heretic/hereticweaps.txt +++ b/wadsrc/static/zscript/heretic/hereticweaps.txt @@ -1037,7 +1037,7 @@ class HornRodFX1 : Actor // Horn Rod FX 2 ------------------------------------------------------------ -class HornRodFX2 : Actor native +class HornRodFX2 : Actor { Default { @@ -1059,7 +1059,7 @@ class HornRodFX2 : Actor native States { - Spawn: + Spawn: FX00 C 3 BRIGHT; FX00 D 3 BRIGHT A_SeekerMissile(10, 30); FX00 E 3 BRIGHT; @@ -1074,6 +1074,18 @@ class HornRodFX2 : Actor native FX00 G 1 A_SkullRodStorm; Wait; } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + Sorcerer2 s2 = Sorcerer2(target); + if (s2 != null && random[HornRodFX2]() < 96) + { // D'Sparil teleports away + s2.DSparilTeleport (); + return -1; + } + return damage; + } + } // Rain pillar 1 ------------------------------------------------------------ @@ -1195,7 +1207,7 @@ class PhoenixRodPowered : PhoenixRod native // Phoenix FX 1 ------------------------------------------------------------- -class PhoenixFX1 : Actor native +class PhoenixFX1 : Actor { Default { @@ -1225,6 +1237,18 @@ class PhoenixFX1 : Actor native FX08 DEFGH 4 BRIGHT; Stop; } + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + Sorcerer2 s2 = Sorcerer2(target); + if (s2 != null && random[HornRodFX2]() < 96) + { // D'Sparil teleports away + s2.DSparilTeleport (); + return -1; + } + return damage; + } + } // Phoenix puff -------------------------------------------------------------