mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-07 09:01:57 +00:00
- scriptified the Heresiarch.
This commit is contained in:
parent
f5b3429274
commit
f17f6c30c2
7 changed files with 461 additions and 596 deletions
|
@ -859,7 +859,6 @@ set( NOT_COMPILED_SOURCE_FILES
|
||||||
${OTHER_SYSTEM_SOURCES}
|
${OTHER_SYSTEM_SOURCES}
|
||||||
sc_man_scanner.h
|
sc_man_scanner.h
|
||||||
sc_man_scanner.re
|
sc_man_scanner.re
|
||||||
g_hexen/a_heresiarch.cpp
|
|
||||||
g_hexen/a_spike.cpp
|
g_hexen/a_spike.cpp
|
||||||
g_shared/sbarinfo_commands.cpp
|
g_shared/sbarinfo_commands.cpp
|
||||||
xlat/xlat_parser.y
|
xlat/xlat_parser.y
|
||||||
|
|
|
@ -1,584 +0,0 @@
|
||||||
/*
|
|
||||||
#include "actor.h"
|
|
||||||
#include "info.h"
|
|
||||||
#include "p_local.h"
|
|
||||||
#include "s_sound.h"
|
|
||||||
#include "a_action.h"
|
|
||||||
#include "m_random.h"
|
|
||||||
#include "a_hexenglobal.h"
|
|
||||||
#include "i_system.h"
|
|
||||||
#include "vm.h"
|
|
||||||
#include "g_level.h"
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define SORCBALL_INITIAL_SPEED 7
|
|
||||||
#define SORCBALL_TERMINAL_SPEED 25
|
|
||||||
#define SORCBALL_SPEED_ROTATIONS 5
|
|
||||||
#define SORC_DEFENSE_TIME 255
|
|
||||||
#define SORC_DEFENSE_HEIGHT 45
|
|
||||||
#define BOUNCE_TIME_UNIT (35/2)
|
|
||||||
#define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds
|
|
||||||
#define SORCFX4_SPREAD_ANGLE 20
|
|
||||||
|
|
||||||
#define SORC_DECELERATE 0
|
|
||||||
#define SORC_ACCELERATE 1
|
|
||||||
#define SORC_STOPPING 2
|
|
||||||
#define SORC_FIRESPELL 3
|
|
||||||
#define SORC_STOPPED 4
|
|
||||||
#define SORC_NORMAL 5
|
|
||||||
#define SORC_FIRING_SPELL 6
|
|
||||||
|
|
||||||
#define BALL1_ANGLEOFFSET 0.
|
|
||||||
#define BALL2_ANGLEOFFSET 120.
|
|
||||||
#define BALL3_ANGLEOFFSET 240.
|
|
||||||
|
|
||||||
void A_SlowBalls (AActor *actor);
|
|
||||||
void A_StopBalls (AActor *actor);
|
|
||||||
void A_AccelBalls (AActor *actor);
|
|
||||||
void A_DecelBalls (AActor *actor);
|
|
||||||
void A_SorcOffense2 (AActor *actor);
|
|
||||||
void A_DoBounceCheck (AActor *actor, const char *sound);
|
|
||||||
|
|
||||||
static FRandom pr_heresiarch ("Heresiarch");
|
|
||||||
|
|
||||||
// The Heresiarch him/itself ------------------------------------------------
|
|
||||||
|
|
||||||
class AHeresiarch : public AActor
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (AHeresiarch, AActor)
|
|
||||||
public:
|
|
||||||
PClassActor *StopBall;
|
|
||||||
DAngle BallAngle;
|
|
||||||
|
|
||||||
|
|
||||||
void Serialize(FSerializer &arc);
|
|
||||||
void Die (AActor *source, AActor *inflictor, int dmgflags);
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(AHeresiarch, false, false)
|
|
||||||
|
|
||||||
void AHeresiarch::Serialize(FSerializer &arc)
|
|
||||||
{
|
|
||||||
Super::Serialize (arc);
|
|
||||||
arc("stopball", StopBall)
|
|
||||||
("ballangle", BallAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AHeresiarch::Die (AActor *source, AActor *inflictor, int dmgflags)
|
|
||||||
{
|
|
||||||
// The heresiarch just executes a script instead of a special upon death
|
|
||||||
int script = special;
|
|
||||||
special = 0;
|
|
||||||
|
|
||||||
Super::Die (source, inflictor, dmgflags);
|
|
||||||
|
|
||||||
if (script != 0)
|
|
||||||
{
|
|
||||||
P_StartScript (this, NULL, script, level.MapName, NULL, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base class for the balls flying around the Heresiarch's head -------------
|
|
||||||
|
|
||||||
class ASorcBall : public AActor
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ASorcBall, AActor)
|
|
||||||
public:
|
|
||||||
virtual void DoFireSpell ();
|
|
||||||
virtual void SorcUpdateBallAngle ();
|
|
||||||
virtual void CastSorcererSpell ();
|
|
||||||
DAngle AngleOffset;
|
|
||||||
DAngle OldAngle;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Serialize(FSerializer &arc)
|
|
||||||
{
|
|
||||||
Super::Serialize (arc);
|
|
||||||
arc("angleoffset", AngleOffset)
|
|
||||||
("oldangle", OldAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpecialBlastHandling (AActor *source, double strength)
|
|
||||||
{ // don't blast sorcerer balls
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ASorcBall, false, false)
|
|
||||||
|
|
||||||
// First ball (purple) - fires projectiles ----------------------------------
|
|
||||||
|
|
||||||
class ASorcBall1 : public ASorcBall
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ASorcBall1, ASorcBall)
|
|
||||||
public:
|
|
||||||
void BeginPlay ()
|
|
||||||
{
|
|
||||||
Super::BeginPlay ();
|
|
||||||
AngleOffset = BALL1_ANGLEOFFSET;
|
|
||||||
}
|
|
||||||
virtual void DoFireSpell ();
|
|
||||||
virtual void SorcUpdateBallAngle ();
|
|
||||||
virtual void CastSorcererSpell ();
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ASorcBall1, false, false)
|
|
||||||
|
|
||||||
// Second ball (blue) - generates the shield --------------------------------
|
|
||||||
|
|
||||||
class ASorcBall2 : public ASorcBall
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ASorcBall2, ASorcBall)
|
|
||||||
public:
|
|
||||||
void BeginPlay ()
|
|
||||||
{
|
|
||||||
Super::BeginPlay ();
|
|
||||||
AngleOffset = BALL2_ANGLEOFFSET;
|
|
||||||
}
|
|
||||||
virtual void CastSorcererSpell ();
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ASorcBall2, false, false)
|
|
||||||
|
|
||||||
// Third ball (green) - summons Bishops -------------------------------------
|
|
||||||
|
|
||||||
class ASorcBall3 : public ASorcBall
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ASorcBall3, ASorcBall)
|
|
||||||
public:
|
|
||||||
void BeginPlay ()
|
|
||||||
{
|
|
||||||
Super::BeginPlay ();
|
|
||||||
AngleOffset = BALL3_ANGLEOFFSET;
|
|
||||||
}
|
|
||||||
virtual void CastSorcererSpell ();
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ASorcBall3, false, false)
|
|
||||||
|
|
||||||
// Sorcerer spell 1 (The burning, bouncing head thing) ----------------------
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// SorcBall::DoFireSpell
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall::DoFireSpell ()
|
|
||||||
{
|
|
||||||
CastSorcererSpell ();
|
|
||||||
target->args[3] = SORC_STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// SorcBall1::DoFireSpell
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall1::DoFireSpell ()
|
|
||||||
{
|
|
||||||
if (pr_heresiarch() < 200)
|
|
||||||
{
|
|
||||||
S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE);
|
|
||||||
special2 = SORCFX4_RAPIDFIRE_TIME;
|
|
||||||
args[4] = 128;
|
|
||||||
target->args[3] = SORC_FIRING_SPELL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Super::DoFireSpell ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_FIELD(AHeresiarch, BallAngle);
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_SorcBallOrbit
|
|
||||||
//
|
|
||||||
// - actor is ball
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_SorcBallOrbit)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(ASorcBall);
|
|
||||||
|
|
||||||
// [RH] If no parent, then die instead of crashing
|
|
||||||
if (self->target == NULL)
|
|
||||||
{
|
|
||||||
self->SetState (self->FindState(NAME_Pain));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mode = self->target->args[3];
|
|
||||||
AHeresiarch *parent = barrier_cast<AHeresiarch *>(self->target);
|
|
||||||
double dist = parent->radius - (self->radius*2);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// This cannot happen anymore because this is defined locally in SorcBall
|
|
||||||
if (!self->IsKindOf (RUNTIME_CLASS(ASorcBall)))
|
|
||||||
{
|
|
||||||
I_Error ("Corrupted sorcerer:\nTried to use a %s", self->GetClass()->TypeName.GetChars());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (self->target->health <= 0)
|
|
||||||
{
|
|
||||||
self->SetState (self->FindState(NAME_Pain));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DAngle prevangle = self->OldAngle;
|
|
||||||
DAngle baseangle = parent->BallAngle;
|
|
||||||
DAngle angle = baseangle + self->AngleOffset;
|
|
||||||
|
|
||||||
self->Angles.Yaw = angle;
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case SORC_NORMAL: // Balls rotating normally
|
|
||||||
self->SorcUpdateBallAngle ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_DECELERATE: // Balls decelerating
|
|
||||||
A_DecelBalls(self);
|
|
||||||
self->SorcUpdateBallAngle ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_ACCELERATE: // Balls accelerating
|
|
||||||
A_AccelBalls(self);
|
|
||||||
self->SorcUpdateBallAngle ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_STOPPING: // Balls stopping
|
|
||||||
if ((parent->StopBall == self->GetClass()) &&
|
|
||||||
(parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
|
|
||||||
absangle(angle, parent->Angles.Yaw) < 42.1875)
|
|
||||||
{
|
|
||||||
// Can stop now
|
|
||||||
self->target->args[3] = SORC_FIRESPELL;
|
|
||||||
self->target->args[4] = 0;
|
|
||||||
// Set angle so self angle == sorcerer angle
|
|
||||||
parent->BallAngle = parent->Angles.Yaw - self->AngleOffset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->SorcUpdateBallAngle ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_FIRESPELL: // Casting spell
|
|
||||||
if (parent->StopBall == self->GetClass())
|
|
||||||
{
|
|
||||||
// Put sorcerer into special throw spell anim
|
|
||||||
if (parent->health > 0)
|
|
||||||
parent->SetState (parent->FindState("Attack1"));
|
|
||||||
|
|
||||||
self->DoFireSpell ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_FIRING_SPELL:
|
|
||||||
if (parent->StopBall == self->GetClass())
|
|
||||||
{
|
|
||||||
if (self->special2-- <= 0)
|
|
||||||
{
|
|
||||||
// Done rapid firing
|
|
||||||
parent->args[3] = SORC_STOPPED;
|
|
||||||
// Back to orbit balls
|
|
||||||
if (parent->health > 0)
|
|
||||||
parent->SetState (parent->FindState("Attack2"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Do rapid fire spell
|
|
||||||
A_SorcOffense2(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SORC_STOPPED: // Balls stopped
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( angle.BAMs() < prevangle.BAMs() && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
|
|
||||||
{
|
|
||||||
parent->args[1]++; // Bump rotation counter
|
|
||||||
// Completed full rotation - make woosh sound
|
|
||||||
S_Sound (self, CHAN_BODY, "SorcererBallWoosh", 1, ATTN_NORM);
|
|
||||||
}
|
|
||||||
self->OldAngle = angle; // Set previous angle
|
|
||||||
|
|
||||||
DVector3 pos = parent->Vec3Angle(dist, angle, -parent->Floorclip + parent->Height);
|
|
||||||
self->SetOrigin (pos, true);
|
|
||||||
self->floorz = parent->floorz;
|
|
||||||
self->ceilingz = parent->ceilingz;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_StopBalls
|
|
||||||
//
|
|
||||||
// Instant stop when rotation gets to ball in special2
|
|
||||||
// self is sorcerer
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void A_StopBalls(AActor *scary)
|
|
||||||
{
|
|
||||||
AHeresiarch *self = static_cast<AHeresiarch *> (scary);
|
|
||||||
int chance = pr_heresiarch();
|
|
||||||
self->args[3] = SORC_STOPPING; // stopping mode
|
|
||||||
self->args[1] = 0; // Reset rotation counter
|
|
||||||
|
|
||||||
if ((self->args[0] <= 0) && (chance < 200))
|
|
||||||
{
|
|
||||||
self->StopBall = RUNTIME_CLASS(ASorcBall2); // Blue
|
|
||||||
}
|
|
||||||
else if((self->health < (self->SpawnHealth() >> 1)) &&
|
|
||||||
(chance < 200))
|
|
||||||
{
|
|
||||||
self->StopBall = RUNTIME_CLASS(ASorcBall3); // Green
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->StopBall = RUNTIME_CLASS(ASorcBall1); // Yellow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_AccelBalls
|
|
||||||
//
|
|
||||||
// Increase ball orbit speed - actor is ball
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void A_AccelBalls(AActor *self)
|
|
||||||
{
|
|
||||||
AActor *sorc = self->target;
|
|
||||||
|
|
||||||
if (sorc->args[4] < sorc->args[2])
|
|
||||||
{
|
|
||||||
sorc->args[4]++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sorc->args[3] = SORC_NORMAL;
|
|
||||||
if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
|
|
||||||
{
|
|
||||||
// Reached terminal velocity - stop balls
|
|
||||||
A_StopBalls(sorc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_DecelBalls
|
|
||||||
//
|
|
||||||
// Decrease ball orbit speed - actor is ball
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void A_DecelBalls(AActor *self)
|
|
||||||
{
|
|
||||||
AActor *sorc = self->target;
|
|
||||||
|
|
||||||
if (sorc->args[4] > sorc->args[2])
|
|
||||||
{
|
|
||||||
sorc->args[4]--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sorc->args[3] = SORC_NORMAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ASorcBall1::SorcUpdateBallAngle
|
|
||||||
//
|
|
||||||
// Update angle if first ball
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall1::SorcUpdateBallAngle ()
|
|
||||||
{
|
|
||||||
barrier_cast<AHeresiarch*>(target)->BallAngle += target->args[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ASorcBall::SorcUpdateBallAngle
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall::SorcUpdateBallAngle ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ASorcBall::CastSorcererSpell
|
|
||||||
//
|
|
||||||
// Make noise and change the parent sorcerer's animation
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall::CastSorcererSpell ()
|
|
||||||
{
|
|
||||||
S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE);
|
|
||||||
|
|
||||||
// Put sorcerer into throw spell animation
|
|
||||||
if (target->health > 0)
|
|
||||||
target->SetState (target->FindState("Attack2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ASorcBall2::CastSorcererSpell
|
|
||||||
//
|
|
||||||
// Defensive
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall2::CastSorcererSpell ()
|
|
||||||
{
|
|
||||||
Super::CastSorcererSpell ();
|
|
||||||
|
|
||||||
AActor *parent = target;
|
|
||||||
AActor *mo;
|
|
||||||
|
|
||||||
mo = Spawn("SorcFX2", PosPlusZ(parent->Floorclip + SORC_DEFENSE_HEIGHT), ALLOW_REPLACE);
|
|
||||||
parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
|
|
||||||
parent->args[0] = SORC_DEFENSE_TIME;
|
|
||||||
if (mo) mo->target = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ASorcBall3::CastSorcererSpell
|
|
||||||
//
|
|
||||||
// Reinforcements
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall3::CastSorcererSpell ()
|
|
||||||
{
|
|
||||||
Super::CastSorcererSpell ();
|
|
||||||
|
|
||||||
AActor *mo;
|
|
||||||
DAngle ang1, ang2;
|
|
||||||
AActor *parent = target;
|
|
||||||
|
|
||||||
ang1 = Angles.Yaw.Degrees - 45;
|
|
||||||
ang2 = Angles.Yaw.Degrees + 45;
|
|
||||||
PClassActor *cls = PClass::FindActor("SorcFX3");
|
|
||||||
if (health < (SpawnHealth()/3))
|
|
||||||
{ // Spawn 2 at a time
|
|
||||||
mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
|
|
||||||
if (mo) mo->target = parent;
|
|
||||||
mo = P_SpawnMissileAngle(parent, cls, ang2, 4.);
|
|
||||||
if (mo) mo->target = parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (pr_heresiarch() < 128)
|
|
||||||
ang1 = ang2;
|
|
||||||
mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
|
|
||||||
if (mo) mo->target = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
void A_SpawnReinforcements(AActor *actor)
|
|
||||||
{
|
|
||||||
AActor *parent = self->target;
|
|
||||||
AActor *mo;
|
|
||||||
DAngle ang;
|
|
||||||
|
|
||||||
ang = P_Random();
|
|
||||||
mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5.);
|
|
||||||
if (mo) mo->target = parent;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// SorcBall1::CastSorcererSpell
|
|
||||||
//
|
|
||||||
// Offensive
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void ASorcBall1::CastSorcererSpell ()
|
|
||||||
{
|
|
||||||
Super::CastSorcererSpell ();
|
|
||||||
|
|
||||||
AActor *mo;
|
|
||||||
DAngle ang1, ang2;
|
|
||||||
AActor *parent = target;
|
|
||||||
|
|
||||||
ang1 = Angles.Yaw.Degrees + 70;
|
|
||||||
ang2 = Angles.Yaw.Degrees - 70;
|
|
||||||
PClassActor *cls = PClass::FindActor("SorcFX1");
|
|
||||||
mo = P_SpawnMissileAngle (parent, cls, ang1, 0);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->target = parent;
|
|
||||||
mo->tracer = parent->target;
|
|
||||||
mo->args[4] = BOUNCE_TIME_UNIT;
|
|
||||||
mo->args[3] = 15; // Bounce time in seconds
|
|
||||||
}
|
|
||||||
mo = P_SpawnMissileAngle (parent, cls, ang2, 0);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->target = parent;
|
|
||||||
mo->tracer = parent->target;
|
|
||||||
mo->args[4] = BOUNCE_TIME_UNIT;
|
|
||||||
mo->args[3] = 15; // Bounce time in seconds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_SorcOffense2
|
|
||||||
//
|
|
||||||
// Actor is ball
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void A_SorcOffense2(AActor *self)
|
|
||||||
{
|
|
||||||
DAngle ang1;
|
|
||||||
AActor *mo;
|
|
||||||
double delta;
|
|
||||||
int index;
|
|
||||||
AActor *parent = self->target;
|
|
||||||
AActor *dest = parent->target;
|
|
||||||
double dist;
|
|
||||||
|
|
||||||
// [RH] If no enemy, then don't try to shoot.
|
|
||||||
if (dest == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = self->args[4];
|
|
||||||
self->args[4] = (self->args[4] + 15) & 255;
|
|
||||||
delta = DAngle(index * (360 / 256.f)).Sin() * SORCFX4_SPREAD_ANGLE;
|
|
||||||
|
|
||||||
ang1 = self->Angles.Yaw + delta;
|
|
||||||
mo = P_SpawnMissileAngle(parent, PClass::FindActor("SorcFX4"), ang1, 0);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->special2 = 35*5/2; // 5 seconds
|
|
||||||
dist = mo->DistanceBySpeed(dest, mo->Speed);
|
|
||||||
mo->Vel.Z = (dest->Z() - mo->Z()) / dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,5 +24,4 @@
|
||||||
#include "a_pickups.h"
|
#include "a_pickups.h"
|
||||||
|
|
||||||
// Include all the Hexen stuff here to reduce compile time
|
// Include all the Hexen stuff here to reduce compile time
|
||||||
#include "a_heresiarch.cpp"
|
|
||||||
#include "a_spike.cpp"
|
#include "a_spike.cpp"
|
||||||
|
|
|
@ -842,6 +842,13 @@ DEFINE_ACTION_FUNCTION(DObject, GameType)
|
||||||
ACTION_RETURN_INT(gameinfo.gametype);
|
ACTION_RETURN_INT(gameinfo.gametype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(DObject, BAM)
|
||||||
|
{
|
||||||
|
PARAM_PROLOGUE;
|
||||||
|
PARAM_FLOAT(ang);
|
||||||
|
ACTION_RETURN_INT(DAngle(ang).BAMs());
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(FStringTable, Localize)
|
DEFINE_ACTION_FUNCTION(FStringTable, Localize)
|
||||||
{
|
{
|
||||||
PARAM_PROLOGUE;
|
PARAM_PROLOGUE;
|
||||||
|
@ -856,4 +863,5 @@ DEFINE_ACTION_FUNCTION(FString, Replace)
|
||||||
PARAM_STRING(s2);
|
PARAM_STRING(s2);
|
||||||
self->Substitute(*s1, *s2);
|
self->Substitute(*s1, *s2);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2285,6 +2285,25 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags);
|
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags);
|
||||||
c->Type()->Symbols.ReplaceSymbol(sym);
|
c->Type()->Symbols.ReplaceSymbol(sym);
|
||||||
|
|
||||||
|
auto cls = dyn_cast<PClass>(c->Type());
|
||||||
|
PFunction *virtsym = nullptr;
|
||||||
|
if (cls != nullptr && cls->ParentClass != nullptr) virtsym = dyn_cast<PFunction>(cls->ParentClass->Symbols.FindSymbol(FName(f->Name), true));
|
||||||
|
unsigned vindex = ~0u;
|
||||||
|
if (virtsym != nullptr) vindex = virtsym->Variants[0].Implementation->VirtualIndex;
|
||||||
|
|
||||||
|
if (vindex != ~0u || (varflags & VARF_Virtual))
|
||||||
|
{
|
||||||
|
// Todo: Check if the declaration is legal.
|
||||||
|
|
||||||
|
// First step: compare prototypes - if they do not match the virtual base method does not apply.
|
||||||
|
|
||||||
|
// Second step: Check flags. Possible cases:
|
||||||
|
// 1. Base method is final: Error.
|
||||||
|
// 2. This method is override: Base virtual method must exist
|
||||||
|
// 3. This method is virtual but not override: Base may not have a virtual method with the same prototype.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!(f->Flags & ZCC_Native))
|
if (!(f->Flags & ZCC_Native))
|
||||||
{
|
{
|
||||||
if (f->Body == nullptr)
|
if (f->Body == nullptr)
|
||||||
|
|
|
@ -10,6 +10,7 @@ class Object native
|
||||||
native static int GameType();
|
native static int GameType();
|
||||||
native static void S_Sound (Sound sound_id, int channel, float volume = 1, float attenuation = ATTN_NORM);
|
native static void S_Sound (Sound sound_id, int channel, float volume = 1, float attenuation = ATTN_NORM);
|
||||||
native static void C_MidPrint(string fontname, string textlabel, bool bold = false); // always uses the stringtable.
|
native static void C_MidPrint(string fontname, string textlabel, bool bold = false); // always uses the stringtable.
|
||||||
|
native static uint BAM(double angle);
|
||||||
|
|
||||||
/*virtual*/ native void Destroy();
|
/*virtual*/ native void Destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
// special2 Countdown of rapid fire (FX4)
|
// special2 Countdown of rapid fire (FX4)
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
class Heresiarch native
|
class Heresiarch : Actor
|
||||||
{
|
{
|
||||||
|
|
||||||
const SORCBALL_INITIAL_SPEED = 7;
|
const SORCBALL_INITIAL_SPEED = 7;
|
||||||
|
@ -45,7 +45,8 @@ class Heresiarch native
|
||||||
const BALL2_ANGLEOFFSET = 120.;
|
const BALL2_ANGLEOFFSET = 120.;
|
||||||
const BALL3_ANGLEOFFSET = 240.;
|
const BALL3_ANGLEOFFSET = 240.;
|
||||||
|
|
||||||
native double BallAngle;
|
double BallAngle;
|
||||||
|
class<SorcBall> StopBall;
|
||||||
|
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
|
@ -110,7 +111,50 @@ class Heresiarch native
|
||||||
SORC Z -1 Bright;
|
SORC Z -1 Bright;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Die (Actor source, Actor inflictor, int dmgflags)
|
||||||
|
{
|
||||||
|
// The heresiarch just executes a script instead of a special upon death
|
||||||
|
int script = special;
|
||||||
|
special = 0;
|
||||||
|
|
||||||
|
Super.Die (source, inflictor, dmgflags);
|
||||||
|
|
||||||
|
if (script != 0)
|
||||||
|
{
|
||||||
|
ACS_Execute(script, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_StopBalls
|
||||||
|
//
|
||||||
|
// Instant stop when rotation gets to ball in special2
|
||||||
|
// self is sorcerer
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_StopBalls()
|
||||||
|
{
|
||||||
|
int chance = random[Heresiarch]();
|
||||||
|
args[3] = SORC_STOPPING; // stopping mode
|
||||||
|
args[1] = 0; // Reset rotation counter
|
||||||
|
|
||||||
|
if ((args[0] <= 0) && (chance < 200))
|
||||||
|
{
|
||||||
|
StopBall = "SorcBall2"; // Blue
|
||||||
|
}
|
||||||
|
else if((health < (SpawnHealth() >> 1)) && (chance < 200))
|
||||||
|
{
|
||||||
|
StopBall = "SorcBall3"; // Green
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StopBall = "SorcBall1"; // Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
//
|
//
|
||||||
// A_SorcSpinBalls
|
// A_SorcSpinBalls
|
||||||
|
@ -213,7 +257,7 @@ class Heresiarch native
|
||||||
|
|
||||||
// Base class for the balls flying around the Heresiarch's head -------------
|
// Base class for the balls flying around the Heresiarch's head -------------
|
||||||
|
|
||||||
class SorcBall native
|
class SorcBall : Actor
|
||||||
{
|
{
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
|
@ -231,8 +275,239 @@ class SorcBall native
|
||||||
DeathSound "SorcererBigBallExplode";
|
DeathSound "SorcererBigBallExplode";
|
||||||
}
|
}
|
||||||
|
|
||||||
native void A_SorcBallOrbit();
|
double OldAngle, AngleOffset;
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SorcBall::DoFireSpell
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
virtual void DoFireSpell ()
|
||||||
|
{
|
||||||
|
CastSorcererSpell ();
|
||||||
|
target.args[3] = Heresiarch.SORC_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void SorcUpdateBallAngle ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
override bool SpecialBlastHandling (Actor source, double strength)
|
||||||
|
{ // don't blast sorcerer balls
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// ASorcBall::CastSorcererSpell
|
||||||
|
//
|
||||||
|
// Make noise and change the parent sorcerer's animation
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
virtual void CastSorcererSpell ()
|
||||||
|
{
|
||||||
|
target.A_PlaySound ("SorcererSpellCast", CHAN_VOICE);
|
||||||
|
|
||||||
|
// Put sorcerer into throw spell animation
|
||||||
|
if (target.health > 0)
|
||||||
|
target.SetStateLabel ("Attack2");
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_SorcBallOrbit
|
||||||
|
//
|
||||||
|
// - actor is ball
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_SorcBallOrbit()
|
||||||
|
{
|
||||||
|
// [RH] If no parent, then die instead of crashing
|
||||||
|
if (target == null || target.health <= 0)
|
||||||
|
{
|
||||||
|
SetStateLabel ("Pain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mode = target.args[3];
|
||||||
|
Heresiarch parent = Heresiarch(target);
|
||||||
|
double dist = parent.radius - (radius*2);
|
||||||
|
|
||||||
|
double prevangle = OldAngle;
|
||||||
|
double baseangle = parent.BallAngle;
|
||||||
|
double curangle = baseangle + AngleOffset;
|
||||||
|
|
||||||
|
angle = curangle;
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case Heresiarch.SORC_NORMAL: // Balls rotating normally
|
||||||
|
SorcUpdateBallAngle ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Heresiarch.SORC_DECELERATE: // Balls decelerating
|
||||||
|
A_DecelBalls();
|
||||||
|
SorcUpdateBallAngle ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Heresiarch.SORC_ACCELERATE: // Balls accelerating
|
||||||
|
A_AccelBalls();
|
||||||
|
SorcUpdateBallAngle ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Heresiarch.SORC_STOPPING: // Balls stopping
|
||||||
|
if ((parent.StopBall == GetClass()) &&
|
||||||
|
(parent.args[1] > Heresiarch.SORCBALL_SPEED_ROTATIONS) &&
|
||||||
|
absangle(curangle, parent.angle) < 42.1875)
|
||||||
|
{
|
||||||
|
// Can stop now
|
||||||
|
target.args[3] = Heresiarch.SORC_FIRESPELL;
|
||||||
|
target.args[4] = 0;
|
||||||
|
// Set angle so self angle == sorcerer angle
|
||||||
|
parent.BallAngle = parent.angle - AngleOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SorcUpdateBallAngle ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Heresiarch.SORC_FIRESPELL: // Casting spell
|
||||||
|
if (parent.StopBall == GetClass())
|
||||||
|
{
|
||||||
|
// Put sorcerer into special throw spell anim
|
||||||
|
if (parent.health > 0)
|
||||||
|
parent.SetStateLabel("Attack1");
|
||||||
|
|
||||||
|
DoFireSpell ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Heresiarch.SORC_FIRING_SPELL:
|
||||||
|
if (parent.StopBall == GetClass())
|
||||||
|
{
|
||||||
|
if (special2-- <= 0)
|
||||||
|
{
|
||||||
|
// Done rapid firing
|
||||||
|
parent.args[3] = Heresiarch.SORC_STOPPED;
|
||||||
|
// Back to orbit balls
|
||||||
|
if (parent.health > 0)
|
||||||
|
parent.SetStateLabel("Attack2");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Do rapid fire spell
|
||||||
|
A_SorcOffense2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The comparison here depends on binary angle semantics and cannot be done in floating point.
|
||||||
|
// It also requires very exact conversion that must be done natively.
|
||||||
|
if (BAM(curangle) < BAM(prevangle) && (parent.args[4] == Heresiarch.SORCBALL_TERMINAL_SPEED))
|
||||||
|
{
|
||||||
|
parent.args[1]++; // Bump rotation counter
|
||||||
|
// Completed full rotation - make woosh sound
|
||||||
|
A_PlaySound ("SorcererBallWoosh", CHAN_BODY);
|
||||||
|
}
|
||||||
|
OldAngle = curangle; // Set previous angle
|
||||||
|
|
||||||
|
Vector3 pos = parent.Vec3Angle(dist, curangle, -parent.Floorclip + parent.Height);
|
||||||
|
SetOrigin (pos, true);
|
||||||
|
floorz = parent.floorz;
|
||||||
|
ceilingz = parent.ceilingz;
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_SorcOffense2
|
||||||
|
//
|
||||||
|
// Actor is ball
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_SorcOffense2()
|
||||||
|
{
|
||||||
|
Actor parent = target;
|
||||||
|
Actor dest = parent.target;
|
||||||
|
|
||||||
|
// [RH] If no enemy, then don't try to shoot.
|
||||||
|
if (dest == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = args[4];
|
||||||
|
args[4] = (args[4] + 15) & 255;
|
||||||
|
double delta = sin(index * (360 / 256.f)) * Heresiarch.SORCFX4_SPREAD_ANGLE;
|
||||||
|
|
||||||
|
double ang1 = Angle + delta;
|
||||||
|
Actor mo = parent.SpawnMissileAngle("SorcFX4", ang1, 0);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.special2 = 35*5/2; // 5 seconds
|
||||||
|
double dist = mo.DistanceBySpeed(dest, mo.Speed);
|
||||||
|
mo.Vel.Z = (dest.pos.z - mo.pos.z) / dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_AccelBalls
|
||||||
|
//
|
||||||
|
// Increase ball orbit speed - actor is ball
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_AccelBalls()
|
||||||
|
{
|
||||||
|
Heresiarch sorc = Heresiarch(target);
|
||||||
|
|
||||||
|
if (sorc.args[4] < sorc.args[2])
|
||||||
|
{
|
||||||
|
sorc.args[4]++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sorc.args[3] = Heresiarch.SORC_NORMAL;
|
||||||
|
if (sorc.args[4] >= Heresiarch.SORCBALL_TERMINAL_SPEED)
|
||||||
|
{
|
||||||
|
// Reached terminal velocity - stop balls
|
||||||
|
sorc.A_StopBalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_DecelBalls
|
||||||
|
//
|
||||||
|
// Decrease ball orbit speed - actor is ball
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_DecelBalls()
|
||||||
|
{
|
||||||
|
Actor sorc = target;
|
||||||
|
|
||||||
|
if (sorc.args[4] > sorc.args[2])
|
||||||
|
{
|
||||||
|
sorc.args[4]--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sorc.args[3] = Heresiarch.SORC_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void A_SorcBallExplode()
|
void A_SorcBallExplode()
|
||||||
{
|
{
|
||||||
bNoBounceSound = true;
|
bNoBounceSound = true;
|
||||||
|
@ -286,7 +561,7 @@ class SorcBall native
|
||||||
|
|
||||||
// First ball (purple) - fires projectiles ----------------------------------
|
// First ball (purple) - fires projectiles ----------------------------------
|
||||||
|
|
||||||
class SorcBall1 : SorcBall native
|
class SorcBall1 : SorcBall
|
||||||
{
|
{
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
|
@ -303,12 +578,90 @@ class SorcBall1 : SorcBall native
|
||||||
SBS4 FGH 6;
|
SBS4 FGH 6;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void BeginPlay ()
|
||||||
|
{
|
||||||
|
Super.BeginPlay ();
|
||||||
|
AngleOffset = Heresiarch.BALL1_ANGLEOFFSET;
|
||||||
|
A_Log("Ball1 begins " .. AngleOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SorcBall1::CastSorcererSpell
|
||||||
|
//
|
||||||
|
// Offensive
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
override void CastSorcererSpell ()
|
||||||
|
{
|
||||||
|
Super.CastSorcererSpell ();
|
||||||
|
|
||||||
|
Actor parent = target;
|
||||||
|
|
||||||
|
double ang1 = Angle + 70;
|
||||||
|
double ang2 = Angle - 70;
|
||||||
|
Class<Actor> cls = "SorcFX1";
|
||||||
|
Actor mo = parent.SpawnMissileAngle (cls, ang1, 0);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.target = parent;
|
||||||
|
mo.tracer = parent.target;
|
||||||
|
mo.args[4] = Heresiarch.BOUNCE_TIME_UNIT;
|
||||||
|
mo.args[3] = 15; // Bounce time in seconds
|
||||||
|
}
|
||||||
|
mo = parent.SpawnMissileAngle (cls, ang2, 0);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.target = parent;
|
||||||
|
mo.tracer = parent.target;
|
||||||
|
mo.args[4] = Heresiarch.BOUNCE_TIME_UNIT;
|
||||||
|
mo.args[3] = 15; // Bounce time in seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// ASorcBall1::SorcUpdateBallAngle
|
||||||
|
//
|
||||||
|
// Update angle if first ball
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
override void SorcUpdateBallAngle ()
|
||||||
|
{
|
||||||
|
(Heresiarch(target)).BallAngle += target.args[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SorcBall1::DoFireSpell
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
override void DoFireSpell ()
|
||||||
|
{
|
||||||
|
if (random[Heresiarch]() < 200)
|
||||||
|
{
|
||||||
|
target.A_PlaySound ("SorcererSpellCast", CHAN_VOICE, 1, false, ATTN_NONE);
|
||||||
|
special2 = Heresiarch.SORCFX4_RAPIDFIRE_TIME;
|
||||||
|
args[4] = 128;
|
||||||
|
target.args[3] = Heresiarch.SORC_FIRING_SPELL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Super.DoFireSpell ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Second ball (blue) - generates the shield --------------------------------
|
// Second ball (blue) - generates the shield --------------------------------
|
||||||
|
|
||||||
class SorcBall2 : SorcBall native
|
class SorcBall2 : SorcBall
|
||||||
{
|
{
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
|
@ -325,11 +678,40 @@ class SorcBall2 : SorcBall native
|
||||||
SBS3 FGH 6;
|
SBS3 FGH 6;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void BeginPlay ()
|
||||||
|
{
|
||||||
|
Super.BeginPlay ();
|
||||||
|
AngleOffset = Heresiarch.BALL2_ANGLEOFFSET;
|
||||||
|
A_Log("Ball2 begins " .. AngleOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// ASorcBall2::CastSorcererSpell
|
||||||
|
//
|
||||||
|
// Defensive
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
override void CastSorcererSpell ()
|
||||||
|
{
|
||||||
|
Super.CastSorcererSpell ();
|
||||||
|
|
||||||
|
Actor parent = target;
|
||||||
|
Actor mo = Spawn("SorcFX2", Pos + (0, 0, parent.Floorclip + Heresiarch.SORC_DEFENSE_HEIGHT), ALLOW_REPLACE);
|
||||||
|
bReflective = true;
|
||||||
|
bInvulnerable = true;
|
||||||
|
parent.args[0] = Heresiarch.SORC_DEFENSE_TIME;
|
||||||
|
if (mo) mo.target = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third ball (green) - summons Bishops -------------------------------------
|
// Third ball (green) - summons Bishops -------------------------------------
|
||||||
|
|
||||||
class SorcBall3 : SorcBall native
|
class SorcBall3 : SorcBall
|
||||||
{
|
{
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
|
@ -346,6 +728,47 @@ class SorcBall3 : SorcBall native
|
||||||
SBS3 FGH 6;
|
SBS3 FGH 6;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void BeginPlay ()
|
||||||
|
{
|
||||||
|
Super.BeginPlay ();
|
||||||
|
AngleOffset = Heresiarch.BALL3_ANGLEOFFSET;
|
||||||
|
A_Log("Ball3 begins " .. AngleOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// ASorcBall3::CastSorcererSpell
|
||||||
|
//
|
||||||
|
// Reinforcements
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
override void CastSorcererSpell ()
|
||||||
|
{
|
||||||
|
Actor mo;
|
||||||
|
Super.CastSorcererSpell ();
|
||||||
|
Actor parent = target;
|
||||||
|
|
||||||
|
double ang1 = Angle - 45;
|
||||||
|
double ang2 = Angle + 45;
|
||||||
|
Class<Actor> cls = "SorcFX3";
|
||||||
|
if (health < (SpawnHealth()/3))
|
||||||
|
{ // Spawn 2 at a time
|
||||||
|
mo = parent.SpawnMissileAngle(cls, ang1, 4.);
|
||||||
|
if (mo) mo.target = parent;
|
||||||
|
mo = parent.SpawnMissileAngle(cls, ang2, 4.);
|
||||||
|
if (mo) mo.target = parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (random[Heresiarch]() < 128) ang1 = ang2;
|
||||||
|
mo = parent.SpawnMissileAngle(cls, ang1, 4.);
|
||||||
|
if (mo) mo.target = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -489,7 +912,7 @@ class SorcFX2 : Actor
|
||||||
Actor parent = target;
|
Actor parent = target;
|
||||||
|
|
||||||
// [RH] If no parent, then disappear
|
// [RH] If no parent, then disappear
|
||||||
if (parent == NULL)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
Destroy();
|
Destroy();
|
||||||
return;
|
return;
|
||||||
|
@ -616,7 +1039,7 @@ class SorcFX3 : Actor
|
||||||
mo.ClearCounters();
|
mo.ClearCounters();
|
||||||
mo.Destroy ();
|
mo.Destroy ();
|
||||||
}
|
}
|
||||||
else if (target != NULL)
|
else if (target != null)
|
||||||
{ // [RH] Make the new bishops inherit the Heriarch's target
|
{ // [RH] Make the new bishops inherit the Heriarch's target
|
||||||
mo.CopyFriendliness (target, true);
|
mo.CopyFriendliness (target, true);
|
||||||
mo.master = target;
|
mo.master = target;
|
||||||
|
|
Loading…
Reference in a new issue