From f5b3429274b4ad232408bd32799ffb98be1a0d3a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 29 Nov 2016 15:24:38 +0100 Subject: [PATCH] - partial scriptification of the Heresiarch --- src/g_hexen/a_heresiarch.cpp | 386 +------------------- wadsrc/static/zscript/doom/bossbrain.txt | 4 +- wadsrc/static/zscript/hexen/heresiarch.txt | 387 ++++++++++++++++++++- 3 files changed, 372 insertions(+), 405 deletions(-) diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index a53d9926d..38d916094 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -11,23 +11,6 @@ #include "g_level.h" */ -//============================================================================ -// -// Sorcerer stuff -// -// Sorcerer Variables -// specialf1 Angle of ball 1 (all others relative to that) -// StopBall which ball to stop at in stop mode (MT_???) -// args[0] Defense time -// args[1] Number of full rotations since stopping mode -// args[2] Target orbit speed for acceleration/deceleration -// args[3] Movement mode (see SORC_ macros) -// args[4] Current ball orbit speed -// Sorcerer Ball Variables -// specialf1 Previous angle of ball (for woosh) -// special2 Countdown of rapid fire (FX4) -//============================================================================ - #define SORCBALL_INITIAL_SPEED 7 #define SORCBALL_TERMINAL_SPEED 25 #define SORCBALL_SPEED_ROTATIONS 5 @@ -209,42 +192,7 @@ void ASorcBall1::DoFireSpell () } } -//============================================================================ -// -// A_SorcSpinBalls -// -// Spawn spinning balls above head - actor is sorcerer -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcSpinBalls) -{ - PARAM_SELF_PROLOGUE(AHeresiarch); - - AActor *mo; - - self->SpawnState += 2; // [RH] Don't spawn balls again - A_SlowBalls(self); - self->args[0] = 0; // Currently no defense - self->args[3] = SORC_NORMAL; - self->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed - self->BallAngle = 1.; - - DVector3 pos = self->PosPlusZ(-self->Floorclip + self->Height); - - mo = Spawn("SorcBall1", pos, NO_REPLACE); - if (mo) - { - mo->target = self; - mo->special2 = SORCFX4_RAPIDFIRE_TIME; - } - mo = Spawn("SorcBall2", pos, NO_REPLACE); - if (mo) mo->target = self; - mo = Spawn("SorcBall3", pos, NO_REPLACE); - if (mo) mo->target = self; - return 0; -} - - +DEFINE_FIELD(AHeresiarch, BallAngle); //============================================================================ // // A_SorcBallOrbit @@ -370,38 +318,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SorcBallOrbit) return 0; } -//============================================================================ -// -// A_SpeedBalls -// -// Set balls to speed mode - self is sorcerer -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpeedBalls) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->args[3] = SORC_ACCELERATE; // speed mode - self->args[2] = SORCBALL_TERMINAL_SPEED; // target speed - return 0; -} - - -//============================================================================ -// -// A_SlowBalls -// -// Set balls to slow mode - actor is sorcerer -// -//============================================================================ - -void A_SlowBalls(AActor *self) -{ - self->args[3] = SORC_DECELERATE; // slow mode - self->args[2] = SORCBALL_INITIAL_SPEED; // target speed -} - //============================================================================ // // A_StopBalls @@ -666,303 +582,3 @@ void A_SorcOffense2(AActor *self) } } -//============================================================================ -// -// A_SorcBossAttack -// -// Resume ball spinning -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcBossAttack) -{ - PARAM_SELF_PROLOGUE(AActor); - - self->args[3] = SORC_ACCELERATE; - self->args[2] = SORCBALL_INITIAL_SPEED; - return 0; -} - -//============================================================================ -// -// A_SpawnFizzle -// -// spell cast magic fizzle -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnFizzle) -{ - PARAM_SELF_PROLOGUE(AActor); - int speed = (int)self->Speed; - DAngle rangle; - AActor *mo; - int ix; - - DVector3 pos = self->Vec3Angle(5., self->Angles.Yaw, -self->Floorclip + self->Height / 2. ); - for (ix=0; ix<5; ix++) - { - mo = Spawn("SorcSpark1", pos, ALLOW_REPLACE); - if (mo) - { - rangle = self->Angles.Yaw + (pr_heresiarch() % 5) * (4096 / 360.); - mo->Vel.X = (pr_heresiarch() % speed) * rangle.Cos(); - mo->Vel.Y = (pr_heresiarch() % speed) * rangle.Sin(); - mo->Vel.Z = 2; - } - } - return 0; -} - - -//============================================================================ -// -// A_SorcFX1Seek -// -// Yellow spell - offense -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcFX1Seek) -{ - PARAM_SELF_PROLOGUE(AActor); - - A_DoBounceCheck (self, "SorcererHeadScream"); - P_SeekerMissile(self, 2, 6); - return 0; -} - - -//============================================================================ -// -// A_SorcFX2Split -// -// Blue spell - defense -// -//============================================================================ -// -// FX2 Variables -// specialf1 current angle -// special2 -// args[0] 0 = CW, 1 = CCW -// args[1] -//============================================================================ - -// Split ball in two -DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Split) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - - mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE); - if (mo) - { - mo->target = self->target; - mo->args[0] = 0; // CW - mo->specialf1 = self->Angles.Yaw.Degrees; // Set angle - mo->SetState (mo->FindState("Orbit")); - } - mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE); - if (mo) - { - mo->target = self->target; - mo->args[0] = 1; // CCW - mo->specialf1 = self->Angles.Yaw.Degrees; // Set angle - mo->SetState (mo->FindState("Orbit")); - } - self->Destroy (); - return 0; -} - -//============================================================================ -// -// A_SorcFX2Orbit -// -// Orbit FX2 about sorcerer -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Orbit) -{ - PARAM_SELF_PROLOGUE(AActor); - - DAngle angle; - DVector3 pos; - AActor *parent = self->target; - - // [RH] If no parent, then disappear - if (parent == NULL) - { - self->Destroy(); - return 0; - } - - double dist = parent->radius; - - if ((parent->health <= 0) || // Sorcerer is dead - (!parent->args[0])) // Time expired - { - self->SetState (self->FindState(NAME_Death)); - parent->args[0] = 0; - parent->flags2 &= ~MF2_REFLECTIVE; - parent->flags2 &= ~MF2_INVULNERABLE; - } - - if (self->args[0] && (parent->args[0]-- <= 0)) // Time expired - { - self->SetState (self->FindState(NAME_Death)); - parent->args[0] = 0; - parent->flags2 &= ~MF2_REFLECTIVE; - } - - // Move to new position based on angle - if (self->args[0]) // Counter clock-wise - { - self->specialf1 += 10; - angle = self->specialf1; - pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT); - pos.Z += 15 * angle.Cos(); - // Spawn trailer - Spawn("SorcFX2T1", pos, ALLOW_REPLACE); - } - else // Clock wise - { - self->specialf1 -= 10; - angle = self->specialf1; - pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT); - pos.Z += 20 * angle.Sin(); - // Spawn trailer - Spawn("SorcFX2T1", pos, ALLOW_REPLACE); - } - - self->SetOrigin (pos, true); - self->floorz = parent->floorz; - self->ceilingz = parent->ceilingz; - return 0; -} - -//============================================================================ -// -// A_SpawnBishop -// -// Green spell - spawn bishops -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnBishop) -{ - PARAM_SELF_PROLOGUE(AActor); - - AActor *mo; - mo = Spawn("Bishop", self->Pos(), ALLOW_REPLACE); - if (mo) - { - if (!P_TestMobjLocation(mo)) - { - mo->ClearCounters(); - mo->Destroy (); - } - else if (self->target != NULL) - { // [RH] Make the new bishops inherit the Heriarch's target - mo->CopyFriendliness (self->target, true); - mo->master = self->target; - } - } - self->Destroy (); - return 0; -} - -//============================================================================ -// -// A_SorcererBishopEntry -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcererBishopEntry) -{ - PARAM_SELF_PROLOGUE(AActor); - - Spawn("SorcFX3Explosion", self->Pos(), ALLOW_REPLACE); - S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM); - return 0; -} - -//============================================================================ -// -// A_SorcFX4Check -// -// FX4 - rapid fire balls -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcFX4Check) -{ - PARAM_SELF_PROLOGUE(AActor); - - if (self->special2-- <= 0) - { - self->SetState (self->FindState(NAME_Death)); - } - return 0; -} - -//============================================================================ -// -// A_SorcBallPop -// -// Ball death - bounce away in a random direction -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_SorcBallPop) -{ - PARAM_SELF_PROLOGUE(AActor); - - S_Sound (self, CHAN_BODY, "SorcererBallPop", 1, ATTN_NONE); - self->flags &= ~MF_NOGRAVITY; - self->Gravity = 1. / 8;; - - self->Vel.X = ((pr_heresiarch()%10)-5); - self->Vel.Y = ((pr_heresiarch()%10)-5); - self->Vel.Z = (2+(pr_heresiarch()%3)); - self->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit - self->args[3] = 5; // Bounce time in seconds - return 0; -} - -//============================================================================ -// -// A_DoBounceCheck -// -//============================================================================ - -void A_DoBounceCheck (AActor *self, const char *sound) -{ - if (self->args[4]-- <= 0) - { - if (self->args[3]-- <= 0) - { - self->SetState (self->FindState(NAME_Death)); - S_Sound (self, CHAN_BODY, sound, 1, ATTN_NONE); - } - else - { - self->args[4] = BOUNCE_TIME_UNIT; - } - } -} - -//============================================================================ -// -// A_BounceCheck -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_BounceCheck) -{ - PARAM_SELF_PROLOGUE(AActor); - - A_DoBounceCheck (self, "SorcererBigBallExplode"); - return 0; -} diff --git a/wadsrc/static/zscript/doom/bossbrain.txt b/wadsrc/static/zscript/doom/bossbrain.txt index e6a13317f..2a3f39265 100644 --- a/wadsrc/static/zscript/doom/bossbrain.txt +++ b/wadsrc/static/zscript/doom/bossbrain.txt @@ -262,12 +262,12 @@ extend class Actor if (!isdefault) { - A_PlaySound(self.AttackSound, CHAN_WEAPON); + A_PlaySound(self.AttackSound, CHAN_WEAPON, 1., false, ATTN_NONE); } else { // compatibility fallback - A_PlaySound("brain/spit", CHAN_WEAPON); + A_PlaySound("brain/spit", CHAN_WEAPON, 1., false, ATTN_NONE); } } } diff --git a/wadsrc/static/zscript/hexen/heresiarch.txt b/wadsrc/static/zscript/hexen/heresiarch.txt index 7d3ba63a5..cf0b16372 100644 --- a/wadsrc/static/zscript/hexen/heresiarch.txt +++ b/wadsrc/static/zscript/hexen/heresiarch.txt @@ -1,8 +1,52 @@ // The Heresiarch him/itself ------------------------------------------------ +//============================================================================ +// +// Sorcerer stuff +// +// Sorcerer Variables +// specialf1 Angle of ball 1 (all others relative to that) +// StopBall which ball to stop at in stop mode (MT_???) +// args[0] Defense time +// args[1] Number of full rotations since stopping mode +// args[2] Target orbit speed for acceleration/deceleration +// args[3] Movement mode (see SORC_ macros) +// args[4] Current ball orbit speed +// Sorcerer Ball Variables +// specialf1 Previous angle of ball (for woosh) +// special2 Countdown of rapid fire (FX4) +//============================================================================ + class Heresiarch native { + + const SORCBALL_INITIAL_SPEED = 7; + const SORCBALL_TERMINAL_SPEED = 25; + const SORCBALL_SPEED_ROTATIONS = 5; + const SORC_DEFENSE_TIME = 255; + const SORC_DEFENSE_HEIGHT = 45; + const BOUNCE_TIME_UNIT = (35/2); + const SORCFX4_RAPIDFIRE_TIME = (6*3); // 3 seconds + const SORCFX4_SPREAD_ANGLE = 20; + + enum ESorc + { + SORC_DECELERATE, + SORC_ACCELERATE, + SORC_STOPPING, + SORC_FIRESPELL, + SORC_STOPPED, + SORC_NORMAL, + SORC_FIRING_SPELL + } + + const BALL1_ANGLEOFFSET = 0.; + const BALL2_ANGLEOFFSET = 120.; + const BALL3_ANGLEOFFSET = 240.; + + native double BallAngle; + Default { Health 5000; @@ -27,11 +71,6 @@ class Heresiarch native Obituary "$OB_HERESIARCH"; } - native void A_SorcSpinBalls(); - native void A_SpeedBalls(); - native void A_SorcBossAttack(); - native void A_SpawnFizzle(); - States { Spawn: @@ -71,6 +110,105 @@ class Heresiarch native SORC Z -1 Bright; Stop; } + + //============================================================================ + // + // A_SorcSpinBalls + // + // Spawn spinning balls above head - actor is sorcerer + //============================================================================ + + void A_SorcSpinBalls() + { + A_SlowBalls(); + args[0] = 0; // Currently no defense + args[3] = SORC_NORMAL; + args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed + BallAngle = 1.; + + Vector3 ballpos = (pos.xy, -Floorclip + Height); + + Actor mo = Spawn("SorcBall1", pos, NO_REPLACE); + if (mo) + { + mo.target = self; + mo.special2 = SORCFX4_RAPIDFIRE_TIME; + } + mo = Spawn("SorcBall2", pos, NO_REPLACE); + if (mo) mo.target = self; + mo = Spawn("SorcBall3", pos, NO_REPLACE); + if (mo) mo.target = self; + } + + + //============================================================================ + // + // A_SpeedBalls + // + // Set balls to speed mode - self is sorcerer + // + //============================================================================ + + void A_SpeedBalls() + { + args[3] = SORC_ACCELERATE; // speed mode + args[2] = SORCBALL_TERMINAL_SPEED; // target speed + } + + + //============================================================================ + // + // A_SlowBalls + // + // Set balls to slow mode - actor is sorcerer + // + //============================================================================ + + void A_SlowBalls() + { + args[3] = SORC_DECELERATE; // slow mode + args[2] = SORCBALL_INITIAL_SPEED; // target speed + } + + //============================================================================ + // + // A_SorcBossAttack + // + // Resume ball spinning + // + //============================================================================ + + void A_SorcBossAttack() + { + args[3] = SORC_ACCELERATE; + args[2] = SORCBALL_INITIAL_SPEED; + } + + //============================================================================ + // + // A_SpawnFizzle + // + // spell cast magic fizzle + // + //============================================================================ + + void A_SpawnFizzle() + { + Vector3 pos = Vec3Angle(5., Angle, -Floorclip + Height / 2. ); + for (int ix=0; ix<5; ix++) + { + Actor mo = Spawn("SorcSpark1", pos, ALLOW_REPLACE); + if (mo) + { + double rangle = Angle + (random[Heresiarch]() % 5) * (4096 / 360.); + mo.Vel.X = (random[Heresiarch]() % speed) * cos(rangle); + mo.Vel.Y = (random[Heresiarch]() % speed) * sin(rangle); + mo.Vel.Z = 2; + } + } + } + + } // Base class for the balls flying around the Heresiarch's head ------------- @@ -94,14 +232,56 @@ class SorcBall native } native void A_SorcBallOrbit(); - native void A_SorcBallPop(); - native void A_BounceCheck (); void A_SorcBallExplode() { - bNOBOUNCESOUND = true; + bNoBounceSound = true; A_Explode(255, 255); } + + //============================================================================ + // + // A_SorcBallPop + // + // Ball death - bounce away in a random direction + // + //============================================================================ + + void A_SorcBallPop() + { + A_PlaySound ("SorcererBallPop", CHAN_BODY, 1, false, ATTN_NONE); + bNoGravity = false; + Gravity = 1. / 8; + + Vel.X = ((random[Heresiarch]()%10)-5); + Vel.Y = ((random[Heresiarch]()%10)-5); + Vel.Z = (2+(random[Heresiarch]()%3)); + args[4] = Heresiarch.BOUNCE_TIME_UNIT; // Bounce time unit + args[3] = 5; // Bounce time in seconds + } + + //============================================================================ + // + // A_BounceCheck + // + //============================================================================ + + void A_BounceCheck () + { + if (args[4]-- <= 0) + { + if (args[3]-- <= 0) + { + SetStateLabel("Death"); + A_PlaySound ("SorcererBigBallExplode", CHAN_BODY, 1, false, ATTN_NONE); + } + else + { + args[4] = Heresiarch.BOUNCE_TIME_UNIT; + } + } + } + } // First ball (purple) - fires projectiles ---------------------------------- @@ -191,8 +371,6 @@ class SorcFX1 : Actor DeathSound "SorcererHeadScream"; } - native void A_SorcFX1Seek(); - States { Spawn: @@ -204,6 +382,32 @@ class SorcFX1 : Actor FHFX SS 6 Bright; Stop; } + + //============================================================================ + // + // A_SorcFX1Seek + // + // Yellow spell - offense + // + //============================================================================ + + void A_SorcFX1Seek() + { + if (args[4]-- <= 0) + { + if (args[3]-- <= 0) + { + SetStateLabel("Death"); + A_PlaySound ("SorcererHeadScream", CHAN_BODY, 1, false, ATTN_NONE); + } + else + { + args[4] = Heresiarch.BOUNCE_TIME_UNIT; + } + } + A_SeekerMissile(2, 6); + } + } @@ -221,9 +425,6 @@ class SorcFX2 : Actor +NOTELEPORT } - native void A_SorcFX2Split(); - native void A_SorcFX2Orbit (); - states { Spawn: @@ -237,6 +438,106 @@ class SorcFX2 : Actor SBS2 A 10; Stop; } + + //============================================================================ + // + // A_SorcFX2Split + // + // Blue spell - defense + // + //============================================================================ + // + // FX2 Variables + // specialf1 current angle + // special2 + // args[0] 0 = CW, 1 = CCW + // args[1] + //============================================================================ + + // Split ball in two + void A_SorcFX2Split() + { + Actor mo = Spawn(GetClass(), Pos, NO_REPLACE); + if (mo) + { + mo.target = target; + mo.args[0] = 0; // CW + mo.specialf1 = Angle; // Set angle + mo.SetStateLabel("Orbit"); + } + mo = Spawn(GetClass(), Pos, NO_REPLACE); + if (mo) + { + mo.target = target; + mo.args[0] = 1; // CCW + mo.specialf1 = Angle; // Set angle + mo.SetStateLabel("Orbit"); + } + Destroy (); + } + + //============================================================================ + // + // A_SorcFX2Orbit + // + // Orbit FX2 about sorcerer + // + //============================================================================ + + void A_SorcFX2Orbit () + { + Actor parent = target; + + // [RH] If no parent, then disappear + if (parent == NULL) + { + Destroy(); + return; + } + + double dist = parent.radius; + + if ((parent.health <= 0) || // Sorcerer is dead + (!parent.args[0])) // Time expired + { + SetStateLabel("Death"); + parent.args[0] = 0; + parent.bReflective = false; + parent.bInvulnerable = false; + } + + if (args[0] && (parent.args[0]-- <= 0)) // Time expired + { + SetStateLabel("Death"); + parent.args[0] = 0; + parent.bReflective = false; + } + + Vector3 posi; + // Move to new position based on angle + if (args[0]) // Counter clock-wise + { + specialf1 += 10; + angle = specialf1; + posi = parent.Vec3Angle(dist, angle, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT); + posi.Z += 15 * cos(angle); + // Spawn trailer + Spawn("SorcFX2T1", pos, ALLOW_REPLACE); + } + else // Clock wise + { + specialf1 -= 10; + angle = specialf1; + posi = parent.Vec3Angle(dist, angle, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT); + posi.Z += 20 * sin(angle); + // Spawn trailer + Spawn("SorcFX2T1", pos, ALLOW_REPLACE); + } + + SetOrigin (posi, true); + floorz = parent.floorz; + ceilingz = parent.ceilingz; + } } // The translucent trail behind SorcFX2 ------------------------------------- @@ -271,9 +572,6 @@ class SorcFX3 : Actor SeeSound "SorcererBishopSpawn"; } - native void A_SpawnBishop(); - native void A_SorcererBishopEntry(); - States { Spawn: @@ -287,6 +585,45 @@ class SorcFX3 : Actor BISH G 3 A_SpawnBishop; Stop; } + + //============================================================================ + // + // A_SorcererBishopEntry + // + //============================================================================ + + void A_SorcererBishopEntry() + { + Spawn("SorcFX3Explosion", Pos, ALLOW_REPLACE); + A_PlaySound (SeeSound, CHAN_VOICE); + } + + //============================================================================ + // + // A_SpawnBishop + // + // Green spell - spawn bishops + // + //============================================================================ + + void A_SpawnBishop() + { + Actor mo = Spawn("Bishop", Pos, ALLOW_REPLACE); + if (mo) + { + if (!mo.TestMobjLocation()) + { + mo.ClearCounters(); + mo.Destroy (); + } + else if (target != NULL) + { // [RH] Make the new bishops inherit the Heriarch's target + mo.CopyFriendliness (target, true); + mo.master = target; + } + } + Destroy (); + } } @@ -326,8 +663,6 @@ class SorcFX4 : Actor DeathSound "SorcererBallExplode"; } - native void A_SorcFX4Check(); - States { Spawn: @@ -339,6 +674,22 @@ class SorcFX4 : Actor SBS4 FGH 2 Bright; Stop; } + + //============================================================================ + // + // A_SorcFX4Check + // + // FX4 - rapid fire balls + // + //============================================================================ + + void A_SorcFX4Check() + { + if (special2-- <= 0) + { + SetStateLabel ("Death"); + } + } }