mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-08 05:51:09 +00:00
1bd6ac028b
- Added a PickupMessage property to the internal actor parser, replaced most of the virtual PickupMessages with it and placed the code that reads the metadata into AInventory::PickupMessage. Now the PickupMessage method is truly virtual and I can do: Added a Health.LowMessage property to define double message items like Doom's medikit in DECORATE. - Since defining Mana3 as an ammo type and then overriding the TryPickup method means that this item defeats all ammo checks in the game it might as well be defined as a CustomInventory item. At least this fixes the amount given in easy and very hard skills. - Converted all ammo items to DECORATE. - Changed internal property setting of ammo types and sister weapons to use fuglyname as for DECORATE definitions. This allows to export the ammo definitions into DECORATE definitions without doing it for the weapons themselves. - Replaced obituary methods with actor properties. - Fixed: The secret map check didn't work for maps inside Zips. SVN r196 (trunk)
908 lines
23 KiB
C++
908 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 (RUNTIME_CLASS(AFighterPlayer)) &&
|
|
!toucher->IsKindOf (RUNTIME_CLASS(AMagePlayer));
|
|
}
|
|
|
|
//==========================================================================
|
|
|
|
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 | WIF_EXTREME_DEATH)
|
|
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_HoldAtkState (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_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_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, MOD_HIT);
|
|
if (pr_spiritslam() < 128)
|
|
{
|
|
Spawn<AHolyPuff> (x, y, z);
|
|
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);
|
|
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);
|
|
tail->target = spirit; // parent
|
|
for (i = 1; i < 3; i++)
|
|
{
|
|
next = Spawn<AHolyTailTrail> (spirit->x, spirit->y, spirit->z);
|
|
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->state >= parent->DeathState)
|
|
{ // 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->DeathState);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
}
|