#include "actor.h" #include "gi.h" #include "m_random.h" #include "s_sound.h" #include "d_player.h" #include "a_action.h" #include "p_local.h" #include "p_enemy.h" #include "a_action.h" #include "p_pspr.h" #include "gstrings.h" #include "a_hexenglobal.h" #define ZAGSPEED FRACUNIT static FRandom pr_lightningready ("LightningReady"); static FRandom pr_lightningclip ("LightningClip"); static FRandom pr_zap ("LightningZap"); static FRandom pr_zapf ("LightningZapF"); static FRandom pr_hit ("LightningHit"); void A_LightningReady (AActor *actor); void A_MLightningAttack (AActor *actor); void A_LightningClip (AActor *); void A_LightningZap (AActor *); void A_ZapMimic (AActor *); void A_LastZap (AActor *); void A_LightningRemove (AActor *); void A_FreeTargMobj (AActor *); // The Mage's Lightning Arc of Death ---------------------------------------- class AMWeapLightning : public AMageWeapon { DECLARE_ACTOR (AMWeapLightning, AMageWeapon) }; FState AMWeapLightning::States[] = { #define S_MW_LIGHTNING1 0 S_BRIGHT (WMLG, 'A', 4, NULL , &States[S_MW_LIGHTNING1+1]), S_BRIGHT (WMLG, 'B', 4, NULL , &States[S_MW_LIGHTNING1+2]), S_BRIGHT (WMLG, 'C', 4, NULL , &States[S_MW_LIGHTNING1+3]), S_BRIGHT (WMLG, 'D', 4, NULL , &States[S_MW_LIGHTNING1+4]), S_BRIGHT (WMLG, 'E', 4, NULL , &States[S_MW_LIGHTNING1+5]), S_BRIGHT (WMLG, 'F', 4, NULL , &States[S_MW_LIGHTNING1+6]), S_BRIGHT (WMLG, 'G', 4, NULL , &States[S_MW_LIGHTNING1+7]), S_BRIGHT (WMLG, 'H', 4, NULL , &States[S_MW_LIGHTNING1]), #define S_MLIGHTNINGREADY (S_MW_LIGHTNING1+8) S_BRIGHT (MLNG, 'A', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+1]), S_BRIGHT (MLNG, 'A', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+2]), S_BRIGHT (MLNG, 'A', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+3]), S_BRIGHT (MLNG, 'A', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+4]), S_BRIGHT (MLNG, 'A', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+5]), S_BRIGHT (MLNG, 'A', 1, A_LightningReady , &States[S_MLIGHTNINGREADY+6]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+7]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+8]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+9]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+10]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+11]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+12]), S_BRIGHT (MLNG, 'C', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+13]), S_BRIGHT (MLNG, 'C', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+14]), S_BRIGHT (MLNG, 'C', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+15]), S_BRIGHT (MLNG, 'C', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+16]), S_BRIGHT (MLNG, 'C', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+17]), S_BRIGHT (MLNG, 'C', 1, A_LightningReady , &States[S_MLIGHTNINGREADY+18]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+19]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+20]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+21]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+22]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY+23]), S_BRIGHT (MLNG, 'B', 1, A_WeaponReady , &States[S_MLIGHTNINGREADY]), #define S_MLIGHTNINGDOWN (S_MLIGHTNINGREADY+24) S_BRIGHT (MLNG, 'A', 1, A_Lower , &States[S_MLIGHTNINGDOWN]), #define S_MLIGHTNINGUP (S_MLIGHTNINGDOWN+1) S_BRIGHT (MLNG, 'A', 1, A_Raise , &States[S_MLIGHTNINGUP]), #define S_MLIGHTNINGATK (S_MLIGHTNINGUP+1) S_BRIGHT (MLNG, 'D', 3, NULL , &States[S_MLIGHTNINGATK+1]), S_BRIGHT (MLNG, 'E', 3, NULL , &States[S_MLIGHTNINGATK+2]), S_BRIGHT (MLNG, 'F', 4, A_MLightningAttack , &States[S_MLIGHTNINGATK+3]), S_BRIGHT (MLNG, 'G', 4, NULL , &States[S_MLIGHTNINGATK+4]), S_BRIGHT (MLNG, 'H', 3, NULL , &States[S_MLIGHTNINGATK+5]), S_BRIGHT (MLNG, 'I', 3, NULL , &States[S_MLIGHTNINGATK+6]), S_BRIGHT2 (MLNG, 'I', 6, NULL , &States[S_MLIGHTNINGATK+7], 0, 199), S_BRIGHT2 (MLNG, 'C', 2, NULL , &States[S_MLIGHTNINGATK+8], 0, 55), S_BRIGHT2 (MLNG, 'B', 2, NULL , &States[S_MLIGHTNINGATK+9], 0, 50), S_BRIGHT2 (MLNG, 'B', 2, NULL , &States[S_MLIGHTNINGATK+10], 0, 45), S_BRIGHT2 (MLNG, 'B', 2, NULL , &States[S_MLIGHTNINGREADY], 0, 40), }; IMPLEMENT_ACTOR (AMWeapLightning, Hexen, 8040, 0) PROP_Flags (MF_SPECIAL|MF_NOGRAVITY) PROP_SpawnState (S_MW_LIGHTNING1) PROP_Weapon_SelectionOrder (1100) PROP_Weapon_AmmoUse1 (5) PROP_Weapon_AmmoGive1 (25) PROP_Weapon_UpState (S_MLIGHTNINGUP) PROP_Weapon_DownState (S_MLIGHTNINGDOWN) PROP_Weapon_ReadyState (S_MLIGHTNINGREADY) PROP_Weapon_AtkState (S_MLIGHTNINGATK) PROP_Weapon_HoldAtkState (S_MLIGHTNINGATK) PROP_Weapon_Kickback (0) PROP_Weapon_YAdjust (20) PROP_Weapon_MoveCombatDist (23000000) PROP_Weapon_AmmoType1 ("Mana2") PROP_Weapon_ProjectileType ("LightningFloor") PROP_Inventory_PickupMessage("$TXT_WEAPON_M3") END_DEFAULTS // Lightning ---------------------------------------------------------------- IMPLEMENT_ABSTRACT_ACTOR (ALightning) int ALightning::SpecialMissileHit (AActor *thing) { if (thing->flags&MF_SHOOTABLE && thing != target) { if (thing->Mass != INT_MAX) { thing->momx += momx>>4; thing->momy += momy>>4; } if ((!thing->player && !(thing->flags2&MF2_BOSS)) || !(level.time&1)) { if (thing->IsKindOf(RUNTIME_CLASS(ACentaur))) { // Lightning does more damage to centaurs P_DamageMobj(thing, this, target, 9, MOD_ELECTRIC); } else { P_DamageMobj(thing, this, target, 3, MOD_ELECTRIC); } if (!(S_IsActorPlayingSomething (this, CHAN_WEAPON))) { S_Sound (this, CHAN_WEAPON, "MageLightningZap", 1, ATTN_NORM); } if (thing->flags3&MF3_ISMONSTER && pr_hit() < 64) { thing->Howl (); } } health--; if (health <= 0 || thing->health <= 0) { return 0; } if (flags3 & MF3_FLOORHUGGER) { if (lastenemy && ! lastenemy->tracer) { lastenemy->tracer = thing; } } else if (!tracer) { tracer = thing; } } return 1; // lightning zaps through all sprites } // Ceiling Lightning -------------------------------------------------------- class ALightningCeiling : public ALightning { DECLARE_ACTOR (ALightningCeiling, ALightning) }; FState ALightningCeiling::States[] = { #define S_LIGHTNING_CEILING1 0 S_BRIGHT (MLFX, 'A', 2, A_LightningZap , &States[S_LIGHTNING_CEILING1+1]), S_BRIGHT (MLFX, 'B', 2, A_LightningClip , &States[S_LIGHTNING_CEILING1+2]), S_BRIGHT (MLFX, 'C', 2, A_LightningClip , &States[S_LIGHTNING_CEILING1+3]), S_BRIGHT (MLFX, 'D', 2, A_LightningClip , &States[S_LIGHTNING_CEILING1]), #define S_LIGHTNING_C_X1 (S_LIGHTNING_CEILING1+4) S_BRIGHT (MLF2, 'A', 2, A_LightningRemove , &States[S_LIGHTNING_C_X1+1]), S_BRIGHT (MLF2, 'B', 3, NULL , &States[S_LIGHTNING_C_X1+2]), S_BRIGHT (MLF2, 'C', 3, NULL , &States[S_LIGHTNING_C_X1+3]), S_BRIGHT (MLF2, 'D', 3, NULL , &States[S_LIGHTNING_C_X1+4]), S_BRIGHT (MLF2, 'E', 3, NULL , &States[S_LIGHTNING_C_X1+5]), S_BRIGHT (MLF2, 'K', 3, NULL , &States[S_LIGHTNING_C_X1+6]), S_BRIGHT (MLF2, 'L', 3, NULL , &States[S_LIGHTNING_C_X1+7]), S_BRIGHT (MLF2, 'M', 3, NULL , &States[S_LIGHTNING_C_X1+8]), S_NORMAL (ACLO, 'E', 35, NULL , &States[S_LIGHTNING_C_X1+9]), S_BRIGHT (MLF2, 'N', 3, NULL , &States[S_LIGHTNING_C_X1+10]), S_BRIGHT (MLF2, 'O', 3, NULL , &States[S_LIGHTNING_C_X1+11]), S_BRIGHT (MLF2, 'P', 4, NULL , &States[S_LIGHTNING_C_X1+12]), S_BRIGHT (MLF2, 'Q', 3, NULL , &States[S_LIGHTNING_C_X1+13]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_C_X1+14]), S_BRIGHT (MLF2, 'Q', 4, NULL , &States[S_LIGHTNING_C_X1+15]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_C_X1+16]), S_BRIGHT (MLF2, 'O', 3, NULL , &States[S_LIGHTNING_C_X1+17]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_C_X1+18]), S_BRIGHT (MLF2, 'P', 1, A_HideThing , &States[S_LIGHTNING_C_X1+19]), S_NORMAL (ACLO, 'E', 1050, A_FreeTargMobj , NULL), }; IMPLEMENT_ACTOR (ALightningCeiling, Hexen, -1, 0) PROP_SpawnHealth (144) PROP_SpeedFixed (25) PROP_RadiusFixed (16) PROP_HeightFixed (40) PROP_Damage (8) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE) PROP_Flags2 (MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS) PROP_Flags3 (MF3_CEILINGHUGGER) PROP_RenderStyle (STYLE_Add) PROP_SpawnState (S_LIGHTNING_CEILING1) PROP_DeathState (S_LIGHTNING_C_X1) END_DEFAULTS // Floor Lightning ---------------------------------------------------------- class ALightningFloor : public ALightning { DECLARE_ACTOR (ALightningFloor, ALightning) }; FState ALightningFloor::States[] = { #define S_LIGHTNING_FLOOR1 0 S_BRIGHT (MLFX, 'E', 2, A_LightningZap , &States[S_LIGHTNING_FLOOR1+1]), S_BRIGHT (MLFX, 'F', 2, A_LightningClip , &States[S_LIGHTNING_FLOOR1+2]), S_BRIGHT (MLFX, 'G', 2, A_LightningClip , &States[S_LIGHTNING_FLOOR1+3]), S_BRIGHT (MLFX, 'H', 2, A_LightningClip , &States[S_LIGHTNING_FLOOR1]), #define S_LIGHTNING_F_X1 (S_LIGHTNING_FLOOR1+4) S_BRIGHT (MLF2, 'F', 2, A_LightningRemove , &States[S_LIGHTNING_F_X1+1]), S_BRIGHT (MLF2, 'G', 3, NULL , &States[S_LIGHTNING_F_X1+2]), S_BRIGHT (MLF2, 'H', 3, NULL , &States[S_LIGHTNING_F_X1+3]), S_BRIGHT (MLF2, 'I', 3, NULL , &States[S_LIGHTNING_F_X1+4]), S_BRIGHT (MLF2, 'J', 3, NULL , &States[S_LIGHTNING_F_X1+5]), S_BRIGHT (MLF2, 'K', 3, NULL , &States[S_LIGHTNING_F_X1+6]), S_BRIGHT (MLF2, 'L', 3, NULL , &States[S_LIGHTNING_F_X1+7]), S_BRIGHT (MLF2, 'M', 3, NULL , &States[S_LIGHTNING_F_X1+8]), S_NORMAL (ACLO, 'E', 20, NULL , &States[S_LIGHTNING_F_X1+9]), S_BRIGHT (MLF2, 'N', 3, NULL , &States[S_LIGHTNING_F_X1+10]), S_BRIGHT (MLF2, 'O', 3, NULL , &States[S_LIGHTNING_F_X1+11]), S_BRIGHT (MLF2, 'P', 4, NULL , &States[S_LIGHTNING_F_X1+12]), S_BRIGHT (MLF2, 'Q', 3, NULL , &States[S_LIGHTNING_F_X1+13]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_F_X1+14]), S_BRIGHT (MLF2, 'Q', 4, A_LastZap , &States[S_LIGHTNING_F_X1+15]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_F_X1+16]), S_BRIGHT (MLF2, 'O', 3, NULL , &States[S_LIGHTNING_F_X1+17]), S_BRIGHT (MLF2, 'P', 3, NULL , &States[S_LIGHTNING_F_X1+18]), S_BRIGHT (MLF2, 'P', 1, A_HideThing , &ALightningCeiling::States[S_LIGHTNING_C_X1+19]), }; IMPLEMENT_ACTOR (ALightningFloor, Hexen, -1, 0) PROP_SpawnHealth (144) PROP_SpeedFixed (25) PROP_RadiusFixed (16) PROP_HeightFixed (40) PROP_Damage (8) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE) PROP_Flags2 (MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS) PROP_Flags3 (MF3_FLOORHUGGER) PROP_RenderStyle (STYLE_Add) PROP_SpawnState (S_LIGHTNING_FLOOR1) PROP_DeathState (S_LIGHTNING_F_X1) END_DEFAULTS // Lightning Zap ------------------------------------------------------------ class ALightningZap : public AActor { DECLARE_ACTOR (ALightningZap, AActor) public: int SpecialMissileHit (AActor *thing); }; FState ALightningZap::States[] = { #define S_LIGHTNING_ZAP1 0 S_BRIGHT (MLFX, 'I', 2, A_ZapMimic , &States[S_LIGHTNING_ZAP1+1]), S_BRIGHT (MLFX, 'J', 2, A_ZapMimic , &States[S_LIGHTNING_ZAP1+2]), S_BRIGHT (MLFX, 'K', 2, A_ZapMimic , &States[S_LIGHTNING_ZAP1+3]), S_BRIGHT (MLFX, 'L', 2, A_ZapMimic , &States[S_LIGHTNING_ZAP1+4]), S_BRIGHT (MLFX, 'M', 2, A_ZapMimic , &States[S_LIGHTNING_ZAP1]), #define S_LIGHTNING_ZAP_X1 (S_LIGHTNING_ZAP1+5) S_BRIGHT (MLFX, 'N', 2, NULL , &States[S_LIGHTNING_ZAP_X1+1]), S_BRIGHT (MLFX, 'O', 2, NULL , &States[S_LIGHTNING_ZAP_X1+2]), S_BRIGHT (MLFX, 'P', 2, NULL , &States[S_LIGHTNING_ZAP_X1+3]), S_BRIGHT (MLFX, 'Q', 2, NULL , &States[S_LIGHTNING_ZAP_X1+4]), S_BRIGHT (MLFX, 'R', 2, NULL , &States[S_LIGHTNING_ZAP_X1+5]), S_BRIGHT (MLFX, 'S', 2, NULL , &States[S_LIGHTNING_ZAP_X1+6]), S_BRIGHT (MLFX, 'T', 2, NULL , &States[S_LIGHTNING_ZAP_X1+7]), S_BRIGHT (MLFX, 'U', 2, NULL , NULL), }; IMPLEMENT_ACTOR (ALightningZap, Hexen, -1, 0) PROP_RadiusFixed (15) PROP_HeightFixed (35) PROP_Damage (2) PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE) PROP_RenderStyle (STYLE_Add) PROP_SpawnState (S_LIGHTNING_ZAP1) PROP_DeathState (S_LIGHTNING_ZAP_X1+7) END_DEFAULTS int ALightningZap::SpecialMissileHit (AActor *thing) { AActor *lmo; if (thing->flags&MF_SHOOTABLE && thing != target) { lmo = lastenemy; if (lmo) { if (lmo->flags3 & MF3_FLOORHUGGER) { if (lmo->lastenemy && !lmo->lastenemy->tracer) { lmo->lastenemy->tracer = thing; } } else if (!lmo->tracer) { lmo->tracer = thing; } if (!(level.time&3)) { lmo->health--; } } } return -1; } //============================================================================ // // A_LightningReady // //============================================================================ void A_LightningReady (AActor *actor) { A_WeaponReady (actor); if (pr_lightningready() < 160) { S_Sound (actor, CHAN_WEAPON, "MageLightningReady", 1, ATTN_NORM); } } //============================================================================ // // A_LightningClip // //============================================================================ void A_LightningClip (AActor *actor) { AActor *cMo; AActor *target = NULL; int zigZag; if (actor->flags3 & MF3_FLOORHUGGER) { if (actor->lastenemy == NULL) { return; } actor->z = actor->floorz; target = actor->lastenemy->tracer; } else if (actor->flags3 & MF3_CEILINGHUGGER) { actor->z = actor->ceilingz-actor->height; target = actor->tracer; } if (actor->flags3 & MF3_FLOORHUGGER) { // floor lightning zig-zags, and forces the ceiling lightning to mimic cMo = actor->lastenemy; zigZag = pr_lightningclip(); if((zigZag > 128 && actor->special1 < 2) || actor->special1 < -2) { P_ThrustMobj(actor, actor->angle+ANG90, ZAGSPEED); if(cMo) { P_ThrustMobj(cMo, actor->angle+ANG90, ZAGSPEED); } actor->special1++; } else { P_ThrustMobj(actor, actor->angle-ANG90, ZAGSPEED); if(cMo) { P_ThrustMobj(cMo, cMo->angle-ANG90, ZAGSPEED); } actor->special1--; } } if(target) { if(target->health <= 0) { P_ExplodeMissile(actor, NULL); } else { actor->angle = R_PointToAngle2(actor->x, actor->y, target->x, target->y); actor->momx = 0; actor->momy = 0; P_ThrustMobj (actor, actor->angle, actor->Speed>>1); } } } //============================================================================ // // A_LightningZap // //============================================================================ void A_LightningZap (AActor *actor) { AActor *mo; fixed_t deltaZ; A_LightningClip(actor); actor->health -= 8; if (actor->health <= 0) { actor->SetState (actor->DeathState); return; } if (actor->flags3 & MF3_FLOORHUGGER) { deltaZ = 10*FRACUNIT; } else { deltaZ = -10*FRACUNIT; } mo = Spawn (actor->x+((pr_zap()-128)*actor->radius/256), actor->y+((pr_zap()-128)*actor->radius/256), actor->z+deltaZ, ALLOW_REPLACE); if (mo) { mo->lastenemy = actor; mo->momx = actor->momx; mo->momy = actor->momy; mo->target = actor->target; if (actor->flags3 & MF3_FLOORHUGGER) { mo->momz = 20*FRACUNIT; } else { mo->momz = -20*FRACUNIT; } } if ((actor->flags3 & MF3_FLOORHUGGER) && pr_zapf() < 160) { S_Sound (actor, CHAN_BODY, "MageLightningContinuous", 1, ATTN_NORM); } } //============================================================================ // // A_MLightningAttack2 // //============================================================================ void A_MLightningAttack2 (AActor *actor) { AActor *fmo, *cmo; fmo = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ALightningFloor)); cmo = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ALightningCeiling)); if (fmo) { fmo->special1 = 0; fmo->lastenemy = cmo; A_LightningZap (fmo); } if (cmo) { cmo->tracer = NULL; cmo->lastenemy = fmo; A_LightningZap (cmo); } S_Sound (actor, CHAN_BODY, "MageLightningFire", 1, ATTN_NORM); } //============================================================================ // // A_MLightningAttack // //============================================================================ void A_MLightningAttack (AActor *actor) { A_MLightningAttack2(actor); if (actor->player != NULL) { AWeapon *weapon = actor->player->ReadyWeapon; if (weapon != NULL) { weapon->DepleteAmmo (weapon->bAltFire); } } } //============================================================================ // // A_ZapMimic // //============================================================================ void A_ZapMimic (AActor *actor) { AActor *mo; mo = actor->lastenemy; if (mo) { if (mo->state >= mo->DeathState) { P_ExplodeMissile (actor, NULL); } else { actor->momx = mo->momx; actor->momy = mo->momy; } } } //============================================================================ // // A_LastZap // //============================================================================ void A_LastZap (AActor *actor) { AActor *mo; mo = Spawn (actor->x, actor->y, actor->z, ALLOW_REPLACE); if (mo) { mo->SetState (&ALightningZap::States[S_LIGHTNING_ZAP_X1]); mo->momz = 40*FRACUNIT; mo->Damage = 0; } } //============================================================================ // // A_LightningRemove // //============================================================================ void A_LightningRemove (AActor *actor) { AActor *mo; mo = actor->lastenemy; if (mo) { mo->lastenemy = NULL; P_ExplodeMissile (mo, NULL); } }