mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-14 08:31:23 +00:00
- scriptified Hexen's Frost shards.
- scriptified all SpecialMissileHit methods.
This commit is contained in:
parent
5ce5466e18
commit
7b5a589635
10 changed files with 242 additions and 302 deletions
|
@ -667,7 +667,7 @@ public:
|
||||||
// Called by PIT_CheckThing() and needed for some Hexen things.
|
// Called by PIT_CheckThing() and needed for some Hexen things.
|
||||||
// Returns -1 for normal behavior, 0 to return false, and 1 to return true.
|
// Returns -1 for normal behavior, 0 to return false, and 1 to return true.
|
||||||
// I'm not sure I like it this way, but it will do for now.
|
// I'm not sure I like it this way, but it will do for now.
|
||||||
virtual int SpecialMissileHit (AActor *victim);
|
int SpecialMissileHit (AActor *victim);
|
||||||
|
|
||||||
// Returns true if it's okay to switch target to "other" after being attacked by it.
|
// Returns true if it's okay to switch target to "other" after being attacked by it.
|
||||||
virtual bool OkayToSwitchTarget (AActor *other);
|
virtual bool OkayToSwitchTarget (AActor *other);
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
// 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_heresiarch.cpp"
|
||||||
#include "a_magecone.cpp"
|
|
||||||
#include "a_magelightning.cpp"
|
#include "a_magelightning.cpp"
|
||||||
#include "a_magestaff.cpp"
|
#include "a_magestaff.cpp"
|
||||||
#include "a_serpent.cpp"
|
#include "a_serpent.cpp"
|
||||||
|
|
|
@ -1,185 +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"
|
|
||||||
*/
|
|
||||||
|
|
||||||
const int SHARDSPAWN_LEFT = 1;
|
|
||||||
const int SHARDSPAWN_RIGHT = 2;
|
|
||||||
const int SHARDSPAWN_UP = 4;
|
|
||||||
const int SHARDSPAWN_DOWN = 8;
|
|
||||||
|
|
||||||
static FRandom pr_cone ("FireConePL1");
|
|
||||||
|
|
||||||
void A_FireConePL1 (AActor *actor);
|
|
||||||
void A_ShedShard (AActor *);
|
|
||||||
|
|
||||||
// Frost Missile ------------------------------------------------------------
|
|
||||||
|
|
||||||
class AFrostMissile : public AActor
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (AFrostMissile, AActor)
|
|
||||||
public:
|
|
||||||
int DoSpecialDamage (AActor *victim, int damage, FName damagetype);
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(AFrostMissile, false, false)
|
|
||||||
|
|
||||||
int AFrostMissile::DoSpecialDamage (AActor *victim, int damage, FName damagetype)
|
|
||||||
{
|
|
||||||
if (special2 > 0)
|
|
||||||
{
|
|
||||||
damage <<= special2;
|
|
||||||
}
|
|
||||||
return damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_FireConePL1
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_FireConePL1)
|
|
||||||
{
|
|
||||||
PARAM_ACTION_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
DAngle angle;
|
|
||||||
int damage;
|
|
||||||
DAngle slope;
|
|
||||||
int i;
|
|
||||||
AActor *mo;
|
|
||||||
bool conedone=false;
|
|
||||||
player_t *player;
|
|
||||||
FTranslatedLineTarget t;
|
|
||||||
|
|
||||||
if (NULL == (player = self->player))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
AWeapon *weapon = self->player->ReadyWeapon;
|
|
||||||
if (weapon != NULL)
|
|
||||||
{
|
|
||||||
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
S_Sound (self, CHAN_WEAPON, "MageShardsFire", 1, ATTN_NORM);
|
|
||||||
|
|
||||||
damage = 90+(pr_cone()&15);
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
angle = self->Angles.Yaw + i*(45./16);
|
|
||||||
slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0., ALF_CHECK3D);
|
|
||||||
if (t.linetarget)
|
|
||||||
{
|
|
||||||
P_DamageMobj (t.linetarget, self, self, damage, NAME_Ice, DMG_USEANGLE, t.angleFromSource.Degrees);
|
|
||||||
conedone = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// didn't find any creatures, so fire projectiles
|
|
||||||
if (!conedone)
|
|
||||||
{
|
|
||||||
mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(AFrostMissile));
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP
|
|
||||||
|SHARDSPAWN_RIGHT;
|
|
||||||
mo->special2 = 3; // Set sperm count (levels of reproductivity)
|
|
||||||
mo->target = self;
|
|
||||||
mo->args[0] = 3; // Mark Initial shard as super damage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// A_ShedShard
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, A_ShedShard)
|
|
||||||
{
|
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
|
||||||
|
|
||||||
AActor *mo;
|
|
||||||
int spawndir = self->special1;
|
|
||||||
int spermcount = self->special2;
|
|
||||||
|
|
||||||
if (spermcount <= 0)
|
|
||||||
{
|
|
||||||
return 0; // No sperm left
|
|
||||||
}
|
|
||||||
self->special2 = 0;
|
|
||||||
spermcount--;
|
|
||||||
|
|
||||||
// every so many calls, spawn a new missile in its set directions
|
|
||||||
if (spawndir & SHARDSPAWN_LEFT)
|
|
||||||
{
|
|
||||||
mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile),
|
|
||||||
self->Angles.Yaw + 5, 0, (20. + 2 * spermcount), self->target);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->special1 = SHARDSPAWN_LEFT;
|
|
||||||
mo->special2 = spermcount;
|
|
||||||
mo->Vel.Z = self->Vel.Z;
|
|
||||||
mo->args[0] = (spermcount==3)?2:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (spawndir & SHARDSPAWN_RIGHT)
|
|
||||||
{
|
|
||||||
mo = P_SpawnMissileAngleZSpeed(self, self->Z(), RUNTIME_CLASS(AFrostMissile),
|
|
||||||
self->Angles.Yaw - 5, 0, (20. + 2 * spermcount), self->target);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->special1 = SHARDSPAWN_RIGHT;
|
|
||||||
mo->special2 = spermcount;
|
|
||||||
mo->Vel.Z = self->Vel.Z;
|
|
||||||
mo->args[0] = (spermcount==3)?2:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (spawndir & SHARDSPAWN_UP)
|
|
||||||
{
|
|
||||||
mo = P_SpawnMissileAngleZSpeed(self, self->Z() + 8., RUNTIME_CLASS(AFrostMissile),
|
|
||||||
self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->Vel.Z = self->Vel.Z;
|
|
||||||
if (spermcount & 1) // Every other reproduction
|
|
||||||
mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
|
|
||||||
else
|
|
||||||
mo->special1 = SHARDSPAWN_UP;
|
|
||||||
mo->special2 = spermcount;
|
|
||||||
mo->args[0] = (spermcount==3)?2:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (spawndir & SHARDSPAWN_DOWN)
|
|
||||||
{
|
|
||||||
mo = P_SpawnMissileAngleZSpeed(self, self->Z() - 4., RUNTIME_CLASS(AFrostMissile),
|
|
||||||
self->Angles.Yaw, 0, (15. + 2 * spermcount), self->target);
|
|
||||||
if (mo)
|
|
||||||
{
|
|
||||||
mo->Vel.Z = self->Vel.Z;
|
|
||||||
if (spermcount & 1) // Every other reproduction
|
|
||||||
mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
|
|
||||||
else
|
|
||||||
mo->special1 = SHARDSPAWN_DOWN;
|
|
||||||
mo->special2 = spermcount;
|
|
||||||
mo->target = self->target;
|
|
||||||
mo->args[0] = (spermcount==3)?2:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -25,99 +25,6 @@ static FRandom pr_hit ("LightningHit");
|
||||||
DECLARE_ACTION(A_LightningClip)
|
DECLARE_ACTION(A_LightningClip)
|
||||||
DECLARE_ACTION(A_LightningZap)
|
DECLARE_ACTION(A_LightningZap)
|
||||||
|
|
||||||
// Lightning ----------------------------------------------------------------
|
|
||||||
|
|
||||||
class ALightning : public AActor
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ALightning, AActor)
|
|
||||||
public:
|
|
||||||
int SpecialMissileHit (AActor *victim);
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ALightning, false, false)
|
|
||||||
|
|
||||||
int ALightning::SpecialMissileHit (AActor *thing)
|
|
||||||
{
|
|
||||||
if (thing->flags&MF_SHOOTABLE && thing != target)
|
|
||||||
{
|
|
||||||
if (thing->Mass != INT_MAX)
|
|
||||||
{
|
|
||||||
thing->Vel.X += Vel.X / 16;
|
|
||||||
thing->Vel.Y += Vel.Y / 16;
|
|
||||||
}
|
|
||||||
if ((!thing->player && !(thing->flags2&MF2_BOSS))
|
|
||||||
|| !(level.time&1))
|
|
||||||
{
|
|
||||||
P_DamageMobj(thing, this, target, 3, NAME_Electric);
|
|
||||||
if (!(S_IsActorPlayingSomething (this, CHAN_WEAPON, -1)))
|
|
||||||
{
|
|
||||||
S_Sound (this, CHAN_WEAPON, this->AttackSound, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lightning Zap ------------------------------------------------------------
|
|
||||||
|
|
||||||
class ALightningZap : public AActor
|
|
||||||
{
|
|
||||||
DECLARE_CLASS (ALightningZap, AActor)
|
|
||||||
public:
|
|
||||||
int SpecialMissileHit (AActor *thing);
|
|
||||||
};
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(ALightningZap, false, false)
|
|
||||||
|
|
||||||
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
|
// A_LightningReady
|
||||||
|
|
|
@ -72,24 +72,11 @@ class AMageStaffFX2 : public AActor
|
||||||
{
|
{
|
||||||
DECLARE_CLASS(AMageStaffFX2, AActor)
|
DECLARE_CLASS(AMageStaffFX2, AActor)
|
||||||
public:
|
public:
|
||||||
int SpecialMissileHit (AActor *victim);
|
|
||||||
bool SpecialBlastHandling (AActor *source, double strength);
|
bool SpecialBlastHandling (AActor *source, double strength);
|
||||||
};
|
};
|
||||||
|
|
||||||
IMPLEMENT_CLASS(AMageStaffFX2, false, false)
|
IMPLEMENT_CLASS(AMageStaffFX2, false, false)
|
||||||
|
|
||||||
int AMageStaffFX2::SpecialMissileHit (AActor *victim)
|
|
||||||
{
|
|
||||||
if (victim != target &&
|
|
||||||
!victim->player &&
|
|
||||||
!(victim->flags2 & MF2_BOSS))
|
|
||||||
{
|
|
||||||
P_DamageMobj (victim, this, target, 10, NAME_Fire);
|
|
||||||
return 1; // Keep going
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AMageStaffFX2::SpecialBlastHandling (AActor *source, double strength)
|
bool AMageStaffFX2::SpecialBlastHandling (AActor *source, double strength)
|
||||||
{
|
{
|
||||||
// Reflect to originator
|
// Reflect to originator
|
||||||
|
|
|
@ -3357,6 +3357,7 @@ bool AActor::SpecialBlastHandling (AActor *source, double strength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This only gets called from the script side so we do not need a native wrapper like for the other virtual methods.
|
// This only gets called from the script side so we do not need a native wrapper like for the other virtual methods.
|
||||||
|
// This will be removed, once all actors overriding this method are exported.
|
||||||
DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling)
|
DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling)
|
||||||
{
|
{
|
||||||
PARAM_SELF_PROLOGUE(AActor);
|
PARAM_SELF_PROLOGUE(AActor);
|
||||||
|
@ -3365,10 +3366,20 @@ DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling)
|
||||||
ACTION_RETURN_BOOL(self->SpecialBlastHandling(source, strength));
|
ACTION_RETURN_BOOL(self->SpecialBlastHandling(source, strength));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This virtual method only exists on the script side.
|
||||||
int AActor::SpecialMissileHit (AActor *victim)
|
int AActor::SpecialMissileHit (AActor *victim)
|
||||||
{
|
{
|
||||||
return -1;
|
IFVIRTUAL(AActor, SpecialMissileHit)
|
||||||
|
{
|
||||||
|
VMValue params[2] = { (DObject*)this, victim };
|
||||||
|
VMReturn ret;
|
||||||
|
int retval;
|
||||||
|
VMFrameStack stack;
|
||||||
|
ret.IntAt(&retval);
|
||||||
|
stack.Call(func, params, 2, &ret, 1, nullptr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
else return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle)
|
bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle)
|
||||||
|
|
|
@ -6,6 +6,7 @@ class Actor : Thinker native
|
||||||
const FLOATRANDZ = ONCEILINGZ-1;
|
const FLOATRANDZ = ONCEILINGZ-1;
|
||||||
const TELEFRAG_DAMAGE = 1000000;
|
const TELEFRAG_DAMAGE = 1000000;
|
||||||
const MinVel = 1./65536;
|
const MinVel = 1./65536;
|
||||||
|
const LARGE_MASS = 10000000; // not INT_MAX on purpose
|
||||||
|
|
||||||
|
|
||||||
// flags are not defined here, the native fields for those get synthesized from the internal tables.
|
// flags are not defined here, the native fields for those get synthesized from the internal tables.
|
||||||
|
@ -268,6 +269,11 @@ class Actor : Thinker native
|
||||||
virtual native bool UseInventory(Inventory item);
|
virtual native bool UseInventory(Inventory item);
|
||||||
virtual native bool SpecialBlastHandling (Actor source, double strength);
|
virtual native bool SpecialBlastHandling (Actor source, double strength);
|
||||||
|
|
||||||
|
virtual int SpecialMissileHit (Actor victim) // for this no native version exists
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
native static double GetDefaultSpeed(class<Actor> type);
|
native static double GetDefaultSpeed(class<Actor> type);
|
||||||
native static class<Actor> GetSpawnableType(int spawnnum);
|
native static class<Actor> GetSpawnableType(int spawnnum);
|
||||||
|
|
|
@ -17,8 +17,6 @@ class MWeapFrost : MageWeapon
|
||||||
Tag "$TAG_MWEAPFROST";
|
Tag "$TAG_MWEAPFROST";
|
||||||
}
|
}
|
||||||
|
|
||||||
action native void A_FireConePL1();
|
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
Spawn:
|
Spawn:
|
||||||
|
@ -45,12 +43,68 @@ class MWeapFrost : MageWeapon
|
||||||
CONE A 10 A_ReFire;
|
CONE A 10 A_ReFire;
|
||||||
Goto Ready;
|
Goto Ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_FireConePL1
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
action void A_FireConePL1()
|
||||||
|
{
|
||||||
|
bool conedone=false;
|
||||||
|
FTranslatedLineTarget t;
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Weapon weapon = player.ReadyWeapon;
|
||||||
|
if (weapon != null)
|
||||||
|
{
|
||||||
|
if (!weapon.DepleteAmmo (weapon.bAltFire))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
A_PlaySound ("MageShardsFire", CHAN_WEAPON);
|
||||||
|
|
||||||
|
int damage = 90+(random[MageCone]() & 15);
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
double ang = angle + i*(45./16);
|
||||||
|
double slope = AimLineAttack (ang, MELEERANGE, t, 0., ALF_CHECK3D);
|
||||||
|
if (t.linetarget)
|
||||||
|
{
|
||||||
|
t.linetarget.DamageMobj (self, self, damage, 'Ice', DMG_USEANGLE, t.angleFromSource);
|
||||||
|
conedone = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't find any creatures, so fire projectiles
|
||||||
|
if (!conedone)
|
||||||
|
{
|
||||||
|
Actor mo = SpawnPlayerMissile ("FrostMissile");
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.special1 = FrostMissile.SHARDSPAWN_LEFT|FrostMissile.SHARDSPAWN_DOWN|FrostMissile.SHARDSPAWN_UP|FrostMissile.SHARDSPAWN_RIGHT;
|
||||||
|
mo.special2 = 3; // Set sperm count (levels of reproductivity)
|
||||||
|
mo.target = self;
|
||||||
|
mo.args[0] = 3; // Mark Initial shard as super damage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frost Missile ------------------------------------------------------------
|
// Frost Missile ------------------------------------------------------------
|
||||||
|
|
||||||
class FrostMissile : Actor native
|
class FrostMissile : Actor
|
||||||
{
|
{
|
||||||
|
const SHARDSPAWN_LEFT = 1;
|
||||||
|
const SHARDSPAWN_RIGHT = 2;
|
||||||
|
const SHARDSPAWN_UP = 4;
|
||||||
|
const SHARDSPAWN_DOWN = 8;
|
||||||
|
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
Speed 25;
|
Speed 25;
|
||||||
|
@ -63,8 +117,6 @@ class FrostMissile : Actor native
|
||||||
Obituary "$OB_MPMWEAPFROST";
|
Obituary "$OB_MPMWEAPFROST";
|
||||||
}
|
}
|
||||||
|
|
||||||
native void A_ShedShard();
|
|
||||||
|
|
||||||
States
|
States
|
||||||
{
|
{
|
||||||
Spawn:
|
Spawn:
|
||||||
|
@ -77,6 +129,88 @@ class FrostMissile : Actor native
|
||||||
SHEX ABCDE 5 Bright;
|
SHEX ABCDE 5 Bright;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int DoSpecialDamage (Actor victim, int damage, Name damagetype)
|
||||||
|
{
|
||||||
|
if (special2 > 0)
|
||||||
|
{
|
||||||
|
damage <<= special2;
|
||||||
|
}
|
||||||
|
return damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// A_ShedShard
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void A_ShedShard()
|
||||||
|
{
|
||||||
|
int spawndir = special1;
|
||||||
|
int spermcount = special2;
|
||||||
|
Actor mo;
|
||||||
|
|
||||||
|
if (spermcount <= 0)
|
||||||
|
{
|
||||||
|
return; // No sperm left
|
||||||
|
}
|
||||||
|
special2 = 0;
|
||||||
|
spermcount--;
|
||||||
|
|
||||||
|
// every so many calls, spawn a new missile in its set directions
|
||||||
|
if (spawndir & SHARDSPAWN_LEFT)
|
||||||
|
{
|
||||||
|
mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle + 5, 0, (20. + 2 * spermcount), target);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.special1 = SHARDSPAWN_LEFT;
|
||||||
|
mo.special2 = spermcount;
|
||||||
|
mo.Vel.Z = Vel.Z;
|
||||||
|
mo.args[0] = (spermcount==3)?2:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spawndir & SHARDSPAWN_RIGHT)
|
||||||
|
{
|
||||||
|
mo = SpawnMissileAngleZSpeed(pos.z, "FrostMissile", angle - 5, 0, (20. + 2 * spermcount), target);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.special1 = SHARDSPAWN_RIGHT;
|
||||||
|
mo.special2 = spermcount;
|
||||||
|
mo.Vel.Z = Vel.Z;
|
||||||
|
mo.args[0] = (spermcount==3)?2:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spawndir & SHARDSPAWN_UP)
|
||||||
|
{
|
||||||
|
mo = SpawnMissileAngleZSpeed(pos.z + 8., "FrostMissile", angle, 0, (15. + 2 * spermcount), target);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.Vel.Z = Vel.Z;
|
||||||
|
if (spermcount & 1) // Every other reproduction
|
||||||
|
mo.special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
|
||||||
|
else
|
||||||
|
mo.special1 = SHARDSPAWN_UP;
|
||||||
|
mo.special2 = spermcount;
|
||||||
|
mo.args[0] = (spermcount==3)?2:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spawndir & SHARDSPAWN_DOWN)
|
||||||
|
{
|
||||||
|
mo = SpawnMissileAngleZSpeed(pos.z - 4., "FrostMissile", angle, 0, (15. + 2 * spermcount), target);
|
||||||
|
if (mo)
|
||||||
|
{
|
||||||
|
mo.Vel.Z = Vel.Z;
|
||||||
|
if (spermcount & 1) // Every other reproduction
|
||||||
|
mo.special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
|
||||||
|
else
|
||||||
|
mo.special1 = SHARDSPAWN_DOWN;
|
||||||
|
mo.special2 = spermcount;
|
||||||
|
mo.target = target;
|
||||||
|
mo.args[0] = (spermcount==3)?2:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ice Shard ----------------------------------------------------------------
|
// Ice Shard ----------------------------------------------------------------
|
||||||
|
|
|
@ -54,7 +54,7 @@ class MWeapLightning : MageWeapon
|
||||||
|
|
||||||
// Ceiling Lightning --------------------------------------------------------
|
// Ceiling Lightning --------------------------------------------------------
|
||||||
|
|
||||||
class Lightning : Actor native
|
class Lightning : Actor
|
||||||
{
|
{
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
|
@ -63,6 +63,45 @@ class Lightning : Actor native
|
||||||
ActiveSound "MageLightningContinuous";
|
ActiveSound "MageLightningContinuous";
|
||||||
Obituary "$OB_MPMWEAPLIGHTNING";
|
Obituary "$OB_MPMWEAPLIGHTNING";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int SpecialMissileHit (Actor thing)
|
||||||
|
{
|
||||||
|
if (thing.bShootable && thing != target)
|
||||||
|
{
|
||||||
|
if (thing.Mass < LARGE_MASS)
|
||||||
|
{
|
||||||
|
thing.Vel.X += Vel.X / 16;
|
||||||
|
thing.Vel.Y += Vel.Y / 16;
|
||||||
|
}
|
||||||
|
if ((!thing.player && !thing.bBoss) || !(level.time&1))
|
||||||
|
{
|
||||||
|
thing.DamageMobj(self, target, 3, 'Electric');
|
||||||
|
A_PlaySound(self.AttackSound, CHAN_WEAPON, 1, true);
|
||||||
|
if (thing.bIsMonster && random[LightningHit]() < 64)
|
||||||
|
{
|
||||||
|
thing.Howl ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
health--;
|
||||||
|
if (health <= 0 || thing.health <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (bFloorHugger)
|
||||||
|
{
|
||||||
|
if (lastenemy && ! lastenemy.tracer)
|
||||||
|
{
|
||||||
|
lastenemy.tracer = thing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!tracer)
|
||||||
|
{
|
||||||
|
tracer = thing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1; // lightning zaps through all sprites
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LightningCeiling : Lightning
|
class LightningCeiling : Lightning
|
||||||
|
@ -141,7 +180,7 @@ class LightningFloor : LightningCeiling
|
||||||
|
|
||||||
// Lightning Zap ------------------------------------------------------------
|
// Lightning Zap ------------------------------------------------------------
|
||||||
|
|
||||||
class LightningZap : Actor native
|
class LightningZap : Actor
|
||||||
{
|
{
|
||||||
Default
|
Default
|
||||||
{
|
{
|
||||||
|
@ -166,4 +205,34 @@ class LightningZap : Actor native
|
||||||
MLFX NOPQRSTU 2 Bright;
|
MLFX NOPQRSTU 2 Bright;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int SpecialMissileHit (Actor thing)
|
||||||
|
{
|
||||||
|
Actor lmo;
|
||||||
|
|
||||||
|
if (thing.bShootable && thing != target)
|
||||||
|
{
|
||||||
|
lmo = lastenemy;
|
||||||
|
if (lmo)
|
||||||
|
{
|
||||||
|
if (lmo.bFloorHugger)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,4 +157,16 @@ class MageStaffFX2 : Actor native
|
||||||
MSP2 I 4 Bright;
|
MSP2 I 4 Bright;
|
||||||
Stop;
|
Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int SpecialMissileHit (Actor victim)
|
||||||
|
{
|
||||||
|
if (victim != target && !victim.player && !victim.bBoss)
|
||||||
|
{
|
||||||
|
victim.DamageMobj (self, target, 10, 'Fire');
|
||||||
|
return 1; // Keep going
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue