Source to src/p_pspr.c


Enter a symbol's name here to quickly find it.


// P_pspr.c

#include "DoomDef.h"
#include "P_local.h"
#include "soundst.h"

// Macros

#define LOWERSPEED FRACUNIT*6
#define RAISESPEED FRACUNIT*6
#define WEAPONBOTTOM 128*FRACUNIT
#define WEAPONTOP 32*FRACUNIT
#define FLAME_THROWER_TICS 10*35
#define MAGIC_JUNK 1234
#define MAX_MACE_SPOTS 8

static int MaceSpotCount;
static struct
{
	fixed_t x;
	fixed_t y;
} MaceSpots[MAX_MACE_SPOTS];

fixed_t bulletslope;

static int WeaponAmmoUsePL1[NUMWEAPONS] = {
	0,					// staff
	USE_GWND_AMMO_1,	// gold wand
	USE_CBOW_AMMO_1,	// crossbow
	USE_BLSR_AMMO_1,	// blaster
	USE_SKRD_AMMO_1,	// skull rod
	USE_PHRD_AMMO_1,	// phoenix rod
	USE_MACE_AMMO_1,	// mace
	0,					// gauntlets
	0					// beak
};

static int WeaponAmmoUsePL2[NUMWEAPONS] = {
	0,					// staff
	USE_GWND_AMMO_2,	// gold wand
	USE_CBOW_AMMO_2,	// crossbow
	USE_BLSR_AMMO_2,	// blaster
	USE_SKRD_AMMO_2,	// skull rod
	USE_PHRD_AMMO_2,	// phoenix rod
	USE_MACE_AMMO_2,	// mace
	0,					// gauntlets
	0					// beak
};

weaponinfo_t wpnlev1info[NUMWEAPONS] =
{
	{ // Staff
		am_noammo,			// ammo
		S_STAFFUP,			// upstate
		S_STAFFDOWN,		// downstate
		S_STAFFREADY,		// readystate
		S_STAFFATK1_1,		// atkstate
		S_STAFFATK1_1,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Gold wand
		am_goldwand,		// ammo
		S_GOLDWANDUP,		// upstate
		S_GOLDWANDDOWN,		// downstate
		S_GOLDWANDREADY,	// readystate
		S_GOLDWANDATK1_1,	// atkstate
		S_GOLDWANDATK1_1,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Crossbow
		am_crossbow,		// ammo
		S_CRBOWUP,			// upstate
		S_CRBOWDOWN,		// downstate
		S_CRBOW1,			// readystate
		S_CRBOWATK1_1,		// atkstate
		S_CRBOWATK1_1,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Blaster
		am_blaster,			// ammo
		S_BLASTERUP,		// upstate
		S_BLASTERDOWN,		// downstate
		S_BLASTERREADY,		// readystate
		S_BLASTERATK1_1,	// atkstate
		S_BLASTERATK1_3,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Skull rod
		am_skullrod,		// ammo
		S_HORNRODUP,		// upstate
		S_HORNRODDOWN,		// downstate
		S_HORNRODREADY,		// readystae
		S_HORNRODATK1_1,	// atkstate
		S_HORNRODATK1_1,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Phoenix rod
		am_phoenixrod,		// ammo
		S_PHOENIXUP,		// upstate
		S_PHOENIXDOWN,		// downstate
		S_PHOENIXREADY,		// readystate
		S_PHOENIXATK1_1,	// atkstate
		S_PHOENIXATK1_1,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Mace
		am_mace,			// ammo
		S_MACEUP,			// upstate
		S_MACEDOWN,			// downstate
		S_MACEREADY,		// readystate
		S_MACEATK1_1,		// atkstate
		S_MACEATK1_2,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Gauntlets
		am_noammo,			// ammo
		S_GAUNTLETUP,		// upstate
		S_GAUNTLETDOWN,		// downstate
		S_GAUNTLETREADY,	// readystate
		S_GAUNTLETATK1_1,	// atkstate
		S_GAUNTLETATK1_3,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Beak
		am_noammo,			// ammo
		S_BEAKUP,			// upstate
		S_BEAKDOWN,			// downstate
		S_BEAKREADY,		// readystate
		S_BEAKATK1_1,		// atkstate
		S_BEAKATK1_1,		// holdatkstate
		S_NULL				// flashstate
	}
};

weaponinfo_t wpnlev2info[NUMWEAPONS] =
{
	{ // Staff
		am_noammo,			// ammo
		S_STAFFUP2,			// upstate
		S_STAFFDOWN2,		// downstate
		S_STAFFREADY2_1,	// readystate
		S_STAFFATK2_1,		// atkstate
		S_STAFFATK2_1,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Gold wand
		am_goldwand,		// ammo
		S_GOLDWANDUP,		// upstate
		S_GOLDWANDDOWN,		// downstate
		S_GOLDWANDREADY,	// readystate
		S_GOLDWANDATK2_1,	// atkstate
		S_GOLDWANDATK2_1,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Crossbow
		am_crossbow,		// ammo
		S_CRBOWUP,			// upstate
		S_CRBOWDOWN,		// downstate
		S_CRBOW1,			// readystate
		S_CRBOWATK2_1,		// atkstate
		S_CRBOWATK2_1,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Blaster
		am_blaster,			// ammo
		S_BLASTERUP,		// upstate
		S_BLASTERDOWN,		// downstate
		S_BLASTERREADY,		// readystate
		S_BLASTERATK2_1,	// atkstate
		S_BLASTERATK2_3,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Skull rod
		am_skullrod,		// ammo
		S_HORNRODUP,		// upstate
		S_HORNRODDOWN,		// downstate
		S_HORNRODREADY,		// readystae
		S_HORNRODATK2_1,	// atkstate
		S_HORNRODATK2_1,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Phoenix rod
		am_phoenixrod,		// ammo
		S_PHOENIXUP,		// upstate
		S_PHOENIXDOWN,		// downstate
		S_PHOENIXREADY,		// readystate
		S_PHOENIXATK2_1,	// atkstate
		S_PHOENIXATK2_2,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Mace
		am_mace,			// ammo
		S_MACEUP,			// upstate
		S_MACEDOWN,			// downstate
		S_MACEREADY,		// readystate
		S_MACEATK2_1,		// atkstate
		S_MACEATK2_1,		// holdatkstate
		S_NULL				// flashstate
	},
	{ // Gauntlets
		am_noammo,			// ammo
		S_GAUNTLETUP2,		// upstate
		S_GAUNTLETDOWN2,	// downstate
		S_GAUNTLETREADY2_1,	// readystate
		S_GAUNTLETATK2_1,	// atkstate
		S_GAUNTLETATK2_3,	// holdatkstate
		S_NULL				// flashstate
	},
	{ // Beak
		am_noammo,			// ammo
		S_BEAKUP,			// upstate
		S_BEAKDOWN,			// downstate
		S_BEAKREADY,		// readystate
		S_BEAKATK2_1,		// atkstate
		S_BEAKATK2_1,		// holdatkstate
		S_NULL				// flashstate
	}
};

//---------------------------------------------------------------------------
//
// PROC P_OpenWeapons
//
// Called at level load before things are loaded.
//
//---------------------------------------------------------------------------

void P_OpenWeapons(void)
{
	MaceSpotCount = 0;
}

//---------------------------------------------------------------------------
//
// PROC P_AddMaceSpot
//
//---------------------------------------------------------------------------

void P_AddMaceSpot(mapthing_t *mthing)
{
	if(MaceSpotCount == MAX_MACE_SPOTS)
	{
		I_Error("Too many mace spots.");
	}
	MaceSpots[MaceSpotCount].x = mthing->x<<FRACBITS;
	MaceSpots[MaceSpotCount].y = mthing->y<<FRACBITS;
	MaceSpotCount++;
}

//---------------------------------------------------------------------------
//
// PROC P_RepositionMace
//
// Chooses the next spot to place the mace.
//
//---------------------------------------------------------------------------

void P_RepositionMace(mobj_t *mo)
{
	int spot;
	subsector_t *ss;

	P_UnsetThingPosition(mo);
	spot = P_Random()%MaceSpotCount;
	mo->x = MaceSpots[spot].x;
	mo->y = MaceSpots[spot].y;
	ss = R_PointInSubsector(mo->x, mo->y);
	mo->z = mo->floorz = ss->sector->floorheight;
	mo->ceilingz = ss->sector->ceilingheight;
	P_SetThingPosition(mo);
}

//---------------------------------------------------------------------------
//
// PROC P_CloseWeapons
//
// Called at level load after things are loaded.
//
//---------------------------------------------------------------------------

void P_CloseWeapons(void)
{
	int spot;

	if(!MaceSpotCount)
	{ // No maces placed
		return;
	}
	if(!deathmatch && P_Random() < 64)
	{ // Sometimes doesn't show up if not in deathmatch
		return;
	}
	spot = P_Random()%MaceSpotCount;
	P_SpawnMobj(MaceSpots[spot].x, MaceSpots[spot].y, ONFLOORZ, MT_WMACE);
}

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

/*
=================
=
= 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_ActivateBeak
//
//---------------------------------------------------------------------------

void P_ActivateBeak(player_t *player)
{
	player->pendingweapon = wp_nochange;
	player->readyweapon = wp_beak;
	player->psprites[ps_weapon].sy = WEAPONTOP;
	P_SetPsprite(player, ps_weapon, S_BEAKREADY);
}

//---------------------------------------------------------------------------
//
// PROC P_PostChickenWeapon
//
//---------------------------------------------------------------------------

void P_PostChickenWeapon(player_t *player, weapontype_t weapon)
{
	if(weapon == wp_beak)
	{ // Should never happen
		weapon = wp_staff;
	}
	player->pendingweapon = wp_nochange;
	player->readyweapon = weapon;
	player->psprites[ps_weapon].sy = WEAPONBOTTOM;
	P_SetPsprite(player, ps_weapon, wpnlev1info[weapon].upstate);
}

//---------------------------------------------------------------------------
//
// PROC P_BringUpWeapon
//
// Starts bringing the pending weapon up from the bottom of the screen.
//
//---------------------------------------------------------------------------

void P_BringUpWeapon(player_t *player)
{
	statenum_t new;

	if(player->pendingweapon == wp_nochange)
	{
		player->pendingweapon = player->readyweapon;
	}
	if(player->pendingweapon == wp_gauntlets)
	{
		S_StartSound(player->mo, sfx_gntact);
	}
	if(player->powers[pw_weaponlevel2])
	{
		new = wpnlev2info[player->pendingweapon].upstate;
	}
	else
	{
		new = wpnlev1info[player->pendingweapon].upstate;
	}
	player->pendingweapon = wp_nochange;
	player->psprites[ps_weapon].sy = WEAPONBOTTOM;
	P_SetPsprite(player, ps_weapon, new);
}

//---------------------------------------------------------------------------
//
// FUNC P_CheckAmmo
//
// Returns true if there is enough ammo to shoot.  If not, selects the
// next weapon to use.
//
//---------------------------------------------------------------------------

boolean P_CheckAmmo(player_t *player)
{
	ammotype_t ammo;
	int *ammoUse;
	int count;

	ammo = wpnlev1info[player->readyweapon].ammo;
	if(player->powers[pw_weaponlevel2] && !deathmatch)
	{
		ammoUse = WeaponAmmoUsePL2;
	}
	else
	{
		ammoUse = WeaponAmmoUsePL1;
	}
	count = ammoUse[player->readyweapon];
	if(ammo == am_noammo || player->ammo[ammo] >= count)
	{
		return(true);
	}
	// out of ammo, pick a weapon to change to
	do
	{
		if(player->weaponowned[wp_skullrod]
			&& player->ammo[am_skullrod] > ammoUse[wp_skullrod])
		{
			player->pendingweapon = wp_skullrod;
		}
		else if(player->weaponowned[wp_blaster]
			&& player->ammo[am_blaster] > ammoUse[wp_blaster])
		{
			player->pendingweapon = wp_blaster;
		}
		else if(player->weaponowned[wp_crossbow]
			&& player->ammo[am_crossbow] > ammoUse[wp_crossbow])
		{
			player->pendingweapon = wp_crossbow;
		}
		else if(player->weaponowned[wp_mace]
			&& player->ammo[am_mace] > ammoUse[wp_mace])
		{
			player->pendingweapon = wp_mace;
		}
		else if(player->ammo[am_goldwand] > ammoUse[wp_goldwand])
		{
			player->pendingweapon = wp_goldwand;
		}
		else if(player->weaponowned[wp_gauntlets])
		{
			player->pendingweapon = wp_gauntlets;
		}
		else if(player->weaponowned[wp_phoenixrod]
			&& player->ammo[am_phoenixrod] > ammoUse[wp_phoenixrod])
		{
			player->pendingweapon = wp_phoenixrod;
		}
		else
		{
			player->pendingweapon = wp_staff;
		}
	} while(player->pendingweapon == wp_nochange);
	if(player->powers[pw_weaponlevel2])
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev2info[player->readyweapon].downstate);
	}
	else
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev1info[player->readyweapon].downstate);
	}
	return(false);
}

//---------------------------------------------------------------------------
//
// PROC P_FireWeapon
//
//---------------------------------------------------------------------------

void P_FireWeapon(player_t *player)
{
	weaponinfo_t *wpinfo;
	statenum_t attackState;

	if(!P_CheckAmmo(player))
	{
		return;
	}
	P_SetMobjState(player->mo, S_PLAY_ATK2);
	wpinfo = player->powers[pw_weaponlevel2] ? &wpnlev2info[0]
		: &wpnlev1info[0];
	attackState = player->refire ? wpinfo[player->readyweapon].holdatkstate
		: wpinfo[player->readyweapon].atkstate;
	P_SetPsprite(player, ps_weapon, attackState);
	P_NoiseAlert(player->mo, player->mo);
	if(player->readyweapon == wp_gauntlets && !player->refire)
	{ // Play the sound for the initial gauntlet attack
		S_StartSound(player->mo, sfx_gntuse);
	}
}

//---------------------------------------------------------------------------
//
// PROC P_DropWeapon
//
// The player died, so put the weapon away.
//
//---------------------------------------------------------------------------

void P_DropWeapon(player_t *player)
{
	if(player->powers[pw_weaponlevel2])
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev2info[player->readyweapon].downstate);
	}
	else
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev1info[player->readyweapon].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;

	if(player->chickenTics)
	{ // Change to the chicken beak
		P_ActivateBeak(player);
		return;
	}
	// Change player from attack state
	if(player->mo->state == &states[S_PLAY_ATK1]
		|| player->mo->state == &states[S_PLAY_ATK2])
	{
		P_SetMobjState(player->mo, S_PLAY);
	}
	// Check for staff PL2 active sound
	if((player->readyweapon == wp_staff)
		&& (psp->state == &states[S_STAFFREADY2_1])
		&& P_Random() < 128)
	{
		S_StartSound(player->mo, sfx_stfcrk);
	}
	// Put the weapon away if the player has a pending weapon or has
	// died.
	if(player->pendingweapon != wp_nochange || !player->health)
	{
		if(player->powers[pw_weaponlevel2])
		{
			P_SetPsprite(player, ps_weapon,
				wpnlev2info[player->readyweapon].downstate);
		}
		else
		{
			P_SetPsprite(player, ps_weapon,
				wpnlev1info[player->readyweapon].downstate);
		}
		return;
	}

	// Check for fire.  The phoenix rod does not auto fire.
	if(player->cmd.buttons&BT_ATTACK)
	{
		if(!player->attackdown || (player->readyweapon != wp_phoenixrod))
		{
			player->attackdown = true;
			P_FireWeapon(player);
			return;
		}
	}
	else
	{
		player->attackdown = false;
	}

	// 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 P_UpdateBeak
//
//---------------------------------------------------------------------------

void P_UpdateBeak(player_t *player, pspdef_t *psp)
{
	psp->sy = WEAPONTOP+(player->chickenPeck<<(FRACBITS-1));
}

//---------------------------------------------------------------------------
//
// PROC A_BeakReady
//
//---------------------------------------------------------------------------

void A_BeakReady(player_t *player, pspdef_t *psp)
{
	if(player->cmd.buttons&BT_ATTACK)
	{ // Chicken beak attack
		player->attackdown = true;
		P_SetMobjState(player->mo, S_CHICPLAY_ATK1);
		if(player->powers[pw_weaponlevel2])
		{
			P_SetPsprite(player, ps_weapon, S_BEAKATK2_1);
		}
		else
		{
			P_SetPsprite(player, ps_weapon, S_BEAKATK1_1);
		}
		P_NoiseAlert(player->mo, player->mo);
	}
	else
	{
		if(player->mo->state == &states[S_CHICPLAY_ATK1])
		{ // Take out of attack state
			P_SetMobjState(player->mo, S_CHICPLAY);
		}
		player->attackdown = false;
	}
}

//---------------------------------------------------------------------------
//
// 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_CheckAmmo(player);
	}
}

//---------------------------------------------------------------------------
//
// PROC A_Lower
//
//---------------------------------------------------------------------------

void A_Lower(player_t *player, pspdef_t *psp)
{
	if(player->chickenTics)
	{
		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_BeakRaise
//
//---------------------------------------------------------------------------

void A_BeakRaise(player_t *player, pspdef_t *psp)
{
	psp->sy = WEAPONTOP;
	P_SetPsprite(player, ps_weapon,
		wpnlev1info[player->readyweapon].readystate);
}

//---------------------------------------------------------------------------
//
// 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->powers[pw_weaponlevel2])
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev2info[player->readyweapon].readystate);
	}
	else
	{
		P_SetPsprite(player, ps_weapon,
			wpnlev1info[player->readyweapon].readystate);
	}
}

/*
===============
=
= P_BulletSlope
=
= Sets a slope so a near miss is at aproximately the height of the
= intended target
=
===============
*/

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 += 2<<26;
			bulletslope = (mo->player->lookdir<<FRACBITS)/173;
		}
	}
}

//****************************************************************************
//
// WEAPON ATTACKS
//
//****************************************************************************

//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL1
//
//----------------------------------------------------------------------------

void A_BeakAttackPL1(player_t *player, pspdef_t *psp)
{
	angle_t angle;
	int damage;
	int slope;

	damage = 1+(P_Random()&3);
	angle = player->mo->angle;
	slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
	PuffType = MT_BEAKPUFF;
	P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
	if(linetarget)
	{
		player->mo->angle = R_PointToAngle2(player->mo->x,
			player->mo->y, linetarget->x, linetarget->y);
	}
	S_StartSound(player->mo, sfx_chicpk1+(P_Random()%3));
	player->chickenPeck = 12;
	psp->tics -= P_Random()&7;
}

//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL2
//
//----------------------------------------------------------------------------

void A_BeakAttackPL2(player_t *player, pspdef_t *psp)
{
	angle_t angle;
	int damage;
	int slope;

	damage = HITDICE(4);
	angle = player->mo->angle;
	slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
	PuffType = MT_BEAKPUFF;
	P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
	if(linetarget)
	{
		player->mo->angle = R_PointToAngle2(player->mo->x,
			player->mo->y, linetarget->x, linetarget->y);
	}
	S_StartSound(player->mo, sfx_chicpk1+(P_Random()%3));
	player->chickenPeck = 12;
	psp->tics -= P_Random()&3;
}

//----------------------------------------------------------------------------
//
// PROC A_StaffAttackPL1
//
//----------------------------------------------------------------------------

void A_StaffAttackPL1(player_t *player, pspdef_t *psp)
{
	angle_t angle;
	int damage;
	int slope;

	damage = 5+(P_Random()&15);
	angle = player->mo->angle;
	angle += (P_Random()-P_Random())<<18;
	slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
	PuffType = MT_STAFFPUFF;
	P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
	if(linetarget)
	{
		//S_StartSound(player->mo, sfx_stfhit);
		// turn to face target
		player->mo->angle = R_PointToAngle2(player->mo->x,
			player->mo->y, linetarget->x, linetarget->y);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_StaffAttackPL2
//
//----------------------------------------------------------------------------

void A_StaffAttackPL2(player_t *player, pspdef_t *psp)
{
	angle_t angle;
	int damage;
	int slope;

	// P_inter.c:P_DamageMobj() handles target momentums
	damage = 18+(P_Random()&63);
	angle = player->mo->angle;
	angle += (P_Random()-P_Random())<<18;
	slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
	PuffType = MT_STAFFPUFF2;
	P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
	if(linetarget)
	{
		//S_StartSound(player->mo, sfx_stfpow);
		// turn to face target
		player->mo->angle = R_PointToAngle2(player->mo->x,
			player->mo->y, linetarget->x, linetarget->y);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_FireBlasterPL1
//
//----------------------------------------------------------------------------

void A_FireBlasterPL1(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;
	angle_t angle;
	int damage;

	mo = player->mo;
	S_StartSound(mo, sfx_gldhit);
	player->ammo[am_blaster] -= USE_BLSR_AMMO_1;
	P_BulletSlope(mo);
	damage = HITDICE(4);
	angle = mo->angle;
	if(player->refire)
	{
		angle += (P_Random()-P_Random())<<18;
	}
	PuffType = MT_BLASTERPUFF1;
	P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
	S_StartSound(player->mo, sfx_blssht);
}

//----------------------------------------------------------------------------
//
// PROC A_FireBlasterPL2
//
//----------------------------------------------------------------------------

void A_FireBlasterPL2(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;

	player->ammo[am_blaster] -=
		deathmatch ? USE_BLSR_AMMO_1 : USE_BLSR_AMMO_2;
	mo = P_SpawnPlayerMissile(player->mo, MT_BLASTERFX1);
	if(mo)
	{
		mo->thinker.function = P_BlasterMobjThinker;
	}
	S_StartSound(player->mo, sfx_blssht);
}

//----------------------------------------------------------------------------
//
// PROC A_FireGoldWandPL1
//
//----------------------------------------------------------------------------

void A_FireGoldWandPL1(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;
	angle_t angle;
	int damage;

	mo = player->mo;
	player->ammo[am_goldwand] -= USE_GWND_AMMO_1;
	P_BulletSlope(mo);
	damage = 7+(P_Random()&7);
	angle = mo->angle;
	if(player->refire)
	{
		angle += (P_Random()-P_Random())<<18;
	}
	PuffType = MT_GOLDWANDPUFF1;
	P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
	S_StartSound(player->mo, sfx_gldhit);
}

//----------------------------------------------------------------------------
//
// PROC A_FireGoldWandPL2
//
//----------------------------------------------------------------------------

void A_FireGoldWandPL2(player_t *player, pspdef_t *psp)
{
	int i;
	mobj_t *mo;
	angle_t angle;
	int damage;
	fixed_t momz;

	mo = player->mo;
	player->ammo[am_goldwand] -=
		deathmatch ? USE_GWND_AMMO_1 : USE_GWND_AMMO_2;
	PuffType = MT_GOLDWANDPUFF2;
	P_BulletSlope(mo);
	momz = FixedMul(mobjinfo[MT_GOLDWANDFX2].speed, bulletslope);
	P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle-(ANG45/8), momz);
	P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle+(ANG45/8), momz);
	angle = mo->angle-(ANG45/8);
	for(i = 0; i < 5; i++)
	{
		damage = 1+(P_Random()&7);
		P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
		angle += ((ANG45/8)*2)/4;
	}
	S_StartSound(player->mo, sfx_gldhit);
}

//----------------------------------------------------------------------------
//
// PROC A_FireMacePL1B
//
//----------------------------------------------------------------------------

void A_FireMacePL1B(player_t *player, pspdef_t *psp)
{
	mobj_t *pmo;
	mobj_t *ball;
	angle_t angle;

	if(player->ammo[am_mace] < USE_MACE_AMMO_1)
	{
		return;
	}
	player->ammo[am_mace] -= USE_MACE_AMMO_1;
	pmo = player->mo;
	ball = P_SpawnMobj(pmo->x, pmo->y, pmo->z+28*FRACUNIT 
		- FOOTCLIPSIZE*(pmo->flags2&MF2_FEETARECLIPPED != 0), MT_MACEFX2);
	ball->momz = 2*FRACUNIT+((player->lookdir)<<(FRACBITS-5));
	angle = pmo->angle;
	ball->target = pmo;
	ball->angle = angle;
	ball->z += (player->lookdir)<<(FRACBITS-4);
	angle >>= ANGLETOFINESHIFT;
	ball->momx = (pmo->momx>>1)
		+FixedMul(ball->info->speed, finecosine[angle]);
	ball->momy = (pmo->momy>>1)
		+FixedMul(ball->info->speed, finesine[angle]);
	S_StartSound(ball, sfx_lobsht);
	P_CheckMissileSpawn(ball);
}

//----------------------------------------------------------------------------
//
// PROC A_FireMacePL1
//
//----------------------------------------------------------------------------

void A_FireMacePL1(player_t *player, pspdef_t *psp)
{
	mobj_t *ball;

	if(P_Random() < 28)
	{
		A_FireMacePL1B(player, psp);
		return;
	}
	if(player->ammo[am_mace] < USE_MACE_AMMO_1)
	{
		return;
	}
	player->ammo[am_mace] -= USE_MACE_AMMO_1;
	psp->sx = ((P_Random()&3)-2)*FRACUNIT;
	psp->sy = WEAPONTOP+(P_Random()&3)*FRACUNIT;
	ball = P_SPMAngle(player->mo, MT_MACEFX1, player->mo->angle
		+(((P_Random()&7)-4)<<24));
	if(ball)
	{
		ball->special1 = 16; // tics till dropoff
	}
}

//----------------------------------------------------------------------------
//
// PROC A_MacePL1Check
//
//----------------------------------------------------------------------------

void A_MacePL1Check(mobj_t *ball)
{
	angle_t angle;

	if(ball->special1 == 0)
	{
		return;
	}
	ball->special1 -= 4;
	if(ball->special1 > 0)
	{
		return;
	}
	ball->special1 = 0;
	ball->flags2 |= MF2_LOGRAV;
	angle = ball->angle>>ANGLETOFINESHIFT;
	ball->momx = FixedMul(7*FRACUNIT, finecosine[angle]);
	ball->momy = FixedMul(7*FRACUNIT, finesine[angle]);
	ball->momz -= ball->momz>>1;
}

//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact
//
//----------------------------------------------------------------------------

void A_MaceBallImpact(mobj_t *ball)
{
	if((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
	{ // Landed in some sort of liquid
		P_RemoveMobj(ball);
		return;
	}
	if((ball->health != MAGIC_JUNK) && (ball->z <= ball->floorz)
		&& ball->momz)
	{ // Bounce
		ball->health = MAGIC_JUNK;
		ball->momz = (ball->momz*192)>>8;
		ball->flags2 &= ~MF2_FLOORBOUNCE;
		P_SetMobjState(ball, ball->info->spawnstate);
		S_StartSound(ball, sfx_bounce);
	}
	else
	{ // Explode
		ball->flags |= MF_NOGRAVITY;
		ball->flags2 &= ~MF2_LOGRAV;
		S_StartSound(ball, sfx_lobhit);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_MaceBallImpact2
//
//----------------------------------------------------------------------------

void A_MaceBallImpact2(mobj_t *ball)
{
	mobj_t *tiny;
	angle_t angle;

	if((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
	{ // Landed in some sort of liquid
		P_RemoveMobj(ball);
		return;
	}
	if((ball->z != ball->floorz) || (ball->momz < 2*FRACUNIT))
	{ // Explode
		ball->momx = ball->momy = ball->momz = 0;
		ball->flags |= MF_NOGRAVITY;
		ball->flags2 &= ~(MF2_LOGRAV|MF2_FLOORBOUNCE);
	}
	else
	{ // Bounce
		ball->momz = (ball->momz*192)>>8;
		P_SetMobjState(ball, ball->info->spawnstate);

		tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3);
		angle = ball->angle+ANG90;
		tiny->target = ball->target;
		tiny->angle = angle;
		angle >>= ANGLETOFINESHIFT;
		tiny->momx = (ball->momx>>1)+FixedMul(ball->momz-FRACUNIT,
			finecosine[angle]);
		tiny->momy = (ball->momy>>1)+FixedMul(ball->momz-FRACUNIT,
			finesine[angle]);
		tiny->momz = ball->momz;
		P_CheckMissileSpawn(tiny);

		tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3);
		angle = ball->angle-ANG90;
		tiny->target = ball->target;
		tiny->angle = angle;
		angle >>= ANGLETOFINESHIFT;
		tiny->momx = (ball->momx>>1)+FixedMul(ball->momz-FRACUNIT,
			finecosine[angle]);
		tiny->momy = (ball->momy>>1)+FixedMul(ball->momz-FRACUNIT,
			finesine[angle]);
		tiny->momz = ball->momz;
		P_CheckMissileSpawn(tiny);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_FireMacePL2
//
//----------------------------------------------------------------------------

void A_FireMacePL2(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;

	player->ammo[am_mace] -=
		deathmatch ? USE_MACE_AMMO_1 : USE_MACE_AMMO_2;
	mo = P_SpawnPlayerMissile(player->mo, MT_MACEFX4);
	if(mo)
	{
		mo->momx += player->mo->momx;
		mo->momy += player->mo->momy;
		mo->momz = 2*FRACUNIT+((player->lookdir)<<(FRACBITS-5));
		if(linetarget)
		{
			mo->special1 = (int)linetarget;
		}
	}
	S_StartSound(player->mo, sfx_lobsht);
}

//----------------------------------------------------------------------------
//
// PROC A_DeathBallImpact
//
//----------------------------------------------------------------------------

void A_DeathBallImpact(mobj_t *ball)
{
	int i;
	mobj_t *target;
	angle_t angle;
	boolean newAngle;

	if((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
	{ // Landed in some sort of liquid
		P_RemoveMobj(ball);
		return;
	}
	if((ball->z <= ball->floorz) && ball->momz)
	{ // Bounce
		newAngle = false;
		target = (mobj_t *)ball->special1;
		if(target)
		{
			if(!(target->flags&MF_SHOOTABLE))
			{ // Target died
				ball->special1 = 0;
			}
			else
			{ // Seek
				angle = R_PointToAngle2(ball->x, ball->y,
					target->x, target->y);
				newAngle = true;
			}
		}
		else
		{ // Find new target
			angle = 0;
			for(i = 0; i < 16; i++)
			{
				P_AimLineAttack(ball, angle, 10*64*FRACUNIT);
				if(linetarget && ball->target != linetarget)
				{
					ball->special1 = (int)linetarget;
					angle = R_PointToAngle2(ball->x, ball->y,
						linetarget->x, linetarget->y);
					newAngle = true;
					break;
				}
				angle += ANGLE_45/2;
			}
		}
		if(newAngle)
		{
			ball->angle = angle;
			angle >>= ANGLETOFINESHIFT;
			ball->momx = FixedMul(ball->info->speed, finecosine[angle]);
			ball->momy = FixedMul(ball->info->speed, finesine[angle]);
		}
		P_SetMobjState(ball, ball->info->spawnstate);
		S_StartSound(ball, sfx_pstop);
	}
	else
	{ // Explode
		ball->flags |= MF_NOGRAVITY;
		ball->flags2 &= ~MF2_LOGRAV;
		S_StartSound(ball, sfx_phohit);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_SpawnRippers
//
//----------------------------------------------------------------------------

void A_SpawnRippers(mobj_t *actor)
{
	int i;
	angle_t angle;
	mobj_t *ripper;

	for(i = 0; i < 8; i++)
	{
		ripper = P_SpawnMobj(actor->x, actor->y, actor->z, MT_RIPPER);
		angle = i*ANG45;
		ripper->target = actor->target;
		ripper->angle = angle;
		angle >>= ANGLETOFINESHIFT;
		ripper->momx = FixedMul(ripper->info->speed, finecosine[angle]);
		ripper->momy = FixedMul(ripper->info->speed, finesine[angle]);
		P_CheckMissileSpawn(ripper);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_FireCrossbowPL1
//
//----------------------------------------------------------------------------

void A_FireCrossbowPL1(player_t *player, pspdef_t *psp)
{
	mobj_t *pmo;

	pmo = player->mo;
	player->ammo[am_crossbow] -= USE_CBOW_AMMO_1;
	P_SpawnPlayerMissile(pmo, MT_CRBOWFX1);
	P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle-(ANG45/10));
	P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle+(ANG45/10));
}

//----------------------------------------------------------------------------
//
// PROC A_FireCrossbowPL2
//
//----------------------------------------------------------------------------

void A_FireCrossbowPL2(player_t *player, pspdef_t *psp)
{
	mobj_t *pmo;

	pmo = player->mo;
	player->ammo[am_crossbow] -=
		deathmatch ? USE_CBOW_AMMO_1 : USE_CBOW_AMMO_2;
	P_SpawnPlayerMissile(pmo, MT_CRBOWFX2);
	P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle-(ANG45/10));
	P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle+(ANG45/10));
	P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle-(ANG45/5));
	P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle+(ANG45/5));
}

//----------------------------------------------------------------------------
//
// PROC A_BoltSpark
//
//----------------------------------------------------------------------------

void A_BoltSpark(mobj_t *bolt)
{
	mobj_t *spark;

	if(P_Random() > 50)
	{
		spark = P_SpawnMobj(bolt->x, bolt->y, bolt->z, MT_CRBOWFX4);
		spark->x += (P_Random()-P_Random())<<10;
		spark->y += (P_Random()-P_Random())<<10;
	}
}

//----------------------------------------------------------------------------
//
// PROC A_FireSkullRodPL1
//
//----------------------------------------------------------------------------

void A_FireSkullRodPL1(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;

	if(player->ammo[am_skullrod] < USE_SKRD_AMMO_1)
	{
		return;
	}
	player->ammo[am_skullrod] -= USE_SKRD_AMMO_1;
	mo = P_SpawnPlayerMissile(player->mo, MT_HORNRODFX1);
	// Randomize the first frame
	if(mo && P_Random() > 128)
	{
		P_SetMobjState(mo, S_HRODFX1_2);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_FireSkullRodPL2
//
// The special2 field holds the player number that shot the rain missile.
// The special1 field is used for the seeking routines, then as a counter
// for the sound looping.
//
//----------------------------------------------------------------------------

void A_FireSkullRodPL2(player_t *player, pspdef_t *psp)
{
	player->ammo[am_skullrod] -=
		deathmatch ? USE_SKRD_AMMO_1 : USE_SKRD_AMMO_2;
	P_SpawnPlayerMissile(player->mo, MT_HORNRODFX2);
	// Use MissileMobj instead of the return value from
	// P_SpawnPlayerMissile because we need to give info to the mobj
	// even if it exploded immediately.
	if(netgame)
	{ // Multi-player game
		MissileMobj->special2 = P_GetPlayerNum(player);
	}
	else
	{ // Always use red missiles in single player games
		MissileMobj->special2 = 2;
	}
	if(linetarget)
	{
		MissileMobj->special1 = (int)linetarget;
	}
	S_StartSound(MissileMobj, sfx_hrnpow);
}

//----------------------------------------------------------------------------
//
// PROC A_SkullRodPL2Seek
//
//----------------------------------------------------------------------------

void A_SkullRodPL2Seek(mobj_t *actor)
{
	P_SeekerMissile(actor, ANGLE_1*10, ANGLE_1*30);
}

//----------------------------------------------------------------------------
//
// PROC A_AddPlayerRain
//
//----------------------------------------------------------------------------

void A_AddPlayerRain(mobj_t *actor)
{
	int playerNum;
	player_t *player;

	playerNum = netgame ? actor->special2 : 0;
	if(!playeringame[playerNum])
	{ // Player left the game
		return;
	}
	player = &players[playerNum];
	if(player->health <= 0)
	{ // Player is dead
		return;
	}
	if(player->rain1 && player->rain2)
	{ // Terminate an active rain
		if(player->rain1->health < player->rain2->health)
		{
			if(player->rain1->health > 16)
			{
				player->rain1->health = 16;
			}
			player->rain1 = NULL;
		}
		else
		{
			if(player->rain2->health > 16)
			{
				player->rain2->health = 16;
			}
			player->rain2 = NULL;
		}
	}
	// Add rain mobj to list
	if(player->rain1)
	{
		player->rain2 = actor;
	}
	else
	{
		player->rain1 = actor;
	}
}

//----------------------------------------------------------------------------
//
// PROC A_SkullRodStorm
//
//----------------------------------------------------------------------------

void A_SkullRodStorm(mobj_t *actor)
{
	fixed_t x;
	fixed_t y;
	mobj_t *mo;
	int playerNum;
	player_t *player;

	if(actor->health-- == 0)
	{
		P_SetMobjState(actor, S_NULL);
		playerNum = netgame ? actor->special2 : 0;
		if(!playeringame[playerNum])
		{ // Player left the game
			return;
		}
		player = &players[playerNum];
		if(player->health <= 0)
		{ // Player is dead
			return;
		}
		if(player->rain1 == actor)
		{
			player->rain1 = NULL;
		}
		else if(player->rain2 == actor)
		{
			player->rain2 = NULL;
		}
		return;
	}
	if(P_Random() < 25)
	{ // Fudge rain frequency
		return;
	}
	x = actor->x+((P_Random()&127)-64)*FRACUNIT;
	y = actor->y+((P_Random()&127)-64)*FRACUNIT;
	mo = P_SpawnMobj(x, y, ONCEILINGZ, MT_RAINPLR1+actor->special2);
	mo->target = actor->target;
	mo->momx = 1; // Force collision detection
	mo->momz = -mo->info->speed;
	mo->special2 = actor->special2; // Transfer player number
	P_CheckMissileSpawn(mo);
	if(!(actor->special1&31))
	{
		S_StartSound(actor, sfx_ramrain);
	}
	actor->special1++;
}

//----------------------------------------------------------------------------
//
// PROC A_RainImpact
//
//----------------------------------------------------------------------------

void A_RainImpact(mobj_t *actor)
{
	if(actor->z > actor->floorz)
	{
		P_SetMobjState(actor, S_RAINAIRXPLR1_1+actor->special2);
	}
	else if(P_Random() < 40)
	{
		P_HitFloor(actor);
	}
}

//----------------------------------------------------------------------------
//
// PROC A_HideInCeiling
//
//----------------------------------------------------------------------------

void A_HideInCeiling(mobj_t *actor)
{
	actor->z = actor->ceilingz+4*FRACUNIT;
}

//----------------------------------------------------------------------------
//
// PROC A_FirePhoenixPL1
//
//----------------------------------------------------------------------------

void A_FirePhoenixPL1(player_t *player, pspdef_t *psp)
{
	angle_t angle;

	player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_1;
	P_SpawnPlayerMissile(player->mo, MT_PHOENIXFX1);
	//P_SpawnPlayerMissile(player->mo, MT_MNTRFX2);
	angle = player->mo->angle+ANG180;
	angle >>= ANGLETOFINESHIFT;
	player->mo->momx += FixedMul(4*FRACUNIT, finecosine[angle]);
	player->mo->momy += FixedMul(4*FRACUNIT, finesine[angle]);
}

//----------------------------------------------------------------------------
//
// PROC A_PhoenixPuff
//
//----------------------------------------------------------------------------

void A_PhoenixPuff(mobj_t *actor)
{
	mobj_t *puff;
	angle_t angle;

	P_SeekerMissile(actor, ANGLE_1*5, ANGLE_1*10);
	puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
	angle = actor->angle+ANG90;
	angle >>= ANGLETOFINESHIFT;
	puff->momx = FixedMul(FRACUNIT*1.3, finecosine[angle]);
	puff->momy = FixedMul(FRACUNIT*1.3, finesine[angle]);
	puff->momz = 0;
	puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
	angle = actor->angle-ANG90;
	angle >>= ANGLETOFINESHIFT;
	puff->momx = FixedMul(FRACUNIT*1.3, finecosine[angle]);
	puff->momy = FixedMul(FRACUNIT*1.3, finesine[angle]);
	puff->momz = 0;
}

//----------------------------------------------------------------------------
//
// PROC A_InitPhoenixPL2
//
//----------------------------------------------------------------------------

void A_InitPhoenixPL2(player_t *player, pspdef_t *psp)
{
	player->flamecount = FLAME_THROWER_TICS;
}

//----------------------------------------------------------------------------
//
// PROC A_FirePhoenixPL2
//
// Flame thrower effect.
//
//----------------------------------------------------------------------------

void A_FirePhoenixPL2(player_t *player, pspdef_t *psp)
{
	mobj_t *mo;
	mobj_t *pmo;
	angle_t angle;
	fixed_t x, y, z;
	fixed_t slope;

	if(--player->flamecount == 0)
	{ // Out of flame
		P_SetPsprite(player, ps_weapon, S_PHOENIXATK2_4);
		player->refire = 0;
		return;
	}
	pmo = player->mo;
	angle = pmo->angle;
	x = pmo->x+((P_Random()-P_Random())<<9);
	y = pmo->y+((P_Random()-P_Random())<<9);
	z = pmo->z+26*FRACUNIT+((player->lookdir)<<FRACBITS)/173;
	if(pmo->flags2&MF2_FEETARECLIPPED)
	{
		z -= FOOTCLIPSIZE;
	}
	slope = ((player->lookdir)<<FRACBITS)/173+(FRACUNIT/10);
	mo = P_SpawnMobj(x, y, z, MT_PHOENIXFX2);
	mo->target = pmo;
	mo->angle = angle;
	mo->momx = pmo->momx+FixedMul(mo->info->speed,
		finecosine[angle>>ANGLETOFINESHIFT]);
	mo->momy = pmo->momy+FixedMul(mo->info->speed,
		finesine[angle>>ANGLETOFINESHIFT]);
	mo->momz = FixedMul(mo->info->speed, slope);
	if(!player->refire || !(leveltime%38))
	{
		S_StartSound(player->mo, sfx_phopow);
	}	
	P_CheckMissileSpawn(mo);
}

//----------------------------------------------------------------------------
//
// PROC A_ShutdownPhoenixPL2
//
//----------------------------------------------------------------------------

void A_ShutdownPhoenixPL2(player_t *player, pspdef_t *psp)
{
	player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2;
}

//----------------------------------------------------------------------------
//
// PROC A_FlameEnd
//
//----------------------------------------------------------------------------

void A_FlameEnd(mobj_t *actor)
{
	actor->momz += 1.5*FRACUNIT;
}

//----------------------------------------------------------------------------
//
// PROC A_FloatPuff
//
//----------------------------------------------------------------------------

void A_FloatPuff(mobj_t *puff)
{
	puff->momz += 1.8*FRACUNIT;
}

//---------------------------------------------------------------------------
//
// PROC A_GauntletAttack
//
//---------------------------------------------------------------------------

void A_GauntletAttack(player_t *player, pspdef_t *psp)
{
	angle_t angle;
	int damage;
	int slope;
	int randVal;
	fixed_t dist;

	psp->sx = ((P_Random()&3)-2)*FRACUNIT;
	psp->sy = WEAPONTOP+(P_Random()&3)*FRACUNIT;
	angle = player->mo->angle;
	if(player->powers[pw_weaponlevel2])
	{
		damage = HITDICE(2);
		dist = 4*MELEERANGE;
		angle += (P_Random()-P_Random())<<17;
		PuffType = MT_GAUNTLETPUFF2;
	}
	else
	{
		damage = HITDICE(2);
		dist = MELEERANGE+1;
		angle += (P_Random()-P_Random())<<18;
		PuffType = MT_GAUNTLETPUFF1;
	}
	slope = P_AimLineAttack(player->mo, angle, dist);
	P_LineAttack(player->mo, angle, dist, slope, damage);
	if(!linetarget)
	{
		if(P_Random() > 64)
		{
			player->extralight = !player->extralight;
		}
		S_StartSound(player->mo, sfx_gntful);
		return;
	}
	randVal = P_Random();
	if(randVal < 64)
	{
		player->extralight = 0;
	}
	else if(randVal < 160)
	{
		player->extralight = 1;
	}
	else
	{
		player->extralight = 2;
	}
	if(player->powers[pw_weaponlevel2])
	{
		P_GiveBody(player, damage>>1);
		S_StartSound(player->mo, sfx_gntpow);
	}
	else
	{
		S_StartSound(player->mo, sfx_gnthit);
	}
	// turn to face target
	angle = R_PointToAngle2(player->mo->x, player->mo->y,
		linetarget->x, linetarget->y);
	if(angle-player->mo->angle > ANG180)
	{
		if(angle-player->mo->angle < -ANG90/20)
			player->mo->angle = angle+ANG90/21;
		else
			player->mo->angle -= ANG90/20;
	}
	else
	{
		if(angle-player->mo->angle > ANG90/20)
			player->mo->angle = angle-ANG90/21;
		else
			player->mo->angle += ANG90/20;
	}
	player->mo->flags |= MF_JUSTATTACKED;
}

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