mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-16 01:02:03 +00:00
- scriptified Korax.
This commit is contained in:
parent
4fcb397346
commit
659a592f16
4 changed files with 413 additions and 519 deletions
|
@ -867,7 +867,6 @@ set( NOT_COMPILED_SOURCE_FILES
|
||||||
g_hexen/a_heresiarch.cpp
|
g_hexen/a_heresiarch.cpp
|
||||||
g_hexen/a_hexenspecialdecs.cpp
|
g_hexen/a_hexenspecialdecs.cpp
|
||||||
g_hexen/a_iceguy.cpp
|
g_hexen/a_iceguy.cpp
|
||||||
g_hexen/a_korax.cpp
|
|
||||||
g_hexen/a_magecone.cpp
|
g_hexen/a_magecone.cpp
|
||||||
g_hexen/a_magelightning.cpp
|
g_hexen/a_magelightning.cpp
|
||||||
g_hexen/a_magestaff.cpp
|
g_hexen/a_magestaff.cpp
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
#include "a_heresiarch.cpp"
|
#include "a_heresiarch.cpp"
|
||||||
#include "a_hexenspecialdecs.cpp"
|
#include "a_hexenspecialdecs.cpp"
|
||||||
#include "a_iceguy.cpp"
|
#include "a_iceguy.cpp"
|
||||||
#include "a_korax.cpp"
|
|
||||||
#include "a_magecone.cpp"
|
#include "a_magecone.cpp"
|
||||||
#include "a_magelightning.cpp"
|
#include "a_magelightning.cpp"
|
||||||
#include "a_magestaff.cpp"
|
#include "a_magestaff.cpp"
|
||||||
|
|
|
@ -1,502 +0,0 @@
|
||||||
//===========================================================================
|
|
||||||
// Korax Variables
|
|
||||||
// tracer last teleport destination
|
|
||||||
// special2 set if "below half" script not yet run
|
|
||||||
//
|
|
||||||
// Korax Scripts (reserved)
|
|
||||||
// 249 Tell scripts that we are below half health
|
|
||||||
// 250-254 Control scripts (254 is only used when less than half health)
|
|
||||||
// 255 Death script
|
|
||||||
//
|
|
||||||
// Korax TIDs (reserved)
|
|
||||||
// 245 Reserved for Korax himself
|
|
||||||
// 248 Initial teleport destination
|
|
||||||
// 249 Teleport destination
|
|
||||||
// 250-254 For use in respective control scripts
|
|
||||||
// 255 For use in death script (spawn spots)
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "actor.h"
|
|
||||||
#include "info.h"
|
|
||||||
#include "p_local.h"
|
|
||||||
#include "p_spec.h"
|
|
||||||
#include "s_sound.h"
|
|
||||||
#include "a_action.h"
|
|
||||||
#include "m_random.h"
|
|
||||||
#include "i_system.h"
|
|
||||||
#include "vm.h"
|
|
||||||
#include "g_level.h"
|
|
||||||
*/
|
|
||||||
|
|
||||||
const int KORAX_SPIRIT_LIFETIME = 5*TICRATE/5; // 5 seconds
|
|
||||||
const int KORAX_COMMAND_HEIGHT = 120;
|
|
||||||
const int KORAX_COMMAND_OFFSET = 27;
|
|
||||||
|
|
||||||
const int KORAX_TID = 245;
|
|
||||||
const int KORAX_FIRST_TELEPORT_TID = 248;
|
|
||||||
const int KORAX_TELEPORT_TID = 249;
|
|
||||||
|
|
||||||
const int KORAX_DELTAANGLE = 85;
|
|
||||||
const int KORAX_ARM_EXTENSION_SHORT = 40;
|
|
||||||
const int KORAX_ARM_EXTENSION_LONG = 55;
|
|
||||||
|
|
||||||
const int KORAX_ARM1_HEIGHT = 108;
|
|
||||||
const int KORAX_ARM2_HEIGHT = 82;
|
|
||||||
const int KORAX_ARM3_HEIGHT = 54;
|
|
||||||
const int KORAX_ARM4_HEIGHT = 104;
|
|
||||||
const int KORAX_ARM5_HEIGHT = 86;
|
|
||||||
const int KORAX_ARM6_HEIGHT = 53;
|
|
||||||
|
|
||||||
const double KORAX_BOLT_HEIGHT = 48.;
|
|
||||||
const int KORAX_BOLT_LIFETIME = 3;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static FRandom pr_koraxchase ("KoraxChase");
|
|
||||||
static FRandom pr_kspiritinit ("KSpiritInit");
|
|
||||||
static FRandom pr_koraxdecide ("KoraxDecide");
|
|
||||||
static FRandom pr_koraxmissile ("KoraxMissile");
|
|
||||||
static FRandom pr_koraxcommand ("KoraxCommand");
|
|
||||||
static FRandom pr_kspiritweave ("KSpiritWeave");
|
|
||||||
static FRandom pr_kspiritseek ("KSpiritSeek");
|
|
||||||
static FRandom pr_kspiritroam ("KSpiritRoam");
|
|
||||||
static FRandom pr_kmissile ("SKoraxMissile");
|
|
||||||
|
|
||||||
void A_KoraxChase (AActor *);
|
|
||||||
void A_KoraxStep (AActor *);
|
|
||||||
void A_KoraxStep2 (AActor *);
|
|
||||||
void A_KoraxDecide (AActor *);
|
|
||||||
void A_KoraxBonePop (AActor *);
|
|
||||||
void A_KoraxMissile (AActor *);
|
|
||||||
void A_KoraxCommand (AActor *);
|
|
||||||
void A_KSpiritRoam (AActor *);
|
|
||||||
void A_KBolt (AActor *);
|
|
||||||
void A_KBoltRaise (AActor *);
|
|
||||||
|
|
||||||
void KoraxFire (AActor *actor, PClassActor *type, int arm);
|
|
||||||
void KSpiritInit (AActor *spirit, AActor *korax);
|
|
||||||
AActor *P_SpawnKoraxMissile (const DVector3 &pos, AActor *source, AActor *dest, PClassActor *type);
|
|
||||||
|
|
||||||
extern void SpawnSpiritTail (AActor *spirit);
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KoraxChase
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KoraxChase)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
AActor *spot;
|
|
||||||
|
|
||||||
if ((!self->special2) && (self->health <= (self->SpawnHealth()/2)))
|
|
||||||
{
|
|
||||||
FActorIterator iterator (KORAX_FIRST_TELEPORT_TID);
|
|
||||||
spot = iterator.Next ();
|
|
||||||
if (spot != NULL)
|
|
||||||
{
|
|
||||||
P_Teleport (self, spot->PosAtZ(ONFLOORZ), spot->Angles.Yaw, TELF_SOURCEFOG | TELF_DESTFOG);
|
|
||||||
}
|
|
||||||
|
|
||||||
P_StartScript (self, NULL, 249, NULL, NULL, 0, 0);
|
|
||||||
self->special2 = 1; // Don't run again
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->target == NULL)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (pr_koraxchase()<30)
|
|
||||||
{
|
|
||||||
self->SetState (self->MissileState);
|
|
||||||
}
|
|
||||||
else if (pr_koraxchase()<30)
|
|
||||||
{
|
|
||||||
S_Sound (self, CHAN_VOICE, "KoraxActive", 1, ATTN_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Teleport away
|
|
||||||
if (self->health < (self->SpawnHealth()>>1))
|
|
||||||
{
|
|
||||||
if (pr_koraxchase()<10)
|
|
||||||
{
|
|
||||||
FActorIterator iterator (KORAX_TELEPORT_TID);
|
|
||||||
|
|
||||||
if (self->tracer != NULL)
|
|
||||||
{ // Find the previous teleport destination
|
|
||||||
do
|
|
||||||
{
|
|
||||||
spot = iterator.Next ();
|
|
||||||
} while (spot != NULL && spot != self->tracer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next teleport destination
|
|
||||||
spot = iterator.Next ();
|
|
||||||
self->tracer = spot;
|
|
||||||
if (spot)
|
|
||||||
{
|
|
||||||
P_Teleport (self, spot->PosAtZ(ONFLOORZ), spot->Angles.Yaw, TELF_SOURCEFOG | TELF_DESTFOG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KoraxBonePop
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KoraxBonePop)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
AActor *mo;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Spawn 6 spirits equalangularly
|
|
||||||
for (i = 0; i < 6; ++i)
|
|
||||||
{
|
|
||||||
mo = P_SpawnMissileAngle (self, PClass::FindActor("KoraxSpirit"), DAngle(60.*i), 5.);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
KSpiritInit (mo, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
P_StartScript (self, NULL, 255, NULL, NULL, 0, 0); // Death script
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// KSpiritInit
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void KSpiritInit (AActor *spirit, AActor *korax)
|
|
||||||
{
|
|
||||||
spirit->health = KORAX_SPIRIT_LIFETIME;
|
|
||||||
|
|
||||||
spirit->tracer = korax; // Swarm around korax
|
|
||||||
spirit->WeaveIndexZ = 32 + (pr_kspiritinit() & 7); // Float bob index
|
|
||||||
spirit->args[0] = 10; // initial turn value
|
|
||||||
spirit->args[1] = 0; // initial look angle
|
|
||||||
|
|
||||||
#if 0 // Temporarily deactivated.
|
|
||||||
// Spawn a tail for spirit
|
|
||||||
HolyTail.SpawnSpiritTail (spirit);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KoraxDecide
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KoraxDecide)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
if (pr_koraxdecide()<220)
|
|
||||||
{
|
|
||||||
self->SetState (self->FindState("Attack"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->SetState (self->FindState("Command"));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KoraxMissile
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KoraxMissile)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
static const struct { const char *type, *sound; } choices[6] =
|
|
||||||
{
|
|
||||||
{ "WraithFX1", "WraithMissileFire" },
|
|
||||||
{ "Demon1FX1", "DemonMissileFire" },
|
|
||||||
{ "Demon2FX1", "DemonMissileFire" },
|
|
||||||
{ "FireDemonMissile", "FireDemonAttack" },
|
|
||||||
{ "CentaurFX", "CentaurLeaderAttack" },
|
|
||||||
{ "SerpentFX", "CentaurLeaderAttack" }
|
|
||||||
};
|
|
||||||
|
|
||||||
int type = pr_koraxmissile() % 6;
|
|
||||||
int i;
|
|
||||||
PClassActor *info;
|
|
||||||
|
|
||||||
S_Sound(self, CHAN_VOICE, "KoraxAttack", 1, ATTN_NORM);
|
|
||||||
|
|
||||||
info = PClass::FindActor(choices[type].type);
|
|
||||||
if (info == NULL)
|
|
||||||
{
|
|
||||||
I_Error("Unknown Korax missile: %s\n", choices[type].type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire all 6 missiles at once
|
|
||||||
S_Sound(self, CHAN_WEAPON, choices[type].sound, 1, ATTN_NONE);
|
|
||||||
for (i = 0; i < 6; ++i)
|
|
||||||
{
|
|
||||||
KoraxFire(self, info, i);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KoraxCommand
|
|
||||||
//
|
|
||||||
// Call action code scripts (250-254)
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KoraxCommand)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
DAngle ang;
|
|
||||||
int numcommands;
|
|
||||||
|
|
||||||
S_Sound (self, CHAN_VOICE, "KoraxCommand", 1, ATTN_NORM);
|
|
||||||
|
|
||||||
// Shoot stream of lightning to ceiling
|
|
||||||
ang = self->Angles.Yaw - 90;
|
|
||||||
DVector3 pos = self->Vec3Angle(KORAX_COMMAND_OFFSET, ang, KORAX_COMMAND_HEIGHT);
|
|
||||||
Spawn("KoraxBolt", pos, ALLOW_REPLACE);
|
|
||||||
|
|
||||||
if (self->health <= (self->SpawnHealth() >> 1))
|
|
||||||
{
|
|
||||||
numcommands = 5;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
numcommands = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
P_StartScript (self, NULL, 250+(pr_koraxcommand()%numcommands), NULL, NULL, 0, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// KoraxFire
|
|
||||||
//
|
|
||||||
// Arm projectiles
|
|
||||||
// arm positions numbered:
|
|
||||||
// 1 top left
|
|
||||||
// 2 middle left
|
|
||||||
// 3 lower left
|
|
||||||
// 4 top right
|
|
||||||
// 5 middle right
|
|
||||||
// 6 lower right
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
void KoraxFire (AActor *actor, PClassActor *type, int arm)
|
|
||||||
{
|
|
||||||
static const int extension[6] =
|
|
||||||
{
|
|
||||||
KORAX_ARM_EXTENSION_SHORT,
|
|
||||||
KORAX_ARM_EXTENSION_LONG,
|
|
||||||
KORAX_ARM_EXTENSION_LONG,
|
|
||||||
KORAX_ARM_EXTENSION_SHORT,
|
|
||||||
KORAX_ARM_EXTENSION_LONG,
|
|
||||||
KORAX_ARM_EXTENSION_LONG
|
|
||||||
};
|
|
||||||
static const int armheight[6] =
|
|
||||||
{
|
|
||||||
KORAX_ARM1_HEIGHT,
|
|
||||||
KORAX_ARM2_HEIGHT,
|
|
||||||
KORAX_ARM3_HEIGHT,
|
|
||||||
KORAX_ARM4_HEIGHT,
|
|
||||||
KORAX_ARM5_HEIGHT,
|
|
||||||
KORAX_ARM6_HEIGHT
|
|
||||||
};
|
|
||||||
|
|
||||||
DAngle ang = actor->Angles.Yaw + (arm < 3 ? -KORAX_DELTAANGLE : KORAX_DELTAANGLE);
|
|
||||||
DVector3 pos = actor->Vec3Angle(extension[arm], ang, armheight[arm] - actor->Floorclip);
|
|
||||||
P_SpawnKoraxMissile (pos, actor, actor->target, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KSpiritSeeker
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
static void A_KSpiritSeeker (AActor *actor, DAngle thresh, DAngle turnMax)
|
|
||||||
{
|
|
||||||
int dir;
|
|
||||||
DAngle delta;
|
|
||||||
AActor *target;
|
|
||||||
double newZ;
|
|
||||||
double deltaZ;
|
|
||||||
|
|
||||||
target = actor->tracer;
|
|
||||||
if (target == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dir = P_FaceMobj (actor, target, &delta);
|
|
||||||
if (delta > thresh)
|
|
||||||
{
|
|
||||||
delta /= 2;
|
|
||||||
if(delta > turnMax)
|
|
||||||
{
|
|
||||||
delta = turnMax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(dir)
|
|
||||||
{ // Turn clockwise
|
|
||||||
actor->Angles.Yaw += delta;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // Turn counter clockwise
|
|
||||||
actor->Angles.Yaw -= delta;
|
|
||||||
}
|
|
||||||
actor->VelFromAngle();
|
|
||||||
|
|
||||||
if (!(level.time&15)
|
|
||||||
|| actor->Z() > target->Z() + target->GetDefault()->Height
|
|
||||||
|| actor->Top() < target->Z())
|
|
||||||
{
|
|
||||||
newZ = target->Z() + pr_kspiritseek() * target->GetDefault()->Height / 256;
|
|
||||||
deltaZ = newZ-actor->Z();
|
|
||||||
|
|
||||||
if (fabs(deltaZ) > 15)
|
|
||||||
{
|
|
||||||
if(deltaZ > 0)
|
|
||||||
{
|
|
||||||
deltaZ = 15;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deltaZ = -15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actor->Vel.Z = deltaZ + actor->DistanceBySpeed(target, actor->Speed);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KSpiritRoam
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KSpiritRoam)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
if (self->health-- <= 0)
|
|
||||||
{
|
|
||||||
S_Sound (self, CHAN_VOICE, "SpiritDie", 1, ATTN_NORM);
|
|
||||||
self->SetState (self->FindState("Death"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (self->tracer)
|
|
||||||
{
|
|
||||||
A_KSpiritSeeker(self, (double)self->args[0], self->args[0] * 2.);
|
|
||||||
}
|
|
||||||
int xyspeed = (pr_kspiritweave() % 5);
|
|
||||||
int zspeed = (pr_kspiritweave() % 5);
|
|
||||||
A_Weave(self, xyspeed, zspeed, 4., 2.);
|
|
||||||
|
|
||||||
if (pr_kspiritroam()<50)
|
|
||||||
{
|
|
||||||
S_Sound (self, CHAN_VOICE, "SpiritActive", 1, ATTN_NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KBolt
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KBolt)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
// Countdown lifetime
|
|
||||||
if (self->special1-- <= 0)
|
|
||||||
{
|
|
||||||
self->Destroy ();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_KBoltRaise
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_KBoltRaise)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
AActor *mo;
|
|
||||||
|
|
||||||
// Spawn a child upward
|
|
||||||
double z = self->Z() + KORAX_BOLT_HEIGHT;
|
|
||||||
|
|
||||||
if ((z + KORAX_BOLT_HEIGHT) < self->ceilingz)
|
|
||||||
{
|
|
||||||
mo = Spawn("KoraxBolt", self->PosAtZ(z), ALLOW_REPLACE);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->special1 = KORAX_BOLT_LIFETIME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Maybe cap it off here
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// P_SpawnKoraxMissile
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
AActor *P_SpawnKoraxMissile (const DVector3 &pos, AActor *source, AActor *dest, PClassActor *type)
|
|
||||||
{
|
|
||||||
AActor *th;
|
|
||||||
DAngle an;
|
|
||||||
double dist;
|
|
||||||
|
|
||||||
th = Spawn (type, pos, ALLOW_REPLACE);
|
|
||||||
th->target = source; // Originator
|
|
||||||
an = th->AngleTo(dest);
|
|
||||||
if (dest->flags & MF_SHADOW)
|
|
||||||
{ // Invisible target
|
|
||||||
an += pr_kmissile.Random2() * (45/256.);
|
|
||||||
}
|
|
||||||
th->Angles.Yaw = an;
|
|
||||||
th->VelFromAngle();
|
|
||||||
dist = dest->DistanceBySpeed(th, th->Speed);
|
|
||||||
th->Vel.Z = (dest->Z() - pos.Z + 30) / dist;
|
|
||||||
return (P_CheckMissileSpawn(th, source->radius) ? th : NULL);
|
|
||||||
}
|
|
|
@ -1,5 +1,43 @@
|
||||||
|
//===========================================================================
|
||||||
|
// Korax Variables
|
||||||
|
// tracer last teleport destination
|
||||||
|
// special2 set if "below half" script not yet run
|
||||||
|
//
|
||||||
|
// Korax Scripts (reserved)
|
||||||
|
// 249 Tell scripts that we are below half health
|
||||||
|
// 250-254 Control scripts (254 is only used when less than half health)
|
||||||
|
// 255 Death script
|
||||||
|
//
|
||||||
|
// Korax TIDs (reserved)
|
||||||
|
// 245 Reserved for Korax himself
|
||||||
|
// 248 Initial teleport destination
|
||||||
|
// 249 Teleport destination
|
||||||
|
// 250-254 For use in respective control scripts
|
||||||
|
// 255 For use in death script (spawn spots)
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
class Korax : Actor
|
class Korax : Actor
|
||||||
{
|
{
|
||||||
|
const KORAX_ARM_EXTENSION_SHORT = 40;
|
||||||
|
const KORAX_ARM_EXTENSION_LONG = 55;
|
||||||
|
|
||||||
|
const KORAX_ARM1_HEIGHT = 108;
|
||||||
|
const KORAX_ARM2_HEIGHT = 82;
|
||||||
|
const KORAX_ARM3_HEIGHT = 54;
|
||||||
|
const KORAX_ARM4_HEIGHT = 104;
|
||||||
|
const KORAX_ARM5_HEIGHT = 86;
|
||||||
|
const KORAX_ARM6_HEIGHT = 53;
|
||||||
|
|
||||||
|
const KORAX_FIRST_TELEPORT_TID = 248;
|
||||||
|
const KORAX_TELEPORT_TID = 249;
|
||||||
|
|
||||||
|
const KORAX_DELTAANGLE = 85;
|
||||||
|
|
||||||
|
const KORAX_COMMAND_HEIGHT = 120;
|
||||||
|
const KORAX_COMMAND_OFFSET = 27;
|
||||||
|
|
||||||
|
const KORAX_SPIRIT_LIFETIME = 5*TICRATE/5; // 5 seconds
|
||||||
|
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
Health 5000;
|
Health 5000;
|
||||||
|
@ -24,12 +62,6 @@ class Korax : Actor
|
||||||
Obituary "$OB_KORAX";
|
Obituary "$OB_KORAX";
|
||||||
}
|
}
|
||||||
|
|
||||||
native void A_KoraxChase();
|
|
||||||
native void A_KoraxDecide();
|
|
||||||
native void A_KoraxBonePop();
|
|
||||||
native void A_KoraxMissile();
|
|
||||||
native void A_KoraxCommand();
|
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
Spawn:
|
Spawn:
|
||||||
|
@ -39,13 +71,11 @@ class Korax : Actor
|
||||||
KORX AAA 3 A_KoraxChase;
|
KORX AAA 3 A_KoraxChase;
|
||||||
KORX B 3 A_Chase;
|
KORX B 3 A_Chase;
|
||||||
KORX BBB 3 A_KoraxChase;
|
KORX BBB 3 A_KoraxChase;
|
||||||
KORX C 0 A_PlaySound("KoraxStep");
|
KORX C 3 A_KoraxStep;
|
||||||
KORX C 3 A_Chase;
|
|
||||||
KORX CCC 3 A_KoraxChase;
|
KORX CCC 3 A_KoraxChase;
|
||||||
KORX D 3 A_Chase;
|
KORX D 3 A_Chase;
|
||||||
KORX DDD 3 A_KoraxChase;
|
KORX DDD 3 A_KoraxChase;
|
||||||
KORX A 0 A_PlaySound("KoraxStep");
|
KORX A 3 A_KoraxStep;
|
||||||
KORX A 3 A_Chase;
|
|
||||||
Loop;
|
Loop;
|
||||||
Pain:
|
Pain:
|
||||||
KORX H 5 A_Pain;
|
KORX H 5 A_Pain;
|
||||||
|
@ -79,6 +109,255 @@ class Korax : Actor
|
||||||
KORX E 5 Bright;
|
KORX E 5 Bright;
|
||||||
Goto See;
|
Goto See;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void A_KoraxStep()
|
||||||
|
{
|
||||||
|
A_PlaySound("KoraxStep");
|
||||||
|
A_Chase();
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KoraxChase
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
void A_KoraxChase()
|
||||||
|
{
|
||||||
|
if ((!special2) && (health <= (SpawnHealth()/2)))
|
||||||
|
{
|
||||||
|
ActorIterator it = ActorIterator.Create(KORAX_FIRST_TELEPORT_TID);
|
||||||
|
Actor spot = it.Next ();
|
||||||
|
if (spot != null)
|
||||||
|
{
|
||||||
|
Teleport ((spot.pos.xy, ONFLOORZ), spot.angle, TELF_SOURCEFOG | TELF_DESTFOG);
|
||||||
|
}
|
||||||
|
ACS_Execute(249, 0);
|
||||||
|
special2 = 1; // Don't run again
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (random[KoraxChase]() < 30)
|
||||||
|
{
|
||||||
|
SetState (MissileState);
|
||||||
|
}
|
||||||
|
else if (random[KoraxChase]() < 30)
|
||||||
|
{
|
||||||
|
A_PlaySound("KoraxActive", CHAN_VOICE, 1, false, ATTN_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teleport away
|
||||||
|
if (health < (SpawnHealth() >> 1))
|
||||||
|
{
|
||||||
|
if (random[KoraxChase]() < 10)
|
||||||
|
{
|
||||||
|
ActorIterator it = ActorIterator.Create(KORAX_TELEPORT_TID);
|
||||||
|
Actor spot;
|
||||||
|
|
||||||
|
if (tracer != null)
|
||||||
|
{ // Find the previous teleport destination
|
||||||
|
do
|
||||||
|
{
|
||||||
|
spot = it.Next ();
|
||||||
|
} while (spot != null && spot != tracer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next teleport destination
|
||||||
|
spot = it.Next ();
|
||||||
|
tracer = spot;
|
||||||
|
if (spot)
|
||||||
|
{
|
||||||
|
Teleport ((spot.pos.xy, ONFLOORZ), spot.angle, TELF_SOURCEFOG | TELF_DESTFOG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KoraxDecide
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KoraxDecide()
|
||||||
|
{
|
||||||
|
if (random[KoraxDecide]() < 220)
|
||||||
|
{
|
||||||
|
SetStateLabel ("Attack");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetStateLabel ("Command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KoraxBonePop
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KoraxBonePop()
|
||||||
|
{
|
||||||
|
// Spawn 6 spirits equalangularly
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
{
|
||||||
|
Actor mo = SpawnMissileAngle ("KoraxSpirit", 60.*i, 5.);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
KSpiritInit (mo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ACS_Execute(255, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// KSpiritInit
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
private void KSpiritInit (Actor spirit)
|
||||||
|
{
|
||||||
|
spirit.health = KORAX_SPIRIT_LIFETIME;
|
||||||
|
|
||||||
|
spirit.tracer = self; // Swarm around korax
|
||||||
|
spirit.WeaveIndexZ = 32 + (random[Kspiritnit]() & 7); // Float bob index
|
||||||
|
spirit.args[0] = 10; // initial turn value
|
||||||
|
spirit.args[1] = 0; // initial look angle
|
||||||
|
|
||||||
|
// Spawn a tail for spirit
|
||||||
|
HolyTail.SpawnSpiritTail (spirit);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KoraxMissile
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KoraxMissile()
|
||||||
|
{
|
||||||
|
static const class<Actor> choices[] =
|
||||||
|
{
|
||||||
|
"WraithFX1", "Demon1FX1", "Demon2FX1", "FireDemonMissile", "CentaurFX", "SerpentFX"
|
||||||
|
};
|
||||||
|
static const sound sounds[] =
|
||||||
|
{
|
||||||
|
"WraithMissileFire", "DemonMissileFire", "DemonMissileFire", "FireDemonAttack", "CentaurLeaderAttack", "SerpentLeaderAttack"
|
||||||
|
};
|
||||||
|
int type = random[KoraxMissile]() % 6;
|
||||||
|
|
||||||
|
A_PlaySound("KoraxAttack", CHAN_VOICE);
|
||||||
|
|
||||||
|
// Fire all 6 missiles at once
|
||||||
|
A_PlaySound(sounds[type], CHAN_WEAPON, 1, false, ATTN_NONE);
|
||||||
|
class<Actor> info = choices[type];
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
{
|
||||||
|
KoraxFire(info, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// KoraxFire
|
||||||
|
//
|
||||||
|
// Arm projectiles
|
||||||
|
// arm positions numbered:
|
||||||
|
// 1 top left
|
||||||
|
// 2 middle left
|
||||||
|
// 3 lower left
|
||||||
|
// 4 top right
|
||||||
|
// 5 middle right
|
||||||
|
// 6 lower right
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void KoraxFire (Class<Actor> type, int arm)
|
||||||
|
{
|
||||||
|
static const int extension[] =
|
||||||
|
{
|
||||||
|
KORAX_ARM_EXTENSION_SHORT,
|
||||||
|
KORAX_ARM_EXTENSION_LONG,
|
||||||
|
KORAX_ARM_EXTENSION_LONG,
|
||||||
|
KORAX_ARM_EXTENSION_SHORT,
|
||||||
|
KORAX_ARM_EXTENSION_LONG,
|
||||||
|
KORAX_ARM_EXTENSION_LONG
|
||||||
|
};
|
||||||
|
static const int armheight[] =
|
||||||
|
{
|
||||||
|
KORAX_ARM1_HEIGHT,
|
||||||
|
KORAX_ARM2_HEIGHT,
|
||||||
|
KORAX_ARM3_HEIGHT,
|
||||||
|
KORAX_ARM4_HEIGHT,
|
||||||
|
KORAX_ARM5_HEIGHT,
|
||||||
|
KORAX_ARM6_HEIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
double ang = angle + (arm < 3 ? -KORAX_DELTAANGLE : KORAX_DELTAANGLE);
|
||||||
|
Vector3 pos = Vec3Angle(extension[arm], ang, armheight[arm] - Floorclip);
|
||||||
|
SpawnKoraxMissile (pos, target, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// P_SpawnKoraxMissile
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
private void SpawnKoraxMissile (Vector3 pos, Actor dest, Class<Actor> type)
|
||||||
|
{
|
||||||
|
Actor th = Spawn (type, pos, ALLOW_REPLACE);
|
||||||
|
th.target = self; // Originator
|
||||||
|
double an = th.AngleTo(dest);
|
||||||
|
if (dest.bShadow)
|
||||||
|
{ // Invisible target
|
||||||
|
an += Random2[KoraxMissile]() * (45/256.);
|
||||||
|
}
|
||||||
|
th.angle = an;
|
||||||
|
th.VelFromAngle();
|
||||||
|
double dist = dest.DistanceBySpeed(th, th.Speed);
|
||||||
|
th.Vel.Z = (dest.pos.z - pos.Z + 30) / dist;
|
||||||
|
th.CheckMissileSpawn(radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KoraxCommand
|
||||||
|
//
|
||||||
|
// Call action code scripts (250-254)
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KoraxCommand()
|
||||||
|
{
|
||||||
|
int numcommands;
|
||||||
|
|
||||||
|
A_PlaySound("KoraxCommand", CHAN_VOICE);
|
||||||
|
|
||||||
|
// Shoot stream of lightning to ceiling
|
||||||
|
double ang = angle - 90;
|
||||||
|
Vector3 pos = Vec3Angle(KORAX_COMMAND_OFFSET, ang, KORAX_COMMAND_HEIGHT);
|
||||||
|
Spawn("KoraxBolt", pos, ALLOW_REPLACE);
|
||||||
|
|
||||||
|
if (health <= (SpawnHealth() >> 1))
|
||||||
|
{
|
||||||
|
numcommands = 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
numcommands = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
ACS_Execute(250 + (random[KoraxCommand]()%numcommands), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KoraxSpirit : Actor
|
class KoraxSpirit : Actor
|
||||||
|
@ -94,8 +373,6 @@ class KoraxSpirit : Actor
|
||||||
Alpha 0.4;
|
Alpha 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
native void A_KSpiritRoam();
|
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
Spawn:
|
Spawn:
|
||||||
|
@ -105,10 +382,98 @@ class KoraxSpirit : Actor
|
||||||
SPIR DEFGHI 5;
|
SPIR DEFGHI 5;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KSpiritSeeker
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
private void KSpiritSeeker (double thresh, double turnMax)
|
||||||
|
{
|
||||||
|
Actor target = tracer;
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double dir = deltaangle(angle, AngleTo(target));
|
||||||
|
double delta = abs(dir);
|
||||||
|
if (delta > thresh)
|
||||||
|
{
|
||||||
|
delta /= 2;
|
||||||
|
if(delta > turnMax)
|
||||||
|
{
|
||||||
|
delta = turnMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(dir > 0)
|
||||||
|
{ // Turn clockwise
|
||||||
|
angle += delta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Turn counter clockwise
|
||||||
|
angle -= delta;
|
||||||
|
}
|
||||||
|
VelFromAngle();
|
||||||
|
|
||||||
|
if (!(level.time&15)
|
||||||
|
|| pos.z > target.pos.z + target.Default.Height
|
||||||
|
|| pos.z + height < target.pos.z)
|
||||||
|
{
|
||||||
|
double newZ = target.pos.z + random[KoraxRoam]() * target.Default.Height / 256;
|
||||||
|
double deltaZ = newZ - pos.z;
|
||||||
|
|
||||||
|
if (abs(deltaZ) > 15)
|
||||||
|
{
|
||||||
|
if(deltaZ > 0)
|
||||||
|
{
|
||||||
|
deltaZ = 15;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deltaZ = -15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vel.Z = deltaZ + DistanceBySpeed(target, Speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KSpiritRoam
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KSpiritRoam()
|
||||||
|
{
|
||||||
|
if (health-- <= 0)
|
||||||
|
{
|
||||||
|
A_PlaySound("SpiritDie", CHAN_VOICE);
|
||||||
|
SetStateLabel ("Death");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tracer)
|
||||||
|
{
|
||||||
|
KSpiritSeeker(args[0], args[0] * 2.);
|
||||||
|
}
|
||||||
|
int xyspeed = (random[KoraxRoam]() % 5);
|
||||||
|
int zspeed = (random[KoraxRoam]() % 5);
|
||||||
|
A_Weave(xyspeed, zspeed, 4., 2.);
|
||||||
|
|
||||||
|
if (random[KoraxRoam]() < 50)
|
||||||
|
{
|
||||||
|
A_PlaySound("SpiritActive", CHAN_VOICE, 1, false, ATTN_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KoraxBolt : Actor
|
class KoraxBolt : Actor
|
||||||
{
|
{
|
||||||
|
const KORAX_BOLT_HEIGHT = 48.;
|
||||||
|
const KORAX_BOLT_LIFETIME = 3;
|
||||||
|
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
Radius 15;
|
Radius 15;
|
||||||
|
@ -119,9 +484,6 @@ class KoraxBolt : Actor
|
||||||
RenderStyle "Add";
|
RenderStyle "Add";
|
||||||
}
|
}
|
||||||
|
|
||||||
native void A_KBolt();
|
|
||||||
native void A_KBoltRaise();
|
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
Spawn:
|
Spawn:
|
||||||
|
@ -130,4 +492,40 @@ class KoraxBolt : Actor
|
||||||
MLFX IJKLM 2 Bright A_KBolt;
|
MLFX IJKLM 2 Bright A_KBolt;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KBolt
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KBolt()
|
||||||
|
{
|
||||||
|
// Countdown lifetime
|
||||||
|
if (special1-- <= 0)
|
||||||
|
{
|
||||||
|
Destroy ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_KBoltRaise
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_KBoltRaise()
|
||||||
|
{
|
||||||
|
// Spawn a child upward
|
||||||
|
double z = pos.z + KORAX_BOLT_HEIGHT;
|
||||||
|
|
||||||
|
if ((z + KORAX_BOLT_HEIGHT) < ceilingz)
|
||||||
|
{
|
||||||
|
Actor mo = Spawn("KoraxBolt", (pos.xy, z), ALLOW_REPLACE);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.special1 = KORAX_BOLT_LIFETIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue