ref: e07e07242c04919d40513ffe14f5f1a94ce0b412
dir: /p_pspr.c/
//**************************************************************************
//**
//** p_pspr.c : Heretic 2 : Raven Software, Corp.
//**
//** $Revision: 575 $
//** $Date: 2011-04-13 22:06:28 +0300 (Wed, 13 Apr 2011) $
//**
//**************************************************************************
// HEADER FILES ------------------------------------------------------------
#include "h2stdinc.h"
#include "h2def.h"
#include "p_local.h"
#include "soundst.h"
#include "v_compat.h" /* V_SetPaletteXXX() macros */
// MACROS ------------------------------------------------------------------
#define LOWERSPEED FRACUNIT*6
#define RAISESPEED FRACUNIT*6
#define WEAPONBOTTOM 128*FRACUNIT
#define WEAPONTOP 32*FRACUNIT
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
extern void P_ExplodeMissile(mobj_t *mo);
extern void A_UnHideThing(mobj_t *actor);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern fixed_t FloatBobOffsets[64];
// PUBLIC DATA DEFINITIONS -------------------------------------------------
weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES] =
{
{ // First Weapons
{ // Fighter First Weapon - Punch
MANA_NONE, // mana
S_PUNCHUP, // upstate
S_PUNCHDOWN, // downstate
S_PUNCHREADY, // readystate
S_PUNCHATK1_1, // atkstate
S_PUNCHATK1_1, // holdatkstate
S_NULL // flashstate
},
{ // Cleric First Weapon - Mace
MANA_NONE, // mana
S_CMACEUP, // upstate
S_CMACEDOWN, // downstate
S_CMACEREADY, // readystate
S_CMACEATK_1, // atkstate
S_CMACEATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Mage First Weapon - Wand
MANA_NONE,
S_MWANDUP,
S_MWANDDOWN,
S_MWANDREADY,
S_MWANDATK_1,
S_MWANDATK_1,
S_NULL
},
#ifdef ASSASSIN
{ // Assassin - Katar
MANA_NONE,
S_KATARUP,
S_KATARDOWN,
S_KATARREADY,
S_KATARATK1_1,
S_KATARATK1_1,
S_NULL
},
#endif
{ // Pig - Snout
MANA_NONE, // mana
S_SNOUTUP, // upstate
S_SNOUTDOWN, // downstate
S_SNOUTREADY, // readystate
S_SNOUTATK1, // atkstate
S_SNOUTATK1, // holdatkstate
S_NULL // flashstate
}
},
{ // Second Weapons
{ // Fighter - Axe
MANA_NONE, // mana
S_FAXEUP, // upstate
S_FAXEDOWN, // downstate
S_FAXEREADY, // readystate
S_FAXEATK_1, // atkstate
S_FAXEATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Cleric - Serpent Staff
MANA_1, // mana
S_CSTAFFUP, // upstate
S_CSTAFFDOWN, // downstate
S_CSTAFFREADY, // readystate
S_CSTAFFATK_1, // atkstate
S_CSTAFFATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Mage - Cone of shards
MANA_1, // mana
S_CONEUP, // upstate
S_CONEDOWN, // downstate
S_CONEREADY, // readystate
S_CONEATK1_1, // atkstate
S_CONEATK1_3, // holdatkstate
S_NULL // flashstate
},
#ifdef ASSASSIN
{ // Assassin - Hand Crossbow
MANA_1,
S_ACROSSUP,
S_ACROSSDOWN,
S_ACROSSREADY,
S_ACROSSATK_1,
S_ACROSSATK_3,
S_NULL
},
#endif
{ // Pig - Snout
MANA_NONE, // mana
S_SNOUTUP, // upstate
S_SNOUTDOWN, // downstate
S_SNOUTREADY, // readystate
S_SNOUTATK1, // atkstate
S_SNOUTATK1, // holdatkstate
S_NULL // flashstate
}
},
{ // Third Weapons
{ // Fighter - Hammer
MANA_NONE, // mana
S_FHAMMERUP, // upstate
S_FHAMMERDOWN, // downstate
S_FHAMMERREADY, // readystate
S_FHAMMERATK_1, // atkstate
S_FHAMMERATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Cleric - Flame Strike
MANA_2, // mana
S_CFLAMEUP, // upstate
S_CFLAMEDOWN, // downstate
S_CFLAMEREADY1, // readystate
S_CFLAMEATK_1, // atkstate
S_CFLAMEATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Mage - Lightning
MANA_2, // mana
S_MLIGHTNINGUP, // upstate
S_MLIGHTNINGDOWN, // downstate
S_MLIGHTNINGREADY, // readystate
S_MLIGHTNINGATK_1, // atkstate
S_MLIGHTNINGATK_1, // holdatkstate
S_NULL // flashstate
},
#ifdef ASSASSIN
{ // Assassin - Grenades
MANA_2,
S_AGRENUP,
S_AGRENDOWN,
S_AGRENREADY,
S_AGRENATK_1,
S_AGRENATK_1,
S_NULL
},
#endif
{ // Pig - Snout
MANA_NONE, // mana
S_SNOUTUP, // upstate
S_SNOUTDOWN, // downstate
S_SNOUTREADY, // readystate
S_SNOUTATK1, // atkstate
S_SNOUTATK1, // holdatkstate
S_NULL // flashstate
}
},
{ // Fourth Weapons
{ // Fighter - Rune Sword
MANA_BOTH, // mana
S_FSWORDUP, // upstate
S_FSWORDDOWN, // downstate
S_FSWORDREADY, // readystate
S_FSWORDATK_1, // atkstate
S_FSWORDATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Cleric - Holy Symbol
MANA_BOTH, // mana
S_CHOLYUP, // upstate
S_CHOLYDOWN, // downstate
S_CHOLYREADY, // readystate
S_CHOLYATK_1, // atkstate
S_CHOLYATK_1, // holdatkstate
S_NULL // flashstate
},
{ // Mage - Staff
MANA_BOTH, // mana
S_MSTAFFUP, // upstate
S_MSTAFFDOWN, // downstate
S_MSTAFFREADY, // readystate
S_MSTAFFATK_1, // atkstate
S_MSTAFFATK_1, // holdatkstate
S_NULL // flashstate
},
#ifdef ASSASSIN
{ // Assassin - Staff of Set
MANA_BOTH,
S_ASTAFFUP,
S_ASTAFFDOWN,
S_ASTAFFREADY,
S_ASTAFFATK_1,
S_ASTAFFATK_1,
S_NULL
},
#endif
{ // Pig - Snout
MANA_NONE, // mana
S_SNOUTUP, // upstate
S_SNOUTDOWN, // downstate
S_SNOUTREADY, // readystate
S_SNOUTATK1, // atkstate
S_SNOUTATK1, // holdatkstate
S_NULL // flashstate
}
}
};
// PRIVATE DATA DEFINITIONS ------------------------------------------------
//fixed_t bulletslope; // for P_BulletSlope()
static int WeaponManaUse[NUMCLASSES][NUMWEAPONS] =
{
{ 0, 2, 3, 14 },
{ 0, 1, 4, 18 },
{ 0, 3, 5, 15 },
#ifdef ASSASSIN
{ 0, 3, 3, 1 }, // True to Hexen II
#endif
{ 0, 0, 0, 0 }
};
// CODE --------------------------------------------------------------------
//---------------------------------------------------------------------------
//
// PROC P_SetPsprite
//
//---------------------------------------------------------------------------
void P_SetPsprite(player_t *player, int position, statenum_t stnum)
{
pspdef_t *psp;
state_t *state;
psp = &player->psprites[position];
do
{
if (!stnum)
{ // Object removed itself.
psp->state = NULL;
break;
}
state = &states[stnum];
psp->state = state;
psp->tics = state->tics; // could be 0
if (state->misc1)
{ // Set coordinates.
psp->sx = state->misc1<<FRACBITS;
}
if (state->misc2)
{
psp->sy = state->misc2<<FRACBITS;
}
if (state->action)
{ // Call action routine.
state->action(player, psp);
if (!psp->state)
{
break;
}
}
stnum = psp->state->nextstate;
} while (!psp->tics); // An initial state of 0 could cycle through.
}
//---------------------------------------------------------------------------
//
// PROC P_SetPspriteNF
//
// Identical to P_SetPsprite, without calling the action function
//---------------------------------------------------------------------------
void P_SetPspriteNF(player_t *player, int position, statenum_t stnum)
{
pspdef_t *psp;
state_t *state;
psp = &player->psprites[position];
do
{
if (!stnum)
{ // Object removed itself.
psp->state = NULL;
break;
}
state = &states[stnum];
psp->state = state;
psp->tics = state->tics; // could be 0
if (state->misc1)
{ // Set coordinates.
psp->sx = state->misc1<<FRACBITS;
}
if (state->misc2)
{
psp->sy = state->misc2<<FRACBITS;
}
stnum = psp->state->nextstate;
} while (!psp->tics); // An initial state of 0 could cycle through.
}
/*
=================
=
= P_CalcSwing
=
=================
*/
/*
fixed_t swingx, swingy;
void P_CalcSwing (player_t *player)
{
fixed_t swing;
int angle;
// OPTIMIZE: tablify this
swing = player->bob;
angle = (FINEANGLES/70*leveltime) & FINEMASK;
swingx = FixedMul (swing, finesine[angle]);
angle = (FINEANGLES/70*leveltime + FINEANGLES/2) & FINEMASK;
swingy = -FixedMul (swingx, finesine[angle]);
}
*/
//---------------------------------------------------------------------------
//
// PROC P_ActivateMorphWeapon
//
//---------------------------------------------------------------------------
void P_ActivateMorphWeapon(player_t *player)
{
player->pendingweapon = WP_NOCHANGE;
player->psprites[ps_weapon].sy = WEAPONTOP;
player->readyweapon = WP_FIRST; // Snout is the first weapon
P_SetPsprite(player, ps_weapon, S_SNOUTREADY);
}
//---------------------------------------------------------------------------
//
// PROC P_PostMorphWeapon
//
//---------------------------------------------------------------------------
void P_PostMorphWeapon(player_t *player, weapontype_t weapon)
{
player->pendingweapon = WP_NOCHANGE;
player->readyweapon = weapon;
player->psprites[ps_weapon].sy = WEAPONBOTTOM;
P_SetPsprite(player, ps_weapon, WeaponInfo[weapon][player->playerclass].upstate);
}
//---------------------------------------------------------------------------
//
// PROC P_BringUpWeapon
//
// Starts bringing the pending weapon up from the bottom of the screen.
//
//---------------------------------------------------------------------------
void P_BringUpWeapon(player_t *player)
{
statenum_t newstate;
if (player->pendingweapon == WP_NOCHANGE)
{
player->pendingweapon = player->readyweapon;
}
if (player->playerclass == PCLASS_FIGHTER && player->pendingweapon == WP_SECOND
&& player->mana[MANA_1])
{
newstate = S_FAXEUP_G;
}
else
{
newstate = WeaponInfo[player->pendingweapon][player->playerclass].upstate;
}
player->pendingweapon = WP_NOCHANGE;
player->psprites[ps_weapon].sy = WEAPONBOTTOM;
P_SetPsprite(player, ps_weapon, newstate);
}
//---------------------------------------------------------------------------
//
// FUNC P_CheckMana
//
// Returns true if there is enough mana to shoot. If not, selects the
// next weapon to use.
//
//---------------------------------------------------------------------------
static boolean P_CheckMana(player_t *player)
{
manatype_t mana;
int count;
mana = WeaponInfo[player->readyweapon][player->playerclass].mana;
count = WeaponManaUse[player->playerclass][player->readyweapon];
if (mana == MANA_BOTH)
{
if (player->mana[MANA_1] >= count && player->mana[MANA_2] >= count)
{
return true;
}
}
else if (mana == MANA_NONE || player->mana[mana] >= count)
{
return true;
}
// out of mana, pick a weapon to change to
do
{
if (player->weaponowned[WP_THIRD]
&& player->mana[MANA_2] >= WeaponManaUse[player->playerclass][WP_THIRD])
{
player->pendingweapon = WP_THIRD;
}
else if (player->weaponowned[WP_SECOND]
&& player->mana[MANA_1] >= WeaponManaUse[player->playerclass][WP_SECOND])
{
player->pendingweapon = WP_SECOND;
}
else if (player->weaponowned[WP_FOURTH]
&& player->mana[MANA_1] >= WeaponManaUse[player->playerclass][WP_FOURTH]
&& player->mana[MANA_2] >= WeaponManaUse[player->playerclass][WP_FOURTH])
{
player->pendingweapon = WP_FOURTH;
}
else
{
player->pendingweapon = WP_FIRST;
}
} while (player->pendingweapon == WP_NOCHANGE);
P_SetPsprite(player, ps_weapon,
WeaponInfo[player->readyweapon][player->playerclass].downstate);
return false;
}
//---------------------------------------------------------------------------
//
// PROC P_FireWeapon
//
//---------------------------------------------------------------------------
static void P_FireWeapon(player_t *player)
{
statenum_t attackState;
if (!P_CheckMana(player))
{
return;
}
P_SetMobjState(player->mo, PStateAttack[player->playerclass]); // S_PLAY_ATK1);
if (player->playerclass == PCLASS_FIGHTER && player->readyweapon == WP_SECOND
&& player->mana[MANA_1] > 0)
{ // Glowing axe
attackState = S_FAXEATK_G1;
}
else
{
attackState = player->refire ?
WeaponInfo[player->readyweapon][player->playerclass].holdatkstate
: WeaponInfo[player->readyweapon][player->playerclass].atkstate;
}
P_SetPsprite(player, ps_weapon, attackState);
P_NoiseAlert(player->mo, player->mo);
}
//---------------------------------------------------------------------------
//
// PROC P_DropWeapon
//
// The player died, so put the weapon away.
//
//---------------------------------------------------------------------------
void P_DropWeapon(player_t *player)
{
P_SetPsprite(player, ps_weapon,
WeaponInfo[player->readyweapon][player->playerclass].downstate);
}
//---------------------------------------------------------------------------
//
// PROC A_WeaponReady
//
// The player can fire the weapon or change to another weapon at this time.
//
//---------------------------------------------------------------------------
void A_WeaponReady(player_t *player, pspdef_t *psp)
{
int angle;
// Change player from attack state
if (player->mo->state >= &states[PStateAttack[player->playerclass]]
&& player->mo->state <= &states[PStateAttackEnd[player->playerclass]])
{
P_SetMobjState(player->mo, PStateNormal[player->playerclass]);
}
// Put the weapon away if the player has a pending weapon or has
// died.
if (player->pendingweapon != WP_NOCHANGE || !player->health)
{
P_SetPsprite(player, ps_weapon,
WeaponInfo[player->readyweapon][player->playerclass].downstate);
return;
}
// Check for fire.
if (player->cmd.buttons & BT_ATTACK)
{
player->attackdown = true;
P_FireWeapon(player);
return;
}
else
{
player->attackdown = false;
}
if (!player->morphTics)
{
// Bob the weapon based on movement speed.
angle = (128*leveltime)&FINEMASK;
psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]);
angle &= FINEANGLES/2 - 1;
psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]);
}
}
//---------------------------------------------------------------------------
//
// PROC A_ReFire
//
// The player can re fire the weapon without lowering it entirely.
//
//---------------------------------------------------------------------------
void A_ReFire(player_t *player, pspdef_t *psp)
{
if ((player->cmd.buttons&BT_ATTACK)
&& player->pendingweapon == WP_NOCHANGE && player->health)
{
player->refire++;
P_FireWeapon(player);
}
else
{
player->refire = 0;
P_CheckMana(player);
}
}
//---------------------------------------------------------------------------
//
// PROC A_Lower
//
//---------------------------------------------------------------------------
void A_Lower(player_t *player, pspdef_t *psp)
{
if (player->morphTics)
{
psp->sy = WEAPONBOTTOM;
}
else
{
psp->sy += LOWERSPEED;
}
if (psp->sy < WEAPONBOTTOM)
{ // Not lowered all the way yet
return;
}
if (player->playerstate == PST_DEAD)
{ // Player is dead, so don't bring up a pending weapon
psp->sy = WEAPONBOTTOM;
return;
}
if (!player->health)
{ // Player is dead, so keep the weapon off screen
P_SetPsprite(player, ps_weapon, S_NULL);
return;
}
player->readyweapon = player->pendingweapon;
P_BringUpWeapon(player);
}
//---------------------------------------------------------------------------
//
// PROC A_Raise
//
//---------------------------------------------------------------------------
void A_Raise(player_t *player, pspdef_t *psp)
{
psp->sy -= RAISESPEED;
if (psp->sy > WEAPONTOP)
{ // Not raised all the way yet
return;
}
psp->sy = WEAPONTOP;
if (player->playerclass == PCLASS_FIGHTER && player->readyweapon == WP_SECOND
&& player->mana[MANA_1])
{
P_SetPsprite(player, ps_weapon, S_FAXEREADY_G);
}
else
{
P_SetPsprite(player, ps_weapon,
WeaponInfo[player->readyweapon][player->playerclass].readystate);
}
}
/*
===============
=
= P_BulletSlope
=
= Sets a slope so a near miss is at aproximately the height of the
= intended target
=
===============
*/
/*
static void P_BulletSlope (mobj_t *mo)
{
angle_t an;
//
// see which target is to be aimed at
//
an = mo->angle;
bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
if (!linetarget)
{
an += 1<<26;
bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
if (!linetarget)
{
an -= 2<<26;
bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
}
if (!linetarget)
{
an += 1<<26;
bulletslope = (mo->player->lookdir<<FRACBITS)/173;
}
}
}
*/
//****************************************************************************
//
// WEAPON ATTACKS
//
//****************************************************************************
//============================================================================
//
// AdjustPlayerAngle
//
//============================================================================
#define MAX_ANGLE_ADJUST (5*ANGLE_1)
static void AdjustPlayerAngle(mobj_t *pmo)
{
angle_t angle;
int difference;
angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y);
difference = (int)angle - (int)pmo->angle;
if (abs(difference) > MAX_ANGLE_ADJUST)
{
pmo->angle += difference > 0 ? MAX_ANGLE_ADJUST : -MAX_ANGLE_ADJUST;
}
else
{
pmo->angle = angle;
}
}
//============================================================================
//
// A_SnoutAttack
//
//============================================================================
void A_SnoutAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
int damage;
int slope;
damage = 3 + (P_Random() & 3);
angle = player->mo->angle;
slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
PuffType = MT_SNOUTPUFF;
PuffSpawned = NULL;
P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
S_StartSound(player->mo, SFX_PIG_ACTIVE1 + (P_Random() & 1));
if (linetarget)
{
AdjustPlayerAngle(player->mo);
// player->mo->angle = R_PointToAngle2(player->mo->x,
// player->mo->y, linetarget->x, linetarget->y);
if (PuffSpawned)
{ // Bit something
S_StartSound(player->mo, SFX_PIG_ATTACK);
}
}
}
//============================================================================
//
// A_FHammerAttack
//
//============================================================================
#define HAMMER_RANGE (MELEERANGE+MELEERANGE/2)
void A_FHammerAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
mobj_t *pmo = player->mo;
int damage;
fixed_t power;
int slope;
int i;
damage = 60 + (P_Random() & 63);
power = 10*FRACUNIT;
PuffType = MT_HAMMERPUFF;
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/32);
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage);
AdjustPlayerAngle(pmo);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
pmo->special1 = false; // Don't throw a hammer
goto hammerdone;
}
angle = pmo->angle - i*(ANG45/32);
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage);
AdjustPlayerAngle(pmo);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
pmo->special1 = false; // Don't throw a hammer
goto hammerdone;
}
}
// didn't find any targets in meleerange, so set to throw out a hammer
PuffSpawned = NULL;
angle = pmo->angle;
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE);
P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage);
if (PuffSpawned)
{
pmo->special1 = false;
}
else
{
pmo->special1 = true;
}
hammerdone:
if (player->mana[MANA_2] <
WeaponManaUse[player->playerclass][player->readyweapon])
{ // Don't spawn a hammer if the player doesn't have enough mana
pmo->special1 = false;
}
return;
}
//============================================================================
//
// A_FHammerThrow
//
//============================================================================
void A_FHammerThrow(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
if (!player->mo->special1)
{
return;
}
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
mo = P_SpawnPlayerMissile(player->mo, MT_HAMMER_MISSILE);
if (mo)
{
mo->special1 = 0;
}
}
//============================================================================
//
// A_FSwordAttack
//
//============================================================================
void A_FSwordAttack(player_t *player, pspdef_t *psp)
{
mobj_t *pmo;
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
pmo = player->mo;
P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 10*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45/4);
P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 5*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45/8);
P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z, MT_FSWORD_MISSILE, pmo->angle);
P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 5*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45/8);
P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 10*FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45/4);
S_StartSound(pmo, SFX_FIGHTER_SWORD_FIRE);
}
//============================================================================
//
// A_FSwordAttack2
//
//============================================================================
void A_FSwordAttack2(mobj_t *actor)
{
angle_t angle = actor->angle;
P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle + ANG45/4, 0);
P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle + ANG45/8, 0);
P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle, 0);
P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle - ANG45/8, 0);
P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE,angle - ANG45/4, 0);
S_StartSound(actor, SFX_FIGHTER_SWORD_FIRE);
}
//============================================================================
//
// A_FSwordFlames
//
//============================================================================
void A_FSwordFlames(mobj_t *actor)
{
int i;
for (i = 1 + (P_Random() & 3); i; i--)
{
P_SpawnMobj(actor->x + ((P_Random() - 128) << 12),
actor->y + ((P_Random() - 128) << 12),
actor->z + ((P_Random() - 128) << 11),
MT_FSWORD_FLAME);
}
}
//============================================================================
//
// A_MWandAttack
//
//============================================================================
void A_MWandAttack(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
mo = P_SpawnPlayerMissile(player->mo, MT_MWAND_MISSILE);
if (mo)
{
mo->thinker.function = P_BlasterMobjThinker;
}
S_StartSound(player->mo, SFX_MAGE_WAND_FIRE);
}
// ===== Mage Lightning Weapon =====
//============================================================================
//
// A_LightningReady
//
//============================================================================
void A_LightningReady(player_t *player, pspdef_t *psp)
{
A_WeaponReady(player, psp);
if (P_Random() < 160)
{
S_StartSound(player->mo, SFX_MAGE_LIGHTNING_READY);
}
}
//============================================================================
//
// A_LightningClip
//
//============================================================================
#define ZAGSPEED FRACUNIT
void A_LightningClip(mobj_t *actor)
{
mobj_t *cMo;
mobj_t *target = NULL; /* jim added initialiser */
int zigZag;
if (actor->type == MT_LIGHTNING_FLOOR)
{
actor->z = actor->floorz;
target = (mobj_t *)((mobj_t *)actor->special2)->special1;
}
else if (actor->type == MT_LIGHTNING_CEILING)
{
actor->z = actor->ceilingz - actor->height;
target = (mobj_t *)actor->special1;
}
if (actor->type == MT_LIGHTNING_FLOOR)
{ // floor lightning zig-zags, and forces the ceiling lightning to mimic
cMo = (mobj_t *)actor->special2;
zigZag = P_Random();
if ((zigZag > 128 && actor->special1 < 2) || actor->special1 < -2)
{
P_ThrustMobj(actor, actor->angle + ANG90, ZAGSPEED);
if (cMo)
{
P_ThrustMobj(cMo, actor->angle + ANG90, ZAGSPEED);
}
actor->special1++;
}
else
{
P_ThrustMobj(actor, actor->angle - ANG90, ZAGSPEED);
if (cMo)
{
P_ThrustMobj(cMo, cMo->angle - ANG90, ZAGSPEED);
}
actor->special1--;
}
}
if (target)
{
if (target->health <= 0)
{
P_ExplodeMissile(actor);
}
else
{
actor->angle = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
actor->momx = 0;
actor->momy = 0;
P_ThrustMobj(actor, actor->angle, actor->info->speed>>1);
}
}
}
//============================================================================
//
// A_LightningZap
//
//============================================================================
void A_LightningZap(mobj_t *actor)
{
mobj_t *mo;
fixed_t deltaZ;
A_LightningClip(actor);
actor->health -= 8;
if (actor->health <= 0)
{
P_SetMobjState(actor, actor->info->deathstate);
return;
}
if (actor->type == MT_LIGHTNING_FLOOR)
{
deltaZ = 10*FRACUNIT;
}
else
{
deltaZ = -10*FRACUNIT;
}
mo = P_SpawnMobj(actor->x + ((P_Random() - 128) * actor->radius/256),
actor->y + ((P_Random() - 128) * actor->radius/256),
actor->z + deltaZ, MT_LIGHTNING_ZAP);
if (mo)
{
mo->special2 = (intptr_t)actor;
mo->momx = actor->momx;
mo->momy = actor->momy;
mo->target = actor->target;
if (actor->type == MT_LIGHTNING_FLOOR)
{
mo->momz = 20*FRACUNIT;
}
else
{
mo->momz = -20*FRACUNIT;
}
}
/*
mo = P_SpawnMobj(actor->x + ((P_Random() - 128) * actor->radius/256),
actor->y + ((P_Random() - 128) * actor->radius/256),
actor->z+deltaZ, MT_LIGHTNING_ZAP);
if (mo)
{
mo->special2 = (intptr_t)actor;
mo->momx = actor->momx;
mo->momy = actor->momy;
mo->target = actor->target;
if (actor->type == MT_LIGHTNING_FLOOR)
{
mo->momz = 16*FRACUNIT;
}
else
{
mo->momz = -16*FRACUNIT;
}
}
*/
if (actor->type == MT_LIGHTNING_FLOOR && P_Random() < 160)
{
S_StartSound(actor, SFX_MAGE_LIGHTNING_CONTINUOUS);
}
}
//============================================================================
//
// A_MLightningAttack2
//
//============================================================================
void A_MLightningAttack2(mobj_t *actor)
{
mobj_t *fmo, *cmo;
fmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_FLOOR);
cmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_CEILING);
if (fmo)
{
fmo->special1 = 0;
fmo->special2 = (intptr_t)cmo;
A_LightningZap(fmo);
}
if (cmo)
{
cmo->special1 = 0; // mobj that it will track
cmo->special2 = (intptr_t)fmo;
A_LightningZap(cmo);
}
S_StartSound(actor, SFX_MAGE_LIGHTNING_FIRE);
}
//============================================================================
//
// A_MLightningAttack
//
//============================================================================
void A_MLightningAttack(player_t *player, pspdef_t *psp)
{
A_MLightningAttack2(player->mo);
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
}
//============================================================================
//
// A_ZapMimic
//
//============================================================================
void A_ZapMimic(mobj_t *actor)
{
mobj_t *mo;
mo = (mobj_t *)actor->special2;
if (mo)
{
if (mo->state >= &states[mo->info->deathstate]
|| mo->state == &states[S_FREETARGMOBJ])
{
P_ExplodeMissile(actor);
}
else
{
actor->momx = mo->momx;
actor->momy = mo->momy;
}
}
}
//============================================================================
//
// A_LastZap
//
//============================================================================
void A_LastZap(mobj_t *actor)
{
mobj_t *mo;
mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_LIGHTNING_ZAP);
if (mo)
{
P_SetMobjState(mo, S_LIGHTNING_ZAP_X1);
mo->momz = 40*FRACUNIT;
}
}
//============================================================================
//
// A_LightningRemove
//
//============================================================================
void A_LightningRemove(mobj_t *actor)
{
mobj_t *mo;
mo = (mobj_t *)actor->special2;
if (mo)
{
mo->special2 = 0;
P_ExplodeMissile(mo);
}
}
//============================================================================
//
// MStaffSpawn
//
//============================================================================
static void MStaffSpawn(mobj_t *pmo, angle_t angle)
{
mobj_t *mo;
mo = P_SPMAngle(pmo, MT_MSTAFF_FX2, angle);
if (mo)
{
mo->target = pmo;
mo->special1 = (intptr_t)P_RoughMonsterSearch(mo, 10);
}
}
//============================================================================
//
// A_MStaffAttack
//
//============================================================================
void A_MStaffAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
mobj_t *pmo;
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
pmo = player->mo;
angle = pmo->angle;
MStaffSpawn(pmo, angle);
MStaffSpawn(pmo, angle-ANGLE_1*5);
MStaffSpawn(pmo, angle+ANGLE_1*5);
S_StartSound(player->mo, SFX_MAGE_STAFF_FIRE);
if (player == &players[consoleplayer])
{
player->damagecount = 0;
player->bonuscount = 0;
V_SetPaletteShift(STARTSCOURGEPAL);
}
}
//============================================================================
//
// A_MStaffPalette
//
//============================================================================
void A_MStaffPalette(player_t *player, pspdef_t *psp)
{
int pal;
if (player == &players[consoleplayer])
{
pal = STARTSCOURGEPAL + psp->state - (&states[S_MSTAFFATK_2]);
if (pal == STARTSCOURGEPAL + 3)
{ // reset back to original playpal
pal = 0;
}
V_SetPaletteShift(pal);
}
}
//============================================================================
//
// A_MStaffWeave
//
//============================================================================
void A_MStaffWeave(mobj_t *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 + 6) & 63;
newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2);
newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2);
P_TryMove(actor, newX, newY);
actor->z -= FloatBobOffsets[weaveZ]<<1;
weaveZ = (weaveZ + 3) & 63;
actor->z += FloatBobOffsets[weaveZ]<<1;
if (actor->z <= actor->floorz)
{
actor->z = actor->floorz + FRACUNIT;
}
actor->special2 = weaveZ + (weaveXY<<16);
}
//============================================================================
//
// A_MStaffTrack
//
//============================================================================
void A_MStaffTrack(mobj_t *actor)
{
if ((actor->special1 == 0) && (P_Random() < 50))
{
actor->special1 = (intptr_t)P_RoughMonsterSearch(actor, 10);
}
P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*10);
}
//============================================================================
//
// MStaffSpawn2 - for use by mage class boss
//
//============================================================================
static void MStaffSpawn2(mobj_t *actor, angle_t angle)
{
mobj_t *mo;
mo = P_SpawnMissileAngle(actor, MT_MSTAFF_FX2, angle, 0);
if (mo)
{
mo->target = actor;
mo->special1 = (intptr_t)P_RoughMonsterSearch(mo, 10);
}
}
//============================================================================
//
// A_MStaffAttack2 - for use by mage class boss
//
//============================================================================
void A_MStaffAttack2(mobj_t *actor)
{
angle_t angle;
angle = actor->angle;
MStaffSpawn2(actor, angle);
MStaffSpawn2(actor, angle - ANGLE_1*5);
MStaffSpawn2(actor, angle + ANGLE_1*5);
S_StartSound(actor, SFX_MAGE_STAFF_FIRE);
}
//============================================================================
//
// A_FPunchAttack
//
//============================================================================
void A_FPunchAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
int damage;
int slope;
mobj_t *pmo = player->mo;
fixed_t power;
int i;
damage = 40 + (P_Random() & 15);
power = 2*FRACUNIT;
PuffType = MT_PUNCHPUFF;
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE);
if (linetarget)
{
player->mo->special1++;
if (pmo->special1 == 3)
{
damage <<= 1;
power = 6*FRACUNIT;
PuffType = MT_HAMMERPUFF;
}
P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
goto punchdone;
}
angle = pmo->angle - i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE);
if (linetarget)
{
pmo->special1++;
if (pmo->special1 == 3)
{
damage <<= 1;
power = 6*FRACUNIT;
PuffType = MT_HAMMERPUFF;
}
P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
goto punchdone;
}
}
// didn't find any creatures, so try to strike any walls
pmo->special1 = 0;
angle = pmo->angle;
slope = P_AimLineAttack(pmo, angle, MELEERANGE);
P_LineAttack(pmo, angle, MELEERANGE, slope, damage);
punchdone:
if (pmo->special1 == 3)
{
pmo->special1 = 0;
P_SetPsprite(player, ps_weapon, S_PUNCHATK2_1);
S_StartSound(pmo, SFX_FIGHTER_GRUNT);
}
return;
}
//============================================================================
//
// A_FAxeAttack
//
//============================================================================
#define AXERANGE 2.25*MELEERANGE
void A_FAxeAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
mobj_t *pmo = player->mo;
fixed_t power;
int damage;
int slope;
int i;
int useMana;
damage = 40 + (P_Random() & 15) + (P_Random() & 7);
power = 0;
if (player->mana[MANA_1] > 0)
{
damage <<= 1;
power = 6*FRACUNIT;
PuffType = MT_AXEPUFF_GLOW;
useMana = 1;
}
else
{
PuffType = MT_AXEPUFF;
useMana = 0;
}
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, AXERANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, AXERANGE, slope, damage);
if (linetarget->flags&MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
useMana++;
goto axedone;
}
angle = pmo->angle - i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, AXERANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, AXERANGE, slope, damage);
if (linetarget->flags & MF_COUNTKILL)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
useMana++;
goto axedone;
}
}
// didn't find any creatures, so try to strike any walls
pmo->special1 = 0;
angle = pmo->angle;
slope = P_AimLineAttack(pmo, angle, MELEERANGE);
P_LineAttack(pmo, angle, MELEERANGE, slope, damage);
axedone:
if (useMana == 2)
{
player->mana[MANA_1] -=
WeaponManaUse[player->playerclass][player->readyweapon];
if (player->mana[MANA_1] <= 0)
{
P_SetPsprite(player, ps_weapon, S_FAXEATK_5);
}
}
return;
}
//===========================================================================
//
// A_CMaceAttack
//
//===========================================================================
void A_CMaceAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
int damage;
int slope;
int i;
damage = 25 + (P_Random() & 15);
PuffType = MT_HAMMERPUFF;
for (i = 0; i < 16; i++)
{
angle = player->mo->angle + i*(ANG45/16);
slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE);
if (linetarget)
{
P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, damage);
AdjustPlayerAngle(player->mo);
// player->mo->angle = R_PointToAngle2(player->mo->x,
// player->mo->y, linetarget->x, linetarget->y);
goto macedone;
}
angle = player->mo->angle - i*(ANG45/16);
slope = P_AimLineAttack(player->mo, angle, 2*MELEERANGE);
if (linetarget)
{
P_LineAttack(player->mo, angle, 2*MELEERANGE, slope, damage);
AdjustPlayerAngle(player->mo);
// player->mo->angle = R_PointToAngle2(player->mo->x,
// player->mo->y, linetarget->x, linetarget->y);
goto macedone;
}
}
// didn't find any creatures, so try to strike any walls
player->mo->special1 = 0;
angle = player->mo->angle;
slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
macedone:
return;
}
//============================================================================
//
// A_CStaffCheck
//
//============================================================================
void A_CStaffCheck(player_t *player, pspdef_t *psp)
{
mobj_t *pmo;
int damage;
int newLife;
angle_t angle;
int slope;
int i;
pmo = player->mo;
damage = 20 + (P_Random() & 15);
PuffType = MT_CSTAFFPUFF;
for (i = 0; i < 3; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, 1.5*MELEERANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage);
pmo->angle = R_PointToAngle2(pmo->x, pmo->y,
linetarget->x, linetarget->y);
if ((linetarget->player || linetarget->flags & MF_COUNTKILL)
&& (!(linetarget->flags2 & (MF2_DORMANT+MF2_INVULNERABLE))))
{
newLife = player->health + (damage>>3);
newLife = newLife > 100 ? 100 : newLife;
pmo->health = player->health = newLife;
P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1);
}
player->mana[MANA_1] -=
WeaponManaUse[player->playerclass][player->readyweapon];
break;
}
angle = pmo->angle - i*(ANG45/16);
slope = P_AimLineAttack(player->mo, angle, 1.5*MELEERANGE);
if (linetarget)
{
P_LineAttack(pmo, angle, 1.5*MELEERANGE, slope, damage);
pmo->angle = R_PointToAngle2(pmo->x, pmo->y,
linetarget->x, linetarget->y);
if (linetarget->player || linetarget->flags & MF_COUNTKILL)
{
newLife = player->health + (damage>>4);
newLife = newLife > 100 ? 100 : newLife;
pmo->health = player->health = newLife;
P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1);
}
player->mana[MANA_1] -=
WeaponManaUse[player->playerclass][player->readyweapon];
break;
}
}
}
//============================================================================
//
// A_CStaffAttack
//
//============================================================================
void A_CStaffAttack(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
mobj_t *pmo;
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
pmo = player->mo;
mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle - (ANG45/15));
if (mo)
{
mo->special2 = 32;
}
mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle + (ANG45/15));
if (mo)
{
mo->special2 = 0;
}
S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE);
}
//============================================================================
//
// A_CStaffMissileSlither
//
//============================================================================
void A_CStaffMissileSlither(mobj_t *actor)
{
fixed_t newX, newY;
int weaveXY;
int angle;
weaveXY = actor->special2;
angle = (actor->angle + ANG90)>>ANGLETOFINESHIFT;
newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]);
newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY]);
weaveXY = (weaveXY + 3) & 63;
newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]);
newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]);
P_TryMove(actor, newX, newY);
actor->special2 = weaveXY;
}
//============================================================================
//
// A_CStaffInitBlink
//
//============================================================================
void A_CStaffInitBlink(player_t *player, pspdef_t *psp)
{
player->mo->special1 = (P_Random()>>1) + 20;
}
//============================================================================
//
// A_CStaffCheckBlink
//
//============================================================================
void A_CStaffCheckBlink(player_t *player, pspdef_t *psp)
{
if (!--player->mo->special1)
{
P_SetPsprite(player, ps_weapon, S_CSTAFFBLINK1);
player->mo->special1 = (P_Random() + 50)>>2;
}
}
//============================================================================
//
// A_CFlameAttack
//
//============================================================================
#define FLAMESPEED (0.45*FRACUNIT)
#define CFLAMERANGE (12*64*FRACUNIT)
void A_CFlameAttack(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
mo = P_SpawnPlayerMissile(player->mo, MT_CFLAME_MISSILE);
if (mo)
{
mo->thinker.function = P_BlasterMobjThinker;
mo->special1 = 2;
}
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE);
}
//============================================================================
//
// A_CFlamePuff
//
//============================================================================
void A_CFlamePuff(mobj_t *actor)
{
A_UnHideThing(actor);
actor->momx = 0;
actor->momy = 0;
actor->momz = 0;
S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE);
}
//============================================================================
//
// A_CFlameMissile
//
//============================================================================
void A_CFlameMissile(mobj_t *actor)
{
int i;
int an;
// int an90;
fixed_t dist;
mobj_t *mo;
A_UnHideThing(actor);
S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE);
if (BlockingMobj && BlockingMobj->flags & MF_SHOOTABLE)
{ // Hit something, so spawn the flame circle around the thing
dist = BlockingMobj->radius + 18*FRACUNIT;
for (i = 0; i < 4; i++)
{
an = (i*ANG45)>>ANGLETOFINESHIFT;
// an90 = (i*ANG45 + ANG90)>>ANGLETOFINESHIFT;
mo = P_SpawnMobj(BlockingMobj->x + FixedMul(dist, finecosine[an]),
BlockingMobj->y + FixedMul(dist, finesine[an]),
BlockingMobj->z + 5*FRACUNIT, MT_CIRCLEFLAME);
if (mo)
{
mo->angle = an<<ANGLETOFINESHIFT;
mo->target = actor->target;
mo->momx = mo->special1 = FixedMul(finecosine[an], FLAMESPEED);
mo->momy = mo->special2 = FixedMul(finesine[an], FLAMESPEED);
mo->tics -= P_Random() & 3;
}
mo = P_SpawnMobj(BlockingMobj->x - FixedMul(dist, finecosine[an]),
BlockingMobj->y - FixedMul(dist, finesine[an]),
BlockingMobj->z + 5*FRACUNIT, MT_CIRCLEFLAME);
if (mo)
{
mo->angle = ANG180 + (an<<ANGLETOFINESHIFT);
mo->target = actor->target;
mo->momx = mo->special1 = FixedMul(finecosine[an], -FLAMESPEED);
mo->momy = mo->special2 = FixedMul(finesine[an], -FLAMESPEED);
mo->tics -= P_Random() & 3;
}
}
P_SetMobjState(actor, S_FLAMEPUFF2_1);
}
}
/*
void A_CFlameAttack(player_t *player, pspdef_t *psp)
{
mobj_t *pmo;
angle_t angle;
int damage;
int i;
int an;
// int an90;
fixed_t dist;
mobj_t *mo;
pmo = player->mo;
P_BulletSlope(pmo);
damage = 25 + HITDICE(3);
angle = pmo->angle;
if (player->refire)
{
angle += (P_Random() - P_Random()) << 17;
}
P_AimLineAttack(pmo, angle, CFLAMERANGE); // Correctly set linetarget
if (!linetarget)
{
angle += ANGLE_1*2;
P_AimLineAttack(pmo, angle, CFLAMERANGE);
if (!linetarget)
{
angle -= ANGLE_1*4;
P_AimLineAttack(pmo, angle, CFLAMERANGE);
if (!linetarget)
{
angle += ANGLE_1*2;
}
}
}
if (linetarget)
{
PuffType = MT_FLAMEPUFF2;
}
else
{
PuffType = MT_FLAMEPUFF;
}
P_LineAttack(pmo, angle, CFLAMERANGE, bulletslope, damage);
if (linetarget)
{ // Hit something, so spawn the flame circle around the thing
dist = linetarget->radius + 18*FRACUNIT;
for (i = 0; i < 4; i++)
{
an = (i*ANG45)>>ANGLETOFINESHIFT;
// an90 = (i*ANG45 + ANG90)>>ANGLETOFINESHIFT;
mo = P_SpawnMobj(linetarget->x + FixedMul(dist, finecosine[an]),
linetarget->y + FixedMul(dist, finesine[an]),
linetarget->z + 5*FRACUNIT, MT_CIRCLEFLAME);
if (mo)
{
mo->angle = an<<ANGLETOFINESHIFT;
mo->target = pmo;
mo->momx = mo->special1 = FixedMul(FLAMESPEED, finecosine[an]);
mo->momy = mo->special2 = FixedMul(FLAMESPEED, finesine[an]);
mo->tics -= P_Random() & 3;
}
mo = P_SpawnMobj(linetarget->x - FixedMul(dist, finecosine[an]),
linetarget->y - FixedMul(dist, finesine[an]),
linetarget->z + 5*FRACUNIT, MT_CIRCLEFLAME);
if (mo)
{
mo->angle = ANG180 + (an<<ANGLETOFINESHIFT);
mo->target = pmo;
mo->momx = mo->special1 = FixedMul(-FLAMESPEED, finecosine[an]);
mo->momy = mo->special2 = FixedMul(-FLAMESPEED, finesine[an]);
mo->tics -= P_Random() & 3;
}
}
}
// Create a line of flames from the player to the flame puff
CFlameCreateFlames(player->mo);
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE);
}
*/
//============================================================================
//
// A_CFlameRotate
//
//============================================================================
#define FLAMEROTSPEED 2*FRACUNIT
void A_CFlameRotate(mobj_t *actor)
{
int an;
an = (actor->angle + ANG90)>>ANGLETOFINESHIFT;
actor->momx = actor->special1 + FixedMul(FLAMEROTSPEED, finecosine[an]);
actor->momy = actor->special2 + FixedMul(FLAMEROTSPEED, finesine[an]);
actor->angle += ANG90/15;
}
//============================================================================
//
// A_CHolyAttack3
//
// Spawns the spirits
//============================================================================
void A_CHolyAttack3(mobj_t *actor)
{
P_SpawnMissile(actor, actor->target, MT_HOLY_MISSILE);
S_StartSound(actor, SFX_CHOLY_FIRE);
}
//============================================================================
//
// A_CHolyAttack2
//
// Spawns the spirits
//============================================================================
void A_CHolyAttack2(mobj_t *actor)
{
int j;
int i;
mobj_t *mo;
mobj_t *tail, *next;
for (j = 0; j < 4; j++)
{
mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_FX);
if (!mo)
{
continue;
}
switch (j)
{ // float bob index
case 0:
mo->special2 = P_Random() & 7; // upper-left
break;
case 1:
mo->special2 = 32 + (P_Random() & 7); // upper-right
break;
case 2:
mo->special2 = (32 + (P_Random() & 7)) << 16; // lower-left
break;
case 3:
mo->special2 = ((32 + (P_Random() & 7)) << 16) + 32 + (P_Random() & 7);
break;
}
mo->z = actor->z;
mo->angle = actor->angle + (ANGLE_45 + ANGLE_45/2) - ANGLE_45*j;
P_ThrustMobj(mo, mo->angle, mo->info->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 (linetarget)
{
mo->special1 = (intptr_t)linetarget;
mo->flags |= MF_NOCLIP|MF_SKULLFLY;
mo->flags &= ~MF_MISSILE;
}
tail = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL);
tail->special2 = (intptr_t)mo; // parent
for (i = 1; i < 3; i++)
{
next = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL);
P_SetMobjState(next, next->info->spawnstate + 1);
tail->special1 = (intptr_t)next;
tail = next;
}
tail->special1 = 0; // last tail bit
}
}
//============================================================================
//
// A_CHolyAttack
//
//============================================================================
void A_CHolyAttack(player_t *player, pspdef_t *psp)
{
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
P_SpawnPlayerMissile(player->mo, MT_HOLY_MISSILE);
if (player == &players[consoleplayer])
{
player->damagecount = 0;
player->bonuscount = 0;
V_SetPaletteShift(STARTHOLYPAL);
}
S_StartSound(player->mo, SFX_CHOLY_FIRE);
}
//============================================================================
//
// A_CHolyPalette
//
//============================================================================
void A_CHolyPalette(player_t *player, pspdef_t *psp)
{
int pal;
if (player == &players[consoleplayer])
{
pal = STARTHOLYPAL+psp->state - (&states[S_CHOLYATK_6]);
if (pal == STARTHOLYPAL + 3)
{ // reset back to original playpal
pal = 0;
}
V_SetPaletteShift(pal);
}
}
//============================================================================
//
// CHolyFindTarget
//
//============================================================================
static void CHolyFindTarget(mobj_t *actor)
{
mobj_t *target;
if ((target = P_RoughMonsterSearch(actor, 6)))
{
actor->special1 = (intptr_t)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(mobj_t *actor, angle_t thresh, angle_t turnMax)
{
int dir;
int dist;
angle_t delta;
angle_t angle;
mobj_t *target;
fixed_t newZ;
fixed_t deltaZ;
target = (mobj_t *)actor->special1;
if (target == NULL)
{
return;
}
if (!(target->flags & MF_SHOOTABLE) ||
(!(target->flags & MF_COUNTKILL) && !target->player))
{ // Target died/target isn't a player or creature
actor->special1 = 0;
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->info->speed, finecosine[angle]);
actor->momy = FixedMul(actor->info->speed, finesine[angle]);
if (!(leveltime & 15)
|| actor->z > target->z + (target->height)
|| actor->z + actor->height < target->z)
{
newZ = target->z + ((P_Random()*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->info->speed;
if (dist < 1)
{
dist = 1;
}
actor->momz = deltaZ / dist;
}
return;
}
//============================================================================
//
// A_CHolyWeave
//
//============================================================================
static void CHolyWeave(mobj_t *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 + (P_Random() % 5)) & 63;
newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]<<2);
newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]<<2);
P_TryMove(actor, newX, newY);
actor->z -= FloatBobOffsets[weaveZ]<<1;
weaveZ = (weaveZ + (P_Random() % 5)) & 63;
actor->z += FloatBobOffsets[weaveZ]<<1;
actor->special2 = weaveZ + (weaveXY<<16);
}
//============================================================================
//
// A_CHolySeek
//
//============================================================================
void A_CHolySeek(mobj_t *actor)
{
actor->health--;
if (actor->health <= 0)
{
actor->momx >>= 2;
actor->momy >>= 2;
actor->momz = 0;
P_SetMobjState(actor, actor->info->deathstate);
actor->tics -= P_Random() & 3;
return;
}
if (actor->special1)
{
CHolySeekerMissile(actor, actor->args[0]*ANGLE_1,
actor->args[0]*ANGLE_1*2);
if (!((leveltime + 7) & 15))
{
actor->args[0] = 5 + (P_Random()/20);
}
}
CHolyWeave(actor);
}
//============================================================================
//
// CHolyTailFollow
//
//============================================================================
static void CHolyTailFollow(mobj_t *actor, fixed_t dist)
{
mobj_t *child;
int an;
fixed_t oldDistance, newDistance;
child = (mobj_t *)actor->special1;
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])))
{
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 +
FixedMul(FixedDiv(newDistance, oldDistance), child->z - actor->z);
}
}
CHolyTailFollow(child, dist - FRACUNIT);
}
}
//============================================================================
//
// CHolyTailRemove
//
//============================================================================
static void CHolyTailRemove(mobj_t *actor)
{
mobj_t *child;
child = (mobj_t *)actor->special1;
if (child)
{
CHolyTailRemove(child);
}
P_RemoveMobj(actor);
}
//============================================================================
//
// A_CHolyTail
//
//============================================================================
void A_CHolyTail(mobj_t *actor)
{
mobj_t *parent;
parent = (mobj_t *)actor->special2;
if (parent)
{
if (parent->state >= &states[parent->info->deathstate])
{ // Ghost removed, so remove all tail parts
CHolyTailRemove(actor);
return;
}
else if (P_TryMove(actor,
parent->x - FixedMul(14*FRACUNIT,
finecosine[parent->angle>>ANGLETOFINESHIFT]),
parent->y - FixedMul(14*FRACUNIT,
finesine[parent->angle>>ANGLETOFINESHIFT])))
{
actor->z = parent->z-5*FRACUNIT;
}
CHolyTailFollow(actor, 10*FRACUNIT);
}
}
//============================================================================
//
// A_CHolyCheckScream
//
//============================================================================
void A_CHolyCheckScream(mobj_t *actor)
{
A_CHolySeek(actor);
if (P_Random() < 20)
{
S_StartSound(actor, SFX_SPIRIT_ACTIVE);
}
if (!actor->special1)
{
CHolyFindTarget(actor);
}
}
//============================================================================
//
// A_CHolySpawnPuff
//
//============================================================================
void A_CHolySpawnPuff(mobj_t *actor)
{
P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_MISSILE_PUFF);
}
//----------------------------------------------------------------------------
//
// PROC A_FireConePL1
//
//----------------------------------------------------------------------------
#define SHARDSPAWN_LEFT 1
#define SHARDSPAWN_RIGHT 2
#define SHARDSPAWN_UP 4
#define SHARDSPAWN_DOWN 8
void A_FireConePL1(player_t *player, pspdef_t *psp)
{
angle_t angle;
int damage;
int slope;
int i;
mobj_t *pmo, *mo;
int conedone = false;
pmo = player->mo;
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
S_StartSound(pmo, SFX_MAGE_SHARDS_FIRE);
damage = 90 + (P_Random() & 15);
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, MELEERANGE);
(void) slope; /* variable set but not used */
if (linetarget)
{
pmo->flags2 |= MF2_ICEDAMAGE;
P_DamageMobj(linetarget, pmo, pmo, damage);
pmo->flags2 &= ~MF2_ICEDAMAGE;
conedone = true;
break;
}
}
// didn't find any creatures, so fire projectiles
if (!conedone)
{
mo = P_SpawnPlayerMissile(pmo, MT_SHARDFX1);
if (mo)
{
mo->special1 = SHARDSPAWN_LEFT|SHARDSPAWN_DOWN|SHARDSPAWN_UP
|SHARDSPAWN_RIGHT;
mo->special2 = 3; // Set sperm count (levels of reproductivity)
mo->target = pmo;
mo->args[0] = 3; // Mark Initial shard as super damage
}
}
}
void A_ShedShard(mobj_t *actor)
{
mobj_t *mo;
int spawndir = actor->special1;
int spermcount = actor->special2;
if (spermcount <= 0)
return; // No sperm left
actor->special2 = 0;
spermcount--;
// every so many calls, spawn a new missile in it's set directions
if (spawndir & SHARDSPAWN_LEFT)
{
mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle + (ANG45/9),
0, (20 + 2*spermcount)<<FRACBITS);
if (mo)
{
mo->special1 = SHARDSPAWN_LEFT;
mo->special2 = spermcount;
mo->momz = actor->momz;
mo->target = actor->target;
mo->args[0] = (spermcount == 3) ? 2 : 0;
}
}
if (spawndir & SHARDSPAWN_RIGHT)
{
mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle - (ANG45/9),
0, (20 + 2*spermcount)<<FRACBITS);
if (mo)
{
mo->special1 = SHARDSPAWN_RIGHT;
mo->special2 = spermcount;
mo->momz = actor->momz;
mo->target = actor->target;
mo->args[0] = (spermcount == 3) ? 2 : 0;
}
}
if (spawndir & SHARDSPAWN_UP)
{
mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle,
0, (15 + 2*spermcount)<<FRACBITS);
if (mo)
{
mo->momz = actor->momz;
mo->z += 8*FRACUNIT;
if (spermcount & 1) // Every other reproduction
mo->special1 = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
else
mo->special1 = SHARDSPAWN_UP;
mo->special2 = spermcount;
mo->target = actor->target;
mo->args[0] = (spermcount == 3) ? 2 : 0;
}
}
if (spawndir & SHARDSPAWN_DOWN)
{
mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle,
0, (15 + 2*spermcount)<<FRACBITS);
if (mo)
{
mo->momz = actor->momz;
mo->z -= 4*FRACUNIT;
if (spermcount & 1) // Every other reproduction
mo->special1 = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT;
else
mo->special1 = SHARDSPAWN_DOWN;
mo->special2 = spermcount;
mo->target = actor->target;
mo->args[0] = (spermcount == 3) ? 2 : 0;
}
}
}
//----------------------------------------------------------------------------
//
// PROC A_HideInCeiling
//
//----------------------------------------------------------------------------
/*
void A_HideInCeiling(mobj_t *actor)
{
actor->z = actor->ceilingz + 4*FRACUNIT;
}
*/
//----------------------------------------------------------------------------
//
// PROC A_FloatPuff
//
//----------------------------------------------------------------------------
/*
void A_FloatPuff(mobj_t *puff)
{
puff->momz += 1.8*FRACUNIT;
}
*/
void A_Light0(player_t *player, pspdef_t *psp)
{
player->extralight = 0;
}
/*
void A_Light1(player_t *player, pspdef_t *psp)
{
player->extralight = 1;
}
*/
/*
void A_Light2(player_t *player, pspdef_t *psp)
{
player->extralight = 2;
}
*/
//------------------------------------------------------------------------
//
// PROC P_SetupPsprites
//
// Called at start of level for each player
//
//------------------------------------------------------------------------
void P_SetupPsprites(player_t *player)
{
int i;
// Remove all psprites
for (i = 0; i < NUMPSPRITES; i++)
{
player->psprites[i].state = NULL;
}
// Spawn the ready weapon
player->pendingweapon = player->readyweapon;
P_BringUpWeapon(player);
}
//------------------------------------------------------------------------
//
// PROC P_MovePsprites
//
// Called every tic by player thinking routine
//
//------------------------------------------------------------------------
void P_MovePsprites(player_t *player)
{
int i;
pspdef_t *psp;
state_t *state;
psp = &player->psprites[0];
for (i = 0; i < NUMPSPRITES; i++, psp++)
{
if ((state = psp->state) != 0) // a null state means not active
{
// drop tic count and possibly change state
if (psp->tics != -1) // a -1 tic count never changes
{
psp->tics--;
if (!psp->tics)
{
P_SetPsprite(player, i, psp->state->nextstate);
}
}
}
}
player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
}
//============================================================================
//
// ASSASSIN WEAPONS / ATTACKS (ADD-ON CLASS FROM HEXEN II)
//
//============================================================================
#if defined(ASSASSIN)
//============================================================================
//
// A_AKnifeAttack
//
// Jim Cameron did most of this one
//============================================================================
void A_AKnifeAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
int damage;
int slope;
mobj_t *pmo = player->mo;
fixed_t power;
int i;
boolean oof = false;
/* jim - the Katar should be a bit feebler */
damage = 20 + (P_Random() & 15);
power = 2*FRACUNIT;
PuffType = MT_PUNCHPUFF;
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE);
if (linetarget)
{
player->mo->special1++;
/*
* jim - this is the Mighty Blow for the fighter and is
* not useful here
*/
#if 0
if (pmo->special1 == 3)
{
damage <<= 1;
power = 6*FRACUNIT;
PuffType = MT_HAMMERPUFF;
}
#endif
/*
* jim - instead of that we make the Katar deal more
* damage to a monster if struck from behind. Assume
* so if the angle of striking is within 45 degrees
* of the angle the target is facing in
* OOOPS! but only if it IS a monster! Striking trees from
* behind might be amusing but doesn't do much for realism 8-)
*/
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
if ((angle - linetarget->angle < ANG45) ||
(linetarget->angle - angle < ANG45))
{
damage *= 15;
power = 6 * FRACUNIT;
PuffType = MT_HAMMERPUFF;
oof = true;
}
}
P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
goto knifedone;
}
angle = pmo->angle - i*(ANG45/16);
slope = P_AimLineAttack(pmo, angle, 2*MELEERANGE);
if (linetarget)
{
player->mo->special1++;
/*
* jim - this is the Mighty Blow for the fighter and is
* not useful here
*/
#if 0
if (pmo->special1 == 3)
{
damage <<= 1;
power = 6*FRACUNIT;
PuffType = MT_HAMMERPUFF;
}
#endif
/*
* jim - instead of that we make the Katar deal more
* damage to a monster if struck from behind. Assume
* so if the angle of striking is within 45 degrees
* of the angle the target is facing in
* OOOPS! but only if it IS a monster! Striking trees from
* behind might be amusing but doesn't do much for realism 8-)
*/
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
if ((angle - linetarget->angle < ANG45) ||
(linetarget->angle - angle < ANG45))
{
damage *= 15;
power = 6 * FRACUNIT;
PuffType = MT_HAMMERPUFF;
oof = true;
}
}
P_LineAttack(pmo, angle, 2*MELEERANGE, slope, damage);
if (linetarget->flags & MF_COUNTKILL || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
}
AdjustPlayerAngle(pmo);
goto knifedone;
}
}
/* didn't find any creatures, so try to strike any walls*/
pmo->special1 = 0;
angle = pmo->angle;
slope = P_AimLineAttack(pmo, angle, MELEERANGE);
P_LineAttack(pmo, angle, MELEERANGE, slope, damage);
knifedone:
if (oof)
{
pmo->special1 = 0;
P_SetPsprite(player, ps_weapon, S_KATARATK2_1);
/* jim - come on, she's a girl! */
S_StartSound(pmo, SFX_PLAYER_MAGE_GRUNT);
}
}
void A_ACrossAttack(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
mobj_t *pmo = player->mo;
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
// pmo = player->mo;
// P_SpawnPlayerMissile(pmo, MT_CSTAFF_MISSILE);
/*
* jim - special2 is used to control the serpent staff projectiles'
* `slither' and is not used here
* We do however want to give crossbow missiles BlasterMobjThinker()s
* instead of the ordinary ones because they are FAST.
*/
mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle);
if (mo)
{
/* mo->special2 = 16; */
mo->thinker.function = P_BlasterMobjThinker;
}
mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle - (ANG45/10));
if (mo)
{
/* mo->special2 = 32; */
mo->thinker.function = P_BlasterMobjThinker;
}
mo = P_SPMAngle(pmo, MT_ACROSS_MISSILE, pmo->angle + (ANG45/10));
if (mo)
{
/* mo->special2 = 0; */
mo->thinker.function = P_BlasterMobjThinker;
}
S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE);
}
void A_AGrenAttack(player_t *player, pspdef_t *psp)
{
mobj_t *mo;
mo = P_SpawnMobj(player->mo->x, player->mo->y,
player->mo->z - player->mo->floorclip + 35*FRACUNIT,
MT_THROWINGBOMB);
if (mo)
{
mo->angle = player->mo->angle + (((P_Random() & 7) - 4)<<24);
mo->momz = 4*FRACUNIT + ((player->lookdir)<<(FRACBITS - 4));
mo->z += player->lookdir<<(FRACBITS - 4);
P_ThrustMobj(mo, mo->angle, mo->info->speed);
mo->momx += player->mo->momx>>1;
mo->momy += player->mo->momy>>1;
mo->target = player->mo;
mo->tics -= P_Random() & 3;
P_CheckMissileSpawn(mo);
}
}
void A_AStaffAttack(player_t *player, pspdef_t *psp)
{
angle_t angle;
mobj_t *pmo;
/* THIS ISN'T FINISHED YET!! */
player->mana[MANA_1] -= WeaponManaUse[player->playerclass][player->readyweapon];
player->mana[MANA_2] -= WeaponManaUse[player->playerclass][player->readyweapon];
pmo = player->mo;
angle = pmo->angle;
}
#endif /* ASSASSIN */