mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-08 14:01:32 +00:00
a6a5821f04
powerup. - Changed: Weapons are no longer checked for the EXTREMEDEATH and NOEXTREMEDEATH flags. For all damage related actions it will always be the projectile (or puff for hitscan attacks) that is treated as the damage inflictor. - Fixed: Hexen's fourth weapons made extreme death depend on the weapon the player is holding, not the projectile that did the kill. The WIF_EXTREME_DEATH flag no longer exists as a result. - Expanded PowerSpeed so that subclasses can be derived that alter the speed factor. - Added an MF5_USESPECIAL flag that allows using things to execute their specials. - added MF4_FORCERADIUSDMG flag so that exploding items can be created which are able to hurt boss monsters. - moved ML_BLOCK_PLAYERS to the value 0x4000 to bring it in line with Skulltag's implementation. SVN r530 (trunk)
909 lines
23 KiB
C++
909 lines
23 KiB
C++
#include "actor.h"
|
|
#include "info.h"
|
|
#include "p_local.h"
|
|
#include "m_random.h"
|
|
#include "s_sound.h"
|
|
#include "p_enemy.h"
|
|
#include "a_hexenglobal.h"
|
|
#include "gstrings.h"
|
|
|
|
#define BLAST_FULLSTRENGTH 255
|
|
|
|
static FRandom pr_holyatk2 ("CHolyAtk2");
|
|
static FRandom pr_holyseeker ("CHolySeeker");
|
|
static FRandom pr_holyweave ("CHolyWeave");
|
|
static FRandom pr_holyseek ("CHolySeek");
|
|
static FRandom pr_checkscream ("CCheckScream");
|
|
static FRandom pr_spiritslam ("CHolySlam");
|
|
static FRandom pr_wraithvergedrop ("WraithvergeDrop");
|
|
|
|
void A_CHolySpawnPuff (AActor *);
|
|
void A_CHolyAttack2 (AActor *);
|
|
void A_CHolyTail (AActor *);
|
|
void A_CHolySeek (AActor *);
|
|
void A_CHolyCheckScream (AActor *);
|
|
void A_DropWraithvergePieces (AActor *);
|
|
|
|
void A_CHolyAttack (AActor *);
|
|
void A_CHolyPalette (AActor *);
|
|
|
|
void SpawnSpiritTail (AActor *spirit);
|
|
|
|
//==========================================================================
|
|
|
|
class AClericWeaponPiece : public AFourthWeaponPiece
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AClericWeaponPiece, AFourthWeaponPiece)
|
|
public:
|
|
void BeginPlay ();
|
|
protected:
|
|
bool MatchPlayerClass (AActor *toucher);
|
|
};
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AClericWeaponPiece, Hexen, -1, 0)
|
|
PROP_Inventory_PickupMessage("$TXT_WRAITHVERGE_PIECE")
|
|
END_DEFAULTS
|
|
|
|
bool AClericWeaponPiece::MatchPlayerClass (AActor *toucher)
|
|
{
|
|
return !toucher->IsKindOf (PClass::FindClass(NAME_FighterPlayer)) &&
|
|
!toucher->IsKindOf (PClass::FindClass(NAME_MagePlayer));
|
|
}
|
|
|
|
//==========================================================================
|
|
|
|
class ACWeaponPiece1 : public AClericWeaponPiece
|
|
{
|
|
DECLARE_ACTOR (ACWeaponPiece1, AClericWeaponPiece)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState ACWeaponPiece1::States[] =
|
|
{
|
|
S_BRIGHT (WCH1, 'A', -1, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACWeaponPiece1, Hexen, 18, 33)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_Flags2 (MF2_FLOATBOB)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
void ACWeaponPiece1::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
PieceValue = WPIECE1<<3;
|
|
}
|
|
|
|
//==========================================================================
|
|
|
|
class ACWeaponPiece2 : public AClericWeaponPiece
|
|
{
|
|
DECLARE_ACTOR (ACWeaponPiece2, AClericWeaponPiece)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState ACWeaponPiece2::States[] =
|
|
{
|
|
S_BRIGHT (WCH2, 'A', -1, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACWeaponPiece2, Hexen, 19, 34)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_Flags2 (MF2_FLOATBOB)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
void ACWeaponPiece2::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
PieceValue = WPIECE2<<3;
|
|
}
|
|
|
|
//==========================================================================
|
|
|
|
class ACWeaponPiece3 : public AClericWeaponPiece
|
|
{
|
|
DECLARE_ACTOR (ACWeaponPiece3, AClericWeaponPiece)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState ACWeaponPiece3::States[] =
|
|
{
|
|
S_BRIGHT (WCH3, 'A', -1, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACWeaponPiece3, Hexen, 20, 35)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_Flags2 (MF2_FLOATBOB)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
void ACWeaponPiece3::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
PieceValue = WPIECE3<<3;
|
|
}
|
|
|
|
// An actor that spawns the three pieces of the cleric's fourth weapon ------
|
|
|
|
// This gets spawned if weapon drop is on so that other players can pick up
|
|
// this player's weapon.
|
|
|
|
class AWraithvergeDrop : public AActor
|
|
{
|
|
DECLARE_ACTOR (AWraithvergeDrop, AActor)
|
|
};
|
|
|
|
FState AWraithvergeDrop::States[] =
|
|
{
|
|
S_NORMAL (TNT1, 'A', 1, NULL, &States[1]),
|
|
S_NORMAL (TNT1, 'A', 1, A_DropWraithvergePieces, NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AWraithvergeDrop, Hexen, -1, 0)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
|
|
// Cleric's Wraithverge (Holy Symbol?) --------------------------------------
|
|
|
|
class ACWeapWraithverge : public AClericWeapon
|
|
{
|
|
DECLARE_ACTOR (ACWeapWraithverge, AClericWeapon)
|
|
public:
|
|
void Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << CHolyCount;
|
|
}
|
|
PalEntry GetBlend ()
|
|
{
|
|
return PalEntry (CHolyCount * 128 / 3, 131, 131, 131);
|
|
}
|
|
BYTE CHolyCount;
|
|
};
|
|
|
|
FState ACWeapWraithverge::States[] =
|
|
{
|
|
// Dummy state, because the fourth weapon does not appear in a level directly.
|
|
S_NORMAL (TNT1, 'A', -1, NULL , NULL),
|
|
|
|
#define S_CHOLYREADY 1
|
|
S_NORMAL (CHLY, 'A', 1, A_WeaponReady , &States[S_CHOLYREADY]),
|
|
|
|
#define S_CHOLYDOWN (S_CHOLYREADY+1)
|
|
S_NORMAL (CHLY, 'A', 1, A_Lower , &States[S_CHOLYDOWN]),
|
|
|
|
#define S_CHOLYUP (S_CHOLYDOWN+1)
|
|
S_NORMAL (CHLY, 'A', 1, A_Raise , &States[S_CHOLYUP]),
|
|
|
|
#define S_CHOLYATK (S_CHOLYUP+1)
|
|
S_BRIGHT2 (CHLY, 'A', 1, NULL , &States[S_CHOLYATK+1], 0, 40),
|
|
S_BRIGHT2 (CHLY, 'B', 1, NULL , &States[S_CHOLYATK+2], 0, 40),
|
|
S_BRIGHT2 (CHLY, 'C', 2, NULL , &States[S_CHOLYATK+3], 0, 43),
|
|
S_BRIGHT2 (CHLY, 'D', 2, NULL , &States[S_CHOLYATK+4], 0, 43),
|
|
S_BRIGHT2 (CHLY, 'E', 2, NULL , &States[S_CHOLYATK+5], 0, 45),
|
|
S_BRIGHT2 (CHLY, 'F', 6, A_CHolyAttack , &States[S_CHOLYATK+6], 0, 48),
|
|
S_BRIGHT2 (CHLY, 'G', 2, A_CHolyPalette , &States[S_CHOLYATK+7], 0, 40),
|
|
S_BRIGHT2 (CHLY, 'G', 2, A_CHolyPalette , &States[S_CHOLYATK+8], 0, 40),
|
|
S_BRIGHT2 (CHLY, 'G', 2, A_CHolyPalette , &States[S_CHOLYREADY], 0, 36)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACWeapWraithverge, Hexen, -1, 0)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_SpawnState (0)
|
|
|
|
PROP_Weapon_SelectionOrder (3000)
|
|
PROP_Weapon_Flags (WIF_PRIMARY_USES_BOTH)
|
|
PROP_Weapon_AmmoUse1 (18)
|
|
PROP_Weapon_AmmoUse2 (18)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AmmoGive2 (0)
|
|
PROP_Weapon_UpState (S_CHOLYUP)
|
|
PROP_Weapon_DownState (S_CHOLYDOWN)
|
|
PROP_Weapon_ReadyState (S_CHOLYREADY)
|
|
PROP_Weapon_AtkState (S_CHOLYATK)
|
|
PROP_Weapon_Kickback (150)
|
|
PROP_Weapon_MoveCombatDist (22000000)
|
|
PROP_Weapon_AmmoType1 ("Mana1")
|
|
PROP_Weapon_AmmoType2 ("Mana2")
|
|
PROP_Weapon_ProjectileType ("HolyMissile")
|
|
PROP_Inventory_PickupMessage("$TXT_WEAPON_C4")
|
|
END_DEFAULTS
|
|
|
|
// Holy Missile -------------------------------------------------------------
|
|
|
|
class AHolyMissile : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHolyMissile, AActor)
|
|
};
|
|
|
|
FState AHolyMissile::States[] =
|
|
{
|
|
S_BRIGHT (SPIR, 'P', 3, A_CHolySpawnPuff , &States[1]),
|
|
S_BRIGHT (SPIR, 'P', 3, A_CHolySpawnPuff , &States[2]),
|
|
S_BRIGHT (SPIR, 'P', 3, A_CHolySpawnPuff , &States[3]),
|
|
S_BRIGHT (SPIR, 'P', 3, A_CHolySpawnPuff , &States[4]),
|
|
S_BRIGHT (SPIR, 'P', 1, A_CHolyAttack2 , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHolyMissile, Hexen, -1, 0)
|
|
PROP_SpeedFixed (30)
|
|
PROP_RadiusFixed (15)
|
|
PROP_HeightFixed (8)
|
|
PROP_Damage (4)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE)
|
|
PROP_Flags2 (MF2_NOTELEPORT)
|
|
PROP_Flags4 (MF4_EXTREMEDEATH)
|
|
|
|
PROP_SpawnState (0)
|
|
PROP_DeathState (4)
|
|
END_DEFAULTS
|
|
|
|
// Holy Missile Puff --------------------------------------------------------
|
|
|
|
class AHolyMissilePuff : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHolyMissilePuff, AActor)
|
|
};
|
|
|
|
FState AHolyMissilePuff::States[] =
|
|
{
|
|
S_NORMAL (SPIR, 'Q', 3, NULL , &States[1]),
|
|
S_NORMAL (SPIR, 'R', 3, NULL , &States[2]),
|
|
S_NORMAL (SPIR, 'S', 3, NULL , &States[3]),
|
|
S_NORMAL (SPIR, 'T', 3, NULL , &States[4]),
|
|
S_NORMAL (SPIR, 'U', 3, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHolyMissilePuff, Hexen, -1, 0)
|
|
PROP_RadiusFixed (4)
|
|
PROP_HeightFixed (8)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF)
|
|
PROP_Flags2 (MF2_NOTELEPORT)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HX_ALTSHADOW)
|
|
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
// Holy Puff ----------------------------------------------------------------
|
|
|
|
class AHolyPuff : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHolyPuff, AActor)
|
|
};
|
|
|
|
FState AHolyPuff::States[] =
|
|
{
|
|
S_NORMAL (SPIR, 'K', 3, NULL , &States[1]),
|
|
S_NORMAL (SPIR, 'L', 3, NULL , &States[2]),
|
|
S_NORMAL (SPIR, 'M', 3, NULL , &States[3]),
|
|
S_NORMAL (SPIR, 'N', 3, NULL , &States[4]),
|
|
S_NORMAL (SPIR, 'O', 3, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHolyPuff, Hexen, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HX_SHADOW)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
|
|
// Holy Spirit --------------------------------------------------------------
|
|
|
|
FState AHolySpirit::States[] =
|
|
{
|
|
#define S_HOLY_FX1 0
|
|
S_NORMAL (SPIR, 'A', 2, A_CHolySeek , &States[S_HOLY_FX1+1]),
|
|
S_NORMAL (SPIR, 'A', 2, A_CHolySeek , &States[S_HOLY_FX1+2]),
|
|
S_NORMAL (SPIR, 'B', 2, A_CHolySeek , &States[S_HOLY_FX1+3]),
|
|
S_NORMAL (SPIR, 'B', 2, A_CHolyCheckScream , &States[S_HOLY_FX1]),
|
|
|
|
#define S_HOLY_FX_X1 (S_HOLY_FX1+4)
|
|
S_NORMAL (SPIR, 'D', 4, NULL , &States[S_HOLY_FX_X1+1]),
|
|
S_NORMAL (SPIR, 'E', 4, A_Scream , &States[S_HOLY_FX_X1+2]),
|
|
S_NORMAL (SPIR, 'F', 4, NULL , &States[S_HOLY_FX_X1+3]),
|
|
S_NORMAL (SPIR, 'G', 4, NULL , &States[S_HOLY_FX_X1+4]),
|
|
S_NORMAL (SPIR, 'H', 4, NULL , &States[S_HOLY_FX_X1+5]),
|
|
S_NORMAL (SPIR, 'I', 4, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHolySpirit, Hexen, -1, 0)
|
|
PROP_SpawnHealth (105)
|
|
PROP_SpeedFixed (12)
|
|
PROP_RadiusFixed (10)
|
|
PROP_HeightFixed (6)
|
|
PROP_Damage (3)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_RIP|MF2_IMPACT|MF2_PCROSS|MF2_SEEKERMISSILE)
|
|
PROP_Flags3 (MF3_FOILINVUL|MF3_SKYEXPLODE|MF3_NOEXPLODEFLOOR|MF3_CANBLAST)
|
|
PROP_Flags4 (MF4_EXTREMEDEATH)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HX_ALTSHADOW)
|
|
|
|
PROP_SpawnState (S_HOLY_FX1)
|
|
PROP_DeathState (S_HOLY_FX_X1)
|
|
|
|
PROP_DeathSound ("SpiritDie")
|
|
END_DEFAULTS
|
|
|
|
bool AHolySpirit::Slam (AActor *thing)
|
|
{
|
|
if (thing->flags&MF_SHOOTABLE && thing != target)
|
|
{
|
|
if (multiplayer && !deathmatch && thing->player && target->player)
|
|
{ // don't attack other co-op players
|
|
return true;
|
|
}
|
|
if (thing->flags2&MF2_REFLECTIVE
|
|
&& (thing->player || thing->flags2&MF2_BOSS))
|
|
{
|
|
tracer = target;
|
|
target = thing;
|
|
return true;
|
|
}
|
|
if (thing->flags3&MF3_ISMONSTER || thing->player)
|
|
{
|
|
tracer = thing;
|
|
}
|
|
if (pr_spiritslam() < 96)
|
|
{
|
|
int dam = 12;
|
|
if (thing->player || thing->flags2&MF2_BOSS)
|
|
{
|
|
dam = 3;
|
|
// ghost burns out faster when attacking players/bosses
|
|
health -= 6;
|
|
}
|
|
P_DamageMobj (thing, this, target, dam, NAME_Melee);
|
|
if (pr_spiritslam() < 128)
|
|
{
|
|
Spawn<AHolyPuff> (x, y, z, ALLOW_REPLACE);
|
|
S_Sound (this, CHAN_WEAPON, "SpiritAttack", 1, ATTN_NORM);
|
|
if (thing->flags3&MF3_ISMONSTER && pr_spiritslam() < 128)
|
|
{
|
|
thing->Howl ();
|
|
}
|
|
}
|
|
}
|
|
if (thing->health <= 0)
|
|
{
|
|
tracer = NULL;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AHolySpirit::SpecialBlastHandling (AActor *source, fixed_t strength)
|
|
{
|
|
if (strength == BLAST_FULLSTRENGTH && tracer == source)
|
|
{
|
|
tracer = target;
|
|
target = source;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AHolySpirit::IsOkayToAttack (AActor *link)
|
|
{
|
|
if ((link->flags3&MF3_ISMONSTER ||
|
|
(link->player && link != target))
|
|
&& !(link->flags2&MF2_DORMANT))
|
|
{
|
|
if (!(link->flags&MF_SHOOTABLE))
|
|
{
|
|
return false;
|
|
}
|
|
if (multiplayer && !deathmatch && link->player && target->player)
|
|
{
|
|
return false;
|
|
}
|
|
if (link == target)
|
|
{
|
|
return false;
|
|
}
|
|
else if (P_CheckSight (this, link))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Holy Tail ----------------------------------------------------------------
|
|
|
|
class AHolyTail : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHolyTail, AActor)
|
|
};
|
|
|
|
FState AHolyTail::States[] =
|
|
{
|
|
S_NORMAL (SPIR, 'C', 1, A_CHolyTail , &States[0]),
|
|
S_NORMAL (SPIR, 'D', -1, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHolyTail, Hexen, -1, 0)
|
|
PROP_RadiusFixed (1)
|
|
PROP_HeightFixed (1)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_NOCLIP)
|
|
PROP_Flags2 (MF2_NOTELEPORT)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HX_ALTSHADOW)
|
|
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
// Holy Tail Trail ---------------------------------------------------------
|
|
|
|
class AHolyTailTrail : public AHolyTail
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AHolyTailTrail, AHolyTail)
|
|
};
|
|
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AHolyTailTrail, Hexen, -1, 0)
|
|
PROP_SpawnState (1)
|
|
END_DEFAULTS
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyAttack3
|
|
//
|
|
// Spawns the spirits
|
|
//============================================================================
|
|
|
|
void A_CHolyAttack3 (AActor *actor)
|
|
{
|
|
AActor * missile = P_SpawnMissileZ (actor, actor->z + 40*FRACUNIT, actor->target, RUNTIME_CLASS(AHolyMissile));
|
|
if (missile != NULL) missile->tracer = NULL; // No initial target
|
|
S_Sound (actor, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyAttack2
|
|
//
|
|
// Spawns the spirits
|
|
//============================================================================
|
|
|
|
void A_CHolyAttack2 (AActor *actor)
|
|
{
|
|
int j;
|
|
int i;
|
|
AActor *mo;
|
|
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
mo = Spawn<AHolySpirit> (actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
if (!mo)
|
|
{
|
|
continue;
|
|
}
|
|
switch (j)
|
|
{ // float bob index
|
|
case 0:
|
|
mo->special2 = pr_holyatk2()&7; // upper-left
|
|
break;
|
|
case 1:
|
|
mo->special2 = 32+(pr_holyatk2()&7); // upper-right
|
|
break;
|
|
case 2:
|
|
mo->special2 = (32+(pr_holyatk2()&7))<<16; // lower-left
|
|
break;
|
|
case 3:
|
|
i = pr_holyatk2();
|
|
mo->special2 = ((32+(i&7))<<16)+32+(pr_holyatk2()&7);
|
|
break;
|
|
}
|
|
mo->z = actor->z;
|
|
mo->angle = actor->angle+(ANGLE_45+ANGLE_45/2)-ANGLE_45*j;
|
|
P_ThrustMobj(mo, mo->angle, mo->Speed);
|
|
mo->target = actor->target;
|
|
mo->args[0] = 10; // initial turn value
|
|
mo->args[1] = 0; // initial look angle
|
|
if (deathmatch)
|
|
{ // Ghosts last slightly less longer in DeathMatch
|
|
mo->health = 85;
|
|
}
|
|
if (actor->tracer)
|
|
{
|
|
mo->tracer = actor->tracer;
|
|
mo->flags |= MF_NOCLIP|MF_SKULLFLY;
|
|
mo->flags &= ~MF_MISSILE;
|
|
}
|
|
SpawnSpiritTail (mo);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// SpawnSpiritTail
|
|
//
|
|
//============================================================================
|
|
|
|
void SpawnSpiritTail (AActor *spirit)
|
|
{
|
|
AActor *tail, *next;
|
|
int i;
|
|
|
|
tail = Spawn<AHolyTail> (spirit->x, spirit->y, spirit->z, ALLOW_REPLACE);
|
|
tail->target = spirit; // parent
|
|
for (i = 1; i < 3; i++)
|
|
{
|
|
next = Spawn<AHolyTailTrail> (spirit->x, spirit->y, spirit->z, ALLOW_REPLACE);
|
|
tail->tracer = next;
|
|
tail = next;
|
|
}
|
|
tail->tracer = NULL; // last tail bit
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyAttack
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyAttack (AActor *actor)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (actor->player->ReadyWeapon);
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
AActor * missile = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(AHolyMissile));
|
|
if (missile != NULL) missile->tracer = linetarget;
|
|
|
|
weapon->CHolyCount = 3;
|
|
S_Sound (actor, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyPalette
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyPalette (AActor *actor)
|
|
{
|
|
if (actor->player != NULL)
|
|
{
|
|
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (actor->player->ReadyWeapon);
|
|
if (weapon != NULL && weapon->CHolyCount != 0)
|
|
{
|
|
weapon->CHolyCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyTailFollow
|
|
//
|
|
//============================================================================
|
|
|
|
static void CHolyTailFollow (AActor *actor, fixed_t dist)
|
|
{
|
|
AActor *child;
|
|
int an;
|
|
fixed_t oldDistance, newDistance;
|
|
|
|
while (actor)
|
|
{
|
|
child = actor->tracer;
|
|
if (child)
|
|
{
|
|
an = R_PointToAngle2(actor->x, actor->y, child->x,
|
|
child->y)>>ANGLETOFINESHIFT;
|
|
oldDistance = P_AproxDistance (child->x-actor->x, child->y-actor->y);
|
|
if (P_TryMove (child, actor->x+FixedMul(dist, finecosine[an]),
|
|
actor->y+FixedMul(dist, finesine[an]), true))
|
|
{
|
|
newDistance = P_AproxDistance (child->x-actor->x,
|
|
child->y-actor->y)-FRACUNIT;
|
|
if (oldDistance < FRACUNIT)
|
|
{
|
|
if (child->z < actor->z)
|
|
{
|
|
child->z = actor->z-dist;
|
|
}
|
|
else
|
|
{
|
|
child->z = actor->z+dist;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
child->z = actor->z + Scale (newDistance, child->z-actor->z, oldDistance);
|
|
}
|
|
}
|
|
}
|
|
actor = child;
|
|
dist -= FRACUNIT;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyTailRemove
|
|
//
|
|
//============================================================================
|
|
|
|
static void CHolyTailRemove (AActor *actor)
|
|
{
|
|
AActor *next;
|
|
|
|
while (actor)
|
|
{
|
|
next = actor->tracer;
|
|
actor->Destroy ();
|
|
actor = next;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyTail
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyTail (AActor *actor)
|
|
{
|
|
AActor *parent;
|
|
|
|
parent = actor->target;
|
|
|
|
if (parent == NULL || parent->health <= 0) // better check for health than current state - it's safer!
|
|
{ // Ghost removed, so remove all tail parts
|
|
CHolyTailRemove (actor);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (P_TryMove (actor,
|
|
parent->x - 14*finecosine[parent->angle>>ANGLETOFINESHIFT],
|
|
parent->y - 14*finesine[parent->angle>>ANGLETOFINESHIFT], true))
|
|
{
|
|
actor->z = parent->z-5*FRACUNIT;
|
|
}
|
|
CHolyTailFollow (actor, 10*FRACUNIT);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyFindTarget
|
|
//
|
|
//============================================================================
|
|
|
|
static void CHolyFindTarget (AActor *actor)
|
|
{
|
|
AActor *target;
|
|
|
|
if ( (target = P_RoughMonsterSearch (actor, 6)) )
|
|
{
|
|
actor->tracer = target;
|
|
actor->flags |= MF_NOCLIP|MF_SKULLFLY;
|
|
actor->flags &= ~MF_MISSILE;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolySeekerMissile
|
|
//
|
|
// Similar to P_SeekerMissile, but seeks to a random Z on the target
|
|
//============================================================================
|
|
|
|
static void CHolySeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax)
|
|
{
|
|
int dir;
|
|
int dist;
|
|
angle_t delta;
|
|
angle_t angle;
|
|
AActor *target;
|
|
fixed_t newZ;
|
|
fixed_t deltaZ;
|
|
|
|
target = actor->tracer;
|
|
if (target == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if(!(target->flags&MF_SHOOTABLE)
|
|
|| (!(target->flags3&MF3_ISMONSTER) && !target->player))
|
|
{ // Target died/target isn't a player or creature
|
|
actor->tracer = NULL;
|
|
actor->flags &= ~(MF_NOCLIP|MF_SKULLFLY);
|
|
actor->flags |= MF_MISSILE;
|
|
CHolyFindTarget (actor);
|
|
return;
|
|
}
|
|
dir = P_FaceMobj (actor, target, &delta);
|
|
if (delta > thresh)
|
|
{
|
|
delta >>= 1;
|
|
if (delta > turnMax)
|
|
{
|
|
delta = turnMax;
|
|
}
|
|
}
|
|
if (dir)
|
|
{ // Turn clockwise
|
|
actor->angle += delta;
|
|
}
|
|
else
|
|
{ // Turn counter clockwise
|
|
actor->angle -= delta;
|
|
}
|
|
angle = actor->angle>>ANGLETOFINESHIFT;
|
|
actor->momx = FixedMul (actor->Speed, finecosine[angle]);
|
|
actor->momy = FixedMul (actor->Speed, finesine[angle]);
|
|
if (!(level.time&15)
|
|
|| actor->z > target->z+(target->height)
|
|
|| actor->z+actor->height < target->z)
|
|
{
|
|
newZ = target->z+((pr_holyseeker()*target->height)>>8);
|
|
deltaZ = newZ-actor->z;
|
|
if (abs(deltaZ) > 15*FRACUNIT)
|
|
{
|
|
if (deltaZ > 0)
|
|
{
|
|
deltaZ = 15*FRACUNIT;
|
|
}
|
|
else
|
|
{
|
|
deltaZ = -15*FRACUNIT;
|
|
}
|
|
}
|
|
dist = P_AproxDistance (target->x-actor->x, target->y-actor->y);
|
|
dist = dist / actor->Speed;
|
|
if (dist < 1)
|
|
{
|
|
dist = 1;
|
|
}
|
|
actor->momz = deltaZ/dist;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyWeave
|
|
//
|
|
//============================================================================
|
|
|
|
static void CHolyWeave (AActor *actor)
|
|
{
|
|
fixed_t newX, newY;
|
|
int weaveXY, weaveZ;
|
|
int angle;
|
|
|
|
weaveXY = actor->special2>>16;
|
|
weaveZ = actor->special2&0xFFFF;
|
|
angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
|
|
newX = actor->x-FixedMul(finecosine[angle],
|
|
FloatBobOffsets[weaveXY]<<2);
|
|
newY = actor->y-FixedMul(finesine[angle],
|
|
FloatBobOffsets[weaveXY]<<2);
|
|
weaveXY = (weaveXY+(pr_holyweave()%5))&63;
|
|
newX += FixedMul(finecosine[angle],
|
|
FloatBobOffsets[weaveXY]<<2);
|
|
newY += FixedMul(finesine[angle],
|
|
FloatBobOffsets[weaveXY]<<2);
|
|
P_TryMove(actor, newX, newY, true);
|
|
actor->z -= FloatBobOffsets[weaveZ]<<1;
|
|
weaveZ = (weaveZ+(pr_holyweave()%5))&63;
|
|
actor->z += FloatBobOffsets[weaveZ]<<1;
|
|
actor->special2 = weaveZ+(weaveXY<<16);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolySeek
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolySeek (AActor *actor)
|
|
{
|
|
actor->health--;
|
|
if (actor->health <= 0)
|
|
{
|
|
actor->momx >>= 2;
|
|
actor->momy >>= 2;
|
|
actor->momz = 0;
|
|
actor->SetState (actor->FindState(NAME_Death));
|
|
actor->tics -= pr_holyseek()&3;
|
|
return;
|
|
}
|
|
if (actor->tracer)
|
|
{
|
|
CHolySeekerMissile (actor, actor->args[0]*ANGLE_1,
|
|
actor->args[0]*ANGLE_1*2);
|
|
if (!((level.time+7)&15))
|
|
{
|
|
actor->args[0] = 5+(pr_holyseek()/20);
|
|
}
|
|
}
|
|
CHolyWeave (actor);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyCheckScream
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyCheckScream (AActor *actor)
|
|
{
|
|
A_CHolySeek (actor);
|
|
if (pr_checkscream() < 20)
|
|
{
|
|
S_Sound (actor, CHAN_VOICE, "SpiritActive", 1, ATTN_NORM);
|
|
}
|
|
if (!actor->tracer)
|
|
{
|
|
CHolyFindTarget(actor);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolySpawnPuff
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolySpawnPuff (AActor *actor)
|
|
{
|
|
Spawn<AHolyMissilePuff> (actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
}
|
|
|
|
void AClericWeaponPiece::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
FourthWeaponClass = RUNTIME_CLASS(ACWeapWraithverge);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_DropWraithvergePieces
|
|
//
|
|
//============================================================================
|
|
|
|
void A_DropWraithvergePieces (AActor *actor)
|
|
{
|
|
static const PClass *pieces[3] =
|
|
{
|
|
RUNTIME_CLASS(ACWeaponPiece1),
|
|
RUNTIME_CLASS(ACWeaponPiece2),
|
|
RUNTIME_CLASS(ACWeaponPiece3)
|
|
};
|
|
|
|
for (int i = 0, j = 0, fineang = 0; i < 3; ++i)
|
|
{
|
|
AActor *piece = Spawn (pieces[j], actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
if (piece != NULL)
|
|
{
|
|
piece->momx = actor->momx + finecosine[fineang];
|
|
piece->momy = actor->momy + finesine[fineang];
|
|
piece->momz = actor->momz;
|
|
piece->flags |= MF_DROPPED;
|
|
fineang += FINEANGLES/3;
|
|
j = (j == 0) ? (pr_wraithvergedrop() & 1) + 1 : 3-j;
|
|
}
|
|
}
|
|
}
|