qzdoom/src/g_hexen/a_clericholy.cpp
Christoph Oelckers a6a5821f04 - Added a compatibility option to restore the original behavior of the Invisibility
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)
2007-05-12 11:14:09 +00:00

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