- scriptified Hexen's Frost shards.

- scriptified all SpecialMissileHit methods.
This commit is contained in:
Christoph Oelckers 2016-11-27 21:41:04 +01:00
parent 5ce5466e18
commit 7b5a589635
10 changed files with 242 additions and 302 deletions

View file

@ -667,7 +667,7 @@ public:
// Called by PIT_CheckThing() and needed for some Hexen things.
// 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.
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.
virtual bool OkayToSwitchTarget (AActor *other);

View file

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

View file

@ -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;
}

View file

@ -25,99 +25,6 @@ static FRandom pr_hit ("LightningHit");
DECLARE_ACTION(A_LightningClip)
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

View file

@ -72,24 +72,11 @@ class AMageStaffFX2 : public AActor
{
DECLARE_CLASS(AMageStaffFX2, AActor)
public:
int SpecialMissileHit (AActor *victim);
bool SpecialBlastHandling (AActor *source, double strength);
};
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)
{
// Reflect to originator

View file

@ -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 will be removed, once all actors overriding this method are exported.
DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling)
{
PARAM_SELF_PROLOGUE(AActor);
@ -3365,10 +3366,20 @@ DEFINE_ACTION_FUNCTION(AActor, SpecialBlastHandling)
ACTION_RETURN_BOOL(self->SpecialBlastHandling(source, strength));
}
// This virtual method only exists on the script side.
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)

View file

@ -6,6 +6,7 @@ class Actor : Thinker native
const FLOATRANDZ = ONCEILINGZ-1;
const TELEFRAG_DAMAGE = 1000000;
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.
@ -268,6 +269,11 @@ class Actor : Thinker native
virtual native bool UseInventory(Inventory item);
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 class<Actor> GetSpawnableType(int spawnnum);

View file

@ -17,8 +17,6 @@ class MWeapFrost : MageWeapon
Tag "$TAG_MWEAPFROST";
}
action native void A_FireConePL1();
States
{
Spawn:
@ -45,12 +43,68 @@ class MWeapFrost : MageWeapon
CONE A 10 A_ReFire;
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 ------------------------------------------------------------
class FrostMissile : Actor native
class FrostMissile : Actor
{
const SHARDSPAWN_LEFT = 1;
const SHARDSPAWN_RIGHT = 2;
const SHARDSPAWN_UP = 4;
const SHARDSPAWN_DOWN = 8;
Default
{
Speed 25;
@ -63,8 +117,6 @@ class FrostMissile : Actor native
Obituary "$OB_MPMWEAPFROST";
}
native void A_ShedShard();
States
{
Spawn:
@ -77,6 +129,88 @@ class FrostMissile : Actor native
SHEX ABCDE 5 Bright;
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 ----------------------------------------------------------------

View file

@ -54,7 +54,7 @@ class MWeapLightning : MageWeapon
// Ceiling Lightning --------------------------------------------------------
class Lightning : Actor native
class Lightning : Actor
{
Default
{
@ -63,6 +63,45 @@ class Lightning : Actor native
ActiveSound "MageLightningContinuous";
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
@ -141,7 +180,7 @@ class LightningFloor : LightningCeiling
// Lightning Zap ------------------------------------------------------------
class LightningZap : Actor native
class LightningZap : Actor
{
Default
{
@ -166,4 +205,34 @@ class LightningZap : Actor native
MLFX NOPQRSTU 2 Bright;
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;
}
}

View file

@ -157,4 +157,16 @@ class MageStaffFX2 : Actor native
MSP2 I 4 Bright;
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;
}
}