From 659a592f16829ac7ec643af7a13ac0ad885ccc02 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Nov 2016 16:25:10 +0100 Subject: [PATCH] - scriptified Korax. --- src/CMakeLists.txt | 1 - src/g_hexen/a_hexenmisc.cpp | 1 - src/g_hexen/a_korax.cpp | 502 -------------------------- wadsrc/static/zscript/hexen/korax.txt | 428 +++++++++++++++++++++- 4 files changed, 413 insertions(+), 519 deletions(-) delete mode 100644 src/g_hexen/a_korax.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c1242853d..828c7fc34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -867,7 +867,6 @@ set( NOT_COMPILED_SOURCE_FILES g_hexen/a_heresiarch.cpp g_hexen/a_hexenspecialdecs.cpp g_hexen/a_iceguy.cpp - g_hexen/a_korax.cpp g_hexen/a_magecone.cpp g_hexen/a_magelightning.cpp g_hexen/a_magestaff.cpp diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 7bd38ceab..3fb334cb1 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -36,7 +36,6 @@ #include "a_heresiarch.cpp" #include "a_hexenspecialdecs.cpp" #include "a_iceguy.cpp" -#include "a_korax.cpp" #include "a_magecone.cpp" #include "a_magelightning.cpp" #include "a_magestaff.cpp" diff --git a/src/g_hexen/a_korax.cpp b/src/g_hexen/a_korax.cpp deleted file mode 100644 index 8a459ffd4..000000000 --- a/src/g_hexen/a_korax.cpp +++ /dev/null @@ -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); -} diff --git a/wadsrc/static/zscript/hexen/korax.txt b/wadsrc/static/zscript/hexen/korax.txt index 16bf172a4..1c1f31712 100644 --- a/wadsrc/static/zscript/hexen/korax.txt +++ b/wadsrc/static/zscript/hexen/korax.txt @@ -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 { + 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 { Health 5000; @@ -24,12 +62,6 @@ class Korax : Actor Obituary "$OB_KORAX"; } - native void A_KoraxChase(); - native void A_KoraxDecide(); - native void A_KoraxBonePop(); - native void A_KoraxMissile(); - native void A_KoraxCommand(); - States { Spawn: @@ -39,13 +71,11 @@ class Korax : Actor KORX AAA 3 A_KoraxChase; KORX B 3 A_Chase; KORX BBB 3 A_KoraxChase; - KORX C 0 A_PlaySound("KoraxStep"); - KORX C 3 A_Chase; + KORX C 3 A_KoraxStep; KORX CCC 3 A_KoraxChase; KORX D 3 A_Chase; KORX DDD 3 A_KoraxChase; - KORX A 0 A_PlaySound("KoraxStep"); - KORX A 3 A_Chase; + KORX A 3 A_KoraxStep; Loop; Pain: KORX H 5 A_Pain; @@ -79,6 +109,255 @@ class Korax : Actor KORX E 5 Bright; 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 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 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 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 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 @@ -94,8 +373,6 @@ class KoraxSpirit : Actor Alpha 0.4; } - native void A_KSpiritRoam(); - States { Spawn: @@ -105,10 +382,98 @@ class KoraxSpirit : Actor SPIR DEFGHI 5; 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 { + const KORAX_BOLT_HEIGHT = 48.; + const KORAX_BOLT_LIFETIME = 3; + Default { Radius 15; @@ -119,9 +484,6 @@ class KoraxBolt : Actor RenderStyle "Add"; } - native void A_KBolt(); - native void A_KBoltRaise(); - States { Spawn: @@ -130,4 +492,40 @@ class KoraxBolt : Actor MLFX IJKLM 2 Bright A_KBolt; 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; + } + } + } }