- scriptified Hexen's lightning weapon.

This commit is contained in:
Christoph Oelckers 2016-11-27 22:14:18 +01:00
parent 7b5a589635
commit f9a1388066
6 changed files with 228 additions and 289 deletions

View file

@ -860,8 +860,6 @@ set( NOT_COMPILED_SOURCE_FILES
sc_man_scanner.h
sc_man_scanner.re
g_hexen/a_heresiarch.cpp
g_hexen/a_magecone.cpp
g_hexen/a_magelightning.cpp
g_hexen/a_magestaff.cpp
g_hexen/a_serpent.cpp
g_hexen/a_spike.cpp

View file

@ -25,7 +25,6 @@
// Include all the Hexen stuff here to reduce compile time
#include "a_heresiarch.cpp"
#include "a_magelightning.cpp"
#include "a_magestaff.cpp"
#include "a_serpent.cpp"
#include "a_spike.cpp"

View file

@ -1,274 +0,0 @@
/*
#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 "a_action.h"
#include "p_pspr.h"
#include "gstrings.h"
#include "a_hexenglobal.h"
#include "vm.h"
#include "g_level.h"
*/
#define ZAGSPEED 1.
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");
DECLARE_ACTION(A_LightningClip)
DECLARE_ACTION(A_LightningZap)
//============================================================================
//
// A_LightningReady
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningReady)
{
PARAM_ACTION_PROLOGUE(AActor);
DoReadyWeapon(self);
if (pr_lightningready() < 160)
{
S_Sound (self, CHAN_WEAPON, "MageLightningReady", 1, ATTN_NORM);
}
return 0;
}
//============================================================================
//
// A_LightningClip
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningClip)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *cMo;
AActor *target = NULL;
int zigZag;
if (self->flags3 & MF3_FLOORHUGGER)
{
if (self->lastenemy == NULL)
{
return 0;
}
self->SetZ(self->floorz);
target = self->lastenemy->tracer;
}
else if (self->flags3 & MF3_CEILINGHUGGER)
{
self->SetZ(self->ceilingz - self->Height);
target = self->tracer;
}
if (self->flags3 & MF3_FLOORHUGGER)
{ // floor lightning zig-zags, and forces the ceiling lightning to mimic
cMo = self->lastenemy;
zigZag = pr_lightningclip();
if((zigZag > 128 && self->special1 < 2) || self->special1 < -2)
{
self->Thrust(self->Angles.Yaw + 90, ZAGSPEED);
if(cMo)
{
cMo->Thrust(self->Angles.Yaw + 90, ZAGSPEED);
}
self->special1++;
}
else
{
self->Thrust(self->Angles.Yaw - 90, ZAGSPEED);
if(cMo)
{
cMo->Thrust(self->Angles.Yaw - 90, ZAGSPEED);
}
self->special1--;
}
}
if(target)
{
if(target->health <= 0)
{
P_ExplodeMissile(self, NULL, NULL);
}
else
{
self->Angles.Yaw = self->AngleTo(target);
self->VelFromAngle(self->Speed / 2);
}
}
return 0;
}
//============================================================================
//
// A_LightningZap
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningZap)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *lightning = PClass::FindActor(self->GetClass()->MissileName);
AActor *mo;
if (lightning == NULL)
{
lightning = PClass::FindActor(NAME_LightningZap);
}
CALL_ACTION(A_LightningClip, self);
self->health -= 8;
if (self->health <= 0)
{
self->SetState (self->FindState(NAME_Death));
return 0;
}
double deltaX = (pr_zap() - 128) * self->radius / 256;
double deltaY = (pr_zap() - 128) * self->radius / 256;
double deltaZ = (self->flags3 & MF3_FLOORHUGGER) ? 10 : -10;
mo = Spawn(lightning, self->Vec3Offset(deltaX, deltaY, deltaZ), ALLOW_REPLACE);
if (mo)
{
mo->lastenemy = self;
mo->Vel.X = self->Vel.X;
mo->Vel.Y = self->Vel.Y;
mo->Vel.Z = (self->flags3 & MF3_FLOORHUGGER) ? 20 : -20;
mo->target = self->target;
}
if ((self->flags3 & MF3_FLOORHUGGER) && pr_zapf() < 160)
{
S_Sound (self, CHAN_BODY, self->ActiveSound, 1, ATTN_NORM);
}
return 0;
}
//============================================================================
//
// A_MLightningAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_MLightningAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_CLASS_DEF(floor, AActor);
PARAM_CLASS_DEF(ceiling, AActor);
AActor *fmo, *cmo;
fmo = P_SpawnPlayerMissile (self, floor);
cmo = P_SpawnPlayerMissile (self, ceiling);
if (fmo)
{
fmo->special1 = 0;
fmo->lastenemy = cmo;
CALL_ACTION(A_LightningZap, fmo);
}
if (cmo)
{
cmo->tracer = NULL;
cmo->lastenemy = fmo;
CALL_ACTION(A_LightningZap, cmo);
}
S_Sound (self, CHAN_BODY, "MageLightningFire", 1, ATTN_NORM);
if (self->player != NULL)
{
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
{
weapon->DepleteAmmo (weapon->bAltFire);
}
}
return 0;
}
//============================================================================
//
// A_ZapMimic
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ZapMimic)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = self->lastenemy;
if (mo)
{
if (mo->state >= mo->FindState(NAME_Death))
{
P_ExplodeMissile (self, NULL, NULL);
}
else
{
self->Vel.X = mo->Vel.X;
self->Vel.Y = mo->Vel.Y;
}
}
return 0;
}
//============================================================================
//
// A_LastZap
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LastZap)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *lightning = PClass::FindActor(self->GetClass()->MissileName);
AActor *mo;
if (lightning == NULL)
{
lightning = PClass::FindActor(NAME_LightningZap);
}
mo = Spawn(lightning, self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->FindState (NAME_Death));
mo->Vel.Z = 40;
mo->SetDamage(0);
}
return 0;
}
//============================================================================
//
// A_LightningRemove
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LightningRemove)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
mo = self->lastenemy;
if (mo)
{
mo->lastenemy = NULL;
P_ExplodeMissile (mo, NULL, NULL);
}
return 0;
}

View file

@ -1751,6 +1751,15 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target)
}
}
DEFINE_ACTION_FUNCTION(AActor, ExplodeMissile)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER_DEF(line, line_t);
PARAM_OBJECT_DEF(target, AActor);
P_ExplodeMissile(self, line, target);
return 0;
}
void AActor::PlayBounceSound(bool onfloor)
{

View file

@ -289,6 +289,7 @@ class Actor : Thinker native
native void NoiseAlert(Actor emitter, bool splash = false, double maxdist = 0);
native void ClearBounce();
native void ExplodeMissile(line lin = null, Actor target = null);
native void RestoreDamage();
native int SpawnHealth();
native void SetDamage(int dmg);

View file

@ -16,9 +16,6 @@ class MWeapLightning : MageWeapon
Tag "$TAG_MWEAPLIGHTNING";
}
action native void A_LightningReady();
action native void A_MLightningAttack(class<Actor> floor = "LightningFloor", class<Actor> ceiling = "LightningCeiling");
States
{
Spawn:
@ -50,6 +47,57 @@ class MWeapLightning : MageWeapon
MLNG B 2 Bright Offset (0, 40);
Goto Ready;
}
//============================================================================
//
// A_LightningReady
//
//============================================================================
action void A_LightningReady()
{
A_WeaponReady();
if (random[LightningReady]() < 160)
{
A_PlaySound ("MageLightningReady", CHAN_WEAPON);
}
}
//============================================================================
//
// A_MLightningAttack
//
//============================================================================
action void A_MLightningAttack(class<Actor> floor = "LightningFloor", class<Actor> ceiling = "LightningCeiling")
{
LightningFloor fmo = LightningFloor(SpawnPlayerMissile (floor));
LightningCeiling cmo = LightningCeiling(SpawnPlayerMissile (ceiling));
if (fmo)
{
fmo.special1 = 0;
fmo.lastenemy = cmo;
fmo.A_LightningZap();
}
if (cmo)
{
cmo.tracer = NULL;
cmo.lastenemy = fmo;
cmo.A_LightningZap();
}
A_PlaySound ("MageLightningFire", CHAN_BODY);
if (player != NULL)
{
Weapon weapon = player.ReadyWeapon;
if (weapon != NULL)
{
weapon.DepleteAmmo (weapon.bAltFire);
}
}
}
}
// Ceiling Lightning --------------------------------------------------------
@ -76,7 +124,7 @@ class Lightning : Actor
if ((!thing.player && !thing.bBoss) || !(level.time&1))
{
thing.DamageMobj(self, target, 3, 'Electric');
A_PlaySound(self.AttackSound, CHAN_WEAPON, 1, true);
A_PlaySound(AttackSound, CHAN_WEAPON, 1, true);
if (thing.bIsMonster && random[LightningHit]() < 64)
{
thing.Howl ();
@ -106,6 +154,8 @@ class Lightning : Actor
class LightningCeiling : Lightning
{
const ZAGSPEED = 1;
Default
{
Health 144;
@ -118,10 +168,6 @@ class LightningCeiling : Lightning
RenderStyle "Add";
}
native void A_LightningZap();
native void A_LightningClip();
native void A_LightningRemove();
States
{
Spawn:
@ -143,6 +189,126 @@ class LightningCeiling : Lightning
ACLO E 1050;
Stop;
}
//============================================================================
//
// A_LightningClip
//
//============================================================================
void A_LightningClip()
{
Actor cMo;
Actor target = NULL;
int zigZag;
if (bFloorHugger)
{
if (lastenemy == NULL)
{
return;
}
SetZ(floorz);
target = lastenemy.tracer;
}
else if (bCeilingHugger)
{
SetZ(ceilingz - Height);
target = tracer;
}
if (bFloorHugger)
{ // floor lightning zig-zags, and forces the ceiling lightning to mimic
cMo = lastenemy;
zigZag = random[LightningClip]();
if((zigZag > 128 && special1 < 2) || special1 < -2)
{
Thrust(ZAGSPEED, angle + 90);
if(cMo)
{
cMo.Thrust(ZAGSPEED, angle + 90);
}
special1++;
}
else
{
Thrust(ZAGSPEED,angle - 90);
if(cMo)
{
cMo.Thrust(ZAGSPEED, angle - 90);
}
special1--;
}
}
if(target)
{
if(target.health <= 0)
{
ExplodeMissile();
}
else
{
angle = AngleTo(target);
VelFromAngle(Speed / 2);
}
}
}
//============================================================================
//
// A_LightningZap
//
//============================================================================
void A_LightningZap()
{
Class<Actor> lightning = MissileName;
if (lightning == NULL) lightning = "LightningZap";
A_LightningClip();
health -= 8;
if (health <= 0)
{
SetStateLabel ("Death");
return;
}
double deltaX = (random[LightningZap]() - 128) * radius / 256;
double deltaY = (random[LightningZap]() - 128) * radius / 256;
double deltaZ = (bFloorHugger) ? 10 : -10;
Actor mo = Spawn(lightning, Vec3Offset(deltaX, deltaY, deltaZ), ALLOW_REPLACE);
if (mo)
{
mo.lastenemy = self;
mo.Vel.X = Vel.X;
mo.Vel.Y = Vel.Y;
mo.Vel.Z = (bFloorHugger) ? 20 : -20;
mo.target = target;
}
if (bFloorHugger && random[LightningZap]() < 160)
{
A_PlaySound (ActiveSound, CHAN_BODY);
}
}
//============================================================================
//
// A_LightningRemove
//
//============================================================================
void A_LightningRemove()
{
Actor mo = lastenemy;
if (mo)
{
bNoTarget = true; // tell A_ZapMimic that we are dead. The original code did a state pointer compare which is not safe.
mo.lastenemy = NULL;
mo.ExplodeMissile ();
}
}
}
// Floor Lightning ----------------------------------------------------------
@ -156,8 +322,6 @@ class LightningFloor : LightningCeiling
RenderStyle "Add";
}
native void A_LastZap();
States
{
Spawn:
@ -176,6 +340,26 @@ class LightningFloor : LightningCeiling
MLF2 P 1 Bright A_HideThing;
Goto Super::Death + 19;
}
//============================================================================
//
// A_LastZap
//
//============================================================================
void A_LastZap()
{
Class<Actor> lightning = MissileName;
if (lightning == NULL) lightning = "LightningZap";
Actor mo = Spawn(lightning, self.Pos, ALLOW_REPLACE);
if (mo)
{
mo.SetStateLabel ("Death");
mo.Vel.Z = 40;
mo.SetDamage(0);
}
}
}
// Lightning Zap ------------------------------------------------------------
@ -194,8 +378,6 @@ class LightningZap : Actor
Obituary "$OB_MPMWEAPLIGHTNING";
}
native void A_ZapMimic();
States
{
Spawn:
@ -235,4 +417,28 @@ class LightningZap : Actor
return -1;
}
//============================================================================
//
// A_ZapMimic
//
//============================================================================
void A_ZapMimic()
{
Actor mo = lastenemy;
if (mo)
{
if (mo.bNoTarget)
{
ExplodeMissile ();
}
else
{
Vel.X = mo.Vel.X;
Vel.Y = mo.Vel.Y;
}
}
}
}