
//**************************************************************************
//**
//** p_inter.c : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: p_inter.c,v $
//** $Revision: 1.145 $
//** $Date: 96/01/16 10:35:33 $
//** $Author: bgokey $
//**
//**************************************************************************

#include "h2def.h"
#include "p_local.h"
#include "soundst.h"

#define BONUSADD 6

int ArmorIncrement[NUMCLASSES][NUMARMOR] =
{
	{ 25*FRACUNIT, 20*FRACUNIT, 15*FRACUNIT, 5*FRACUNIT },
	{ 10*FRACUNIT, 25*FRACUNIT, 5*FRACUNIT, 20*FRACUNIT },
	{ 5*FRACUNIT, 15*FRACUNIT, 10*FRACUNIT, 25*FRACUNIT },
	{ 0, 0, 0, 0 }
};

int AutoArmorSave[NUMCLASSES] = { 15*FRACUNIT, 10*FRACUNIT, 5*FRACUNIT, 0 };

char *TextKeyMessages[] = 
{
	TXT_KEY_STEEL,
	TXT_KEY_CAVE,
	TXT_KEY_AXE,
	TXT_KEY_FIRE,
	TXT_KEY_EMERALD,
	TXT_KEY_DUNGEON,
	TXT_KEY_SILVER,
	TXT_KEY_RUSTED,
	TXT_KEY_HORN,
	TXT_KEY_SWAMP,
	TXT_KEY_CASTLE
};

static void SetDormantArtifact(mobj_t *arti);
static void TryPickupArtifact(player_t *player, artitype_t artifactType,
	mobj_t *artifact);
static void TryPickupWeapon(player_t *player, pclass_t weaponClass,
	weapontype_t weaponType, mobj_t *weapon, char *message);
static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass,
	int pieceValue, mobj_t *pieceMobj);

#ifdef __NeXT__
extern void strupr(char *s);
#endif

//--------------------------------------------------------------------------
//
// PROC P_SetMessage
//
//--------------------------------------------------------------------------

void P_SetMessage(player_t *player, char *message, boolean ultmsg)
{
	extern boolean messageson;
	
	if((player->ultimateMessage || !messageson) && !ultmsg)
	{
		return;
	}
	if(strlen(message) > 79)
	{
		memcpy(player->message, message, 80);
		player->message[80] = 0;
	}
	else
	{
		strcpy(player->message, message);
	}
	strupr(player->message);
	player->messageTics = MESSAGETICS;
	player->yellowMessage = false;
	if(ultmsg)
	{
		player->ultimateMessage = true;
	}
	if(player == &players[consoleplayer])
	{
		BorderTopRefresh = true;
	}
}

//==========================================================================
//
// P_SetYellowMessage
//
//==========================================================================

void P_SetYellowMessage(player_t *player, char *message, boolean ultmsg)
{
	extern boolean messageson;
	
	if((player->ultimateMessage || !messageson) && !ultmsg)
	{
		return;
	}
	if(strlen(message) > 79)
	{
		memcpy(player->message, message, 80);
		player->message[80] = 0;
	}
	else
	{
		strcpy(player->message, message);
	}
	player->messageTics = 5*MESSAGETICS; // Bold messages last longer
	player->yellowMessage = true;
	if(ultmsg)
	{
		player->ultimateMessage = true;
	}
	if(player == &players[consoleplayer])
	{
		BorderTopRefresh = true;
	}
}

//==========================================================================
//
// P_ClearMessage
//
//==========================================================================

void P_ClearMessage(player_t *player)
{
	player->messageTics = 0;
	if(player == &players[consoleplayer])
	{
		BorderTopRefresh = true;
	}
}

//----------------------------------------------------------------------------
//
// PROC P_HideSpecialThing
//
//----------------------------------------------------------------------------

void P_HideSpecialThing(mobj_t *thing)
{
	thing->flags &= ~MF_SPECIAL;
	thing->flags2 |= MF2_DONTDRAW;
	P_SetMobjState(thing, S_HIDESPECIAL1);
}

//--------------------------------------------------------------------------
//
// FUNC P_GiveMana
//
// Returns true if the player accepted the mana, false if it was
// refused (player has MAX_MANA).
//
//--------------------------------------------------------------------------

boolean P_GiveMana(player_t *player, manatype_t mana, int count)
{
	int prevMana;
	//weapontype_t changeWeapon;

	if(mana == MANA_NONE || mana == MANA_BOTH)
	{
		return(false);
	}
	if(mana < 0 || mana > NUMMANA)
	{
		I_Error("P_GiveMana: bad type %i", mana);
	}
	if(player->mana[mana] == MAX_MANA)
	{
		return(false);
	}
	if(gameskill == sk_baby || gameskill == sk_nightmare)
	{ // extra mana in baby mode and nightmare mode
		count += count>>1;
	}
	prevMana = player->mana[mana];

	player->mana[mana] += count;
	if(player->mana[mana] > MAX_MANA)
	{
		player->mana[mana] = MAX_MANA;
	}
	if(player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND
	&& mana == MANA_1 && prevMana <= 0)
	{
		P_SetPsprite(player, ps_weapon, S_FAXEREADY_G);
	}
	return(true);
}

//==========================================================================
//
// TryPickupWeapon
//
//==========================================================================

static void TryPickupWeapon(player_t *player, pclass_t weaponClass,
	weapontype_t weaponType, mobj_t *weapon, char *message)
{
	boolean remove;
	boolean gaveMana;
	boolean gaveWeapon;

	remove = true;
	if(player->class != weaponClass)
	{ // Wrong class, but try to pick up for mana
		if(netgame && !deathmatch)
		{ // Can't pick up weapons for other classes in coop netplay
			return;
		}
		if(weaponType == WP_SECOND)
		{
			if(!P_GiveMana(player, MANA_1, 25))
			{
				return;
			}
		}
		else
		{
			if(!P_GiveMana(player, MANA_2, 25))
			{
				return;
			}
		}
	}
	else if(netgame && !deathmatch)
	{ // Cooperative net-game
		if(player->weaponowned[weaponType])
		{
			return;
		}
		player->weaponowned[weaponType] = true;
		if(weaponType == WP_SECOND)
		{
			P_GiveMana(player, MANA_1, 25);
		}
		else
		{
			P_GiveMana(player, MANA_2, 25);
		}
		player->pendingweapon = weaponType;
		remove = false;
	}
	else
	{ // Deathmatch or single player game
		if(weaponType == WP_SECOND)
		{
			gaveMana = P_GiveMana(player, MANA_1, 25);
		}
		else 
		{
			gaveMana = P_GiveMana(player, MANA_2, 25);
		}
		if(player->weaponowned[weaponType])
		{
			gaveWeapon = false;
		}
		else
		{
			gaveWeapon = true;
			player->weaponowned[weaponType] = true;
			if(weaponType > player->readyweapon)
			{ // Only switch to more powerful weapons
				player->pendingweapon = weaponType;
			}
		}
		if(!(gaveWeapon || gaveMana))
		{ // Player didn't need the weapon or any mana
			return;
		}
	}

	P_SetMessage(player, message, false);
	if(weapon->special)
	{
		P_ExecuteLineSpecial(weapon->special, weapon->args,
			NULL, 0, player->mo);
		weapon->special = 0;
	}

	if(remove)
	{
		if(deathmatch && !(weapon->flags2&MF2_DROPPED))
		{
			P_HideSpecialThing(weapon);
		}
		else
		{
			P_RemoveMobj(weapon);
		}
	}

	player->bonuscount += BONUSADD;
	if(player == &players[consoleplayer])
	{
		S_StartSound(NULL, SFX_PICKUP_WEAPON);
		SB_PaletteFlash(false);
	}
}

//--------------------------------------------------------------------------
//
// FUNC P_GiveWeapon
//
// Returns true if the weapon or its mana was accepted.
//
//--------------------------------------------------------------------------

/*
boolean P_GiveWeapon(player_t *player, pclass_t class, weapontype_t weapon)
{
	boolean gaveMana;
	boolean gaveWeapon;

	if(player->class != class)
	{ // player cannot use this weapon, take it anyway, and get mana
		if(netgame && !deathmatch)
		{ // Can't pick up weapons for other classes in coop netplay
			return false;
		}
		if(weapon == WP_SECOND)
		{
			return P_GiveMana(player, MANA_1, 25);
		}
		else
		{
			return P_GiveMana(player, MANA_2, 25);
		}		
	}
	if(netgame && !deathmatch)
	{ // Cooperative net-game
		if(player->weaponowned[weapon])
		{
			return(false);
		}
		player->bonuscount += BONUSADD;
		player->weaponowned[weapon] = true;
		if(weapon == WP_SECOND)
		{
			P_GiveMana(player, MANA_1, 25);
		}
		else 
		{
			P_GiveMana(player, MANA_2, 25);
		}
		player->pendingweapon = weapon;
		if(player == &players[consoleplayer])
		{
			S_StartSound(NULL, SFX_PICKUP_WEAPON);
		}
		return(false);
	}
	if(weapon == WP_SECOND)
	{
		gaveMana = P_GiveMana(player, MANA_1, 25);
	}
	else 
	{
		gaveMana = P_GiveMana(player, MANA_2, 25);
	}
	if(player->weaponowned[weapon])
	{
		gaveWeapon = false;
	}
	else
	{
		gaveWeapon = true;
		player->weaponowned[weapon] = true;
		if(weapon > player->readyweapon)
		{ // Only switch to more powerful weapons
			player->pendingweapon = weapon;
		}
	}
	return(gaveWeapon || gaveMana);
}
*/

//===========================================================================
//
// P_GiveWeaponPiece
//
//===========================================================================

/*
boolean P_GiveWeaponPiece(player_t *player, pclass_t class, int piece)
{
	P_GiveMana(player, MANA_1, 20);
	P_GiveMana(player, MANA_2, 20);
	if(player->class != class)
	{
		return true;
	}
	else if(player->pieces&piece)
	{ // player already has that weapon piece
		return true;
	}
	player->pieces |= piece;
	if(player->pieces == 7)
	{ // player has built the fourth weapon!
		P_GiveWeapon(player, class, WP_FOURTH);
		S_StartSound(player->mo, SFX_WEAPON_BUILD);
	}
	return true;
}
*/

//==========================================================================
//
// TryPickupWeaponPiece
//
//==========================================================================

static void TryPickupWeaponPiece(player_t *player, pclass_t matchClass,
	int pieceValue, mobj_t *pieceMobj)
{
	boolean remove;
	boolean checkAssembled;
	boolean gaveWeapon;
	int gaveMana;
	static char *fourthWeaponText[] =
	{
		TXT_WEAPON_F4,
		TXT_WEAPON_C4,
		TXT_WEAPON_M4
	};
	static char *weaponPieceText[] =
	{
		TXT_QUIETUS_PIECE,
		TXT_WRAITHVERGE_PIECE,
		TXT_BLOODSCOURGE_PIECE
	};
	static int pieceValueTrans[] =
	{
		0,							// 0: never
		WPIECE1|WPIECE2|WPIECE3,	// WPIECE1 (1)
		WPIECE2|WPIECE3,			// WPIECE2 (2)
		0,							// 3: never
		WPIECE3						// WPIECE3 (4)
	};

	remove = true;
	checkAssembled = true;
	gaveWeapon = false;
	if(player->class != matchClass)
	{ // Wrong class, but try to pick up for mana
		if(netgame && !deathmatch)
		{ // Can't pick up wrong-class weapons in coop netplay
			return;
		}
		checkAssembled = false;
		gaveMana = P_GiveMana(player, MANA_1, 20)+
			P_GiveMana(player, MANA_2, 20);
		if(!gaveMana)
		{ // Didn't need the mana, so don't pick it up
			return;
		}
	}
	else if(netgame && !deathmatch)
	{ // Cooperative net-game
		if(player->pieces&pieceValue)
		{ // Already has the piece
			return;
		}
		pieceValue = pieceValueTrans[pieceValue];
		P_GiveMana(player, MANA_1, 20);
		P_GiveMana(player, MANA_2, 20);
		remove = false;
	}
	else
	{ // Deathmatch or single player game
		gaveMana = P_GiveMana(player, MANA_1, 20)+
			P_GiveMana(player, MANA_2, 20);
		if(player->pieces&pieceValue)
		{ // Already has the piece, check if mana needed
			if(!gaveMana)
			{ // Didn't need the mana, so don't pick it up
				return;
			}
			checkAssembled = false;
		}
	}

	// Pick up the weapon piece
	if(pieceMobj->special)
	{
		P_ExecuteLineSpecial(pieceMobj->special, pieceMobj->args,
			NULL, 0, player->mo);
		pieceMobj->special = 0;
	}
	if(remove)
	{
		if(deathmatch && !(pieceMobj->flags2&MF2_DROPPED))
		{
			P_HideSpecialThing(pieceMobj);
		}
		else
		{
			P_RemoveMobj(pieceMobj);
		}
	}
	player->bonuscount += BONUSADD;
	if(player == &players[consoleplayer])
	{
		SB_PaletteFlash(false);
	}

	// Check if fourth weapon assembled
	if(checkAssembled)
	{
		player->pieces |= pieceValue;
		if(player->pieces == (WPIECE1|WPIECE2|WPIECE3))
		{
			gaveWeapon = true;
			player->weaponowned[WP_FOURTH] = true;
			player->pendingweapon = WP_FOURTH;
		}
	}

	if(gaveWeapon)
	{
		P_SetMessage(player, fourthWeaponText[matchClass], false);
		// Play the build-sound full volume for all players
		S_StartSound(NULL, SFX_WEAPON_BUILD);
	}
	else
	{
		P_SetMessage(player, weaponPieceText[matchClass], false);
		if(player == &players[consoleplayer])
		{
			S_StartSound(NULL, SFX_PICKUP_WEAPON);
		}
	}
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------

boolean P_GiveBody(player_t *player, int num)
{
	int max;

	max = MAXHEALTH;
	if(player->morphTics)
	{
		max = MAXMORPHHEALTH;
	}
	if(player->health >= max)
	{
		return(false);
	}
	player->health += num;
	if(player->health > max)
	{
		player->health = max;
	}
	player->mo->health = player->health;
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveArmor
//
// Returns false if the armor is worse than the current armor.
//
//---------------------------------------------------------------------------

boolean P_GiveArmor(player_t *player, armortype_t armortype, int amount)
{
	int hits;
	int totalArmor;

	extern int ArmorMax[NUMCLASSES];

	if(amount == -1)
	{
		hits = ArmorIncrement[player->class][armortype];
		if(player->armorpoints[armortype] >= hits)
		{
			return false;
		}
		else
		{
			player->armorpoints[armortype] = hits;
		}
	}
	else
	{
		hits = amount*5*FRACUNIT;
		totalArmor = player->armorpoints[ARMOR_ARMOR]
			+player->armorpoints[ARMOR_SHIELD]
			+player->armorpoints[ARMOR_HELMET]
			+player->armorpoints[ARMOR_AMULET]
			+AutoArmorSave[player->class];
		if(totalArmor < ArmorMax[player->class]*5*FRACUNIT)
		{
			player->armorpoints[armortype] += hits;
		}
		else
		{
			return false;
		}
	}
	return true;
}

//---------------------------------------------------------------------------
//
// PROC P_GiveKey
//
//---------------------------------------------------------------------------

int P_GiveKey(player_t *player, keytype_t key)
{
	if(player->keys&(1<<key))
	{
		return false;
	}
	player->bonuscount += BONUSADD;
	player->keys |= 1<<key;
	return true;
}

//---------------------------------------------------------------------------
//
// FUNC P_GivePower
//
// Returns true if power accepted.
//
//---------------------------------------------------------------------------

boolean P_GivePower(player_t *player, powertype_t power)
{
	if(power == pw_invulnerability)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = INVULNTICS;
		player->mo->flags2 |= MF2_INVULNERABLE;
		if(player->class == PCLASS_MAGE)
		{
			player->mo->flags2 |= MF2_REFLECTIVE;
		}
		return(true);
	}
	if(power == pw_flight)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = FLIGHTTICS;
		player->mo->flags2 |= MF2_FLY;
		player->mo->flags |= MF_NOGRAVITY;
		if(player->mo->z <= player->mo->floorz)
		{
			player->flyheight = 10; // thrust the player in the air a bit
		}
		return(true);
	}
	if(power == pw_infrared)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = INFRATICS;
		return(true);
	}
	if(power == pw_speed)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = SPEEDTICS;
		return(true);
	}
	if(power == pw_minotaur)
	{
		// Doesn't matter if already have power, renew ticker
		player->powers[power] = MAULATORTICS;
		return(true);
	}
/*
	if(power == pw_ironfeet)
	{
		player->powers[power] = IRONTICS;
		return(true);
	}
	if(power == pw_strength)
	{
		P_GiveBody(player, 100);
		player->powers[power] = 1;
		return(true);
	}
*/
	if(player->powers[power])
	{
		return(false); // already got it
	}
	player->powers[power] = 1;
	return(true);
}

//==========================================================================
//
// TryPickupArtifact
//
//==========================================================================

static void TryPickupArtifact(player_t *player, artitype_t artifactType,
	mobj_t *artifact)
{
	static char *artifactMessages[NUMARTIFACTS] =
	{
		NULL,
		TXT_ARTIINVULNERABILITY,
		TXT_ARTIHEALTH,
		TXT_ARTISUPERHEALTH,
		TXT_ARTIHEALINGRADIUS,
		TXT_ARTISUMMON,
		TXT_ARTITORCH,
		TXT_ARTIEGG,
		TXT_ARTIFLY,
		TXT_ARTIBLASTRADIUS,
		TXT_ARTIPOISONBAG,
		TXT_ARTITELEPORTOTHER,
		TXT_ARTISPEED,
		TXT_ARTIBOOSTMANA,
		TXT_ARTIBOOSTARMOR,
		TXT_ARTITELEPORT,
		TXT_ARTIPUZZSKULL,
		TXT_ARTIPUZZGEMBIG,
		TXT_ARTIPUZZGEMRED,
		TXT_ARTIPUZZGEMGREEN1,
		TXT_ARTIPUZZGEMGREEN2,
		TXT_ARTIPUZZGEMBLUE1,
		TXT_ARTIPUZZGEMBLUE2,
		TXT_ARTIPUZZBOOK1,
		TXT_ARTIPUZZBOOK2,
		TXT_ARTIPUZZSKULL2,
		TXT_ARTIPUZZFWEAPON,
		TXT_ARTIPUZZCWEAPON,
		TXT_ARTIPUZZMWEAPON,
		TXT_ARTIPUZZGEAR,	// All gear pickups use the same text
		TXT_ARTIPUZZGEAR,
		TXT_ARTIPUZZGEAR,
		TXT_ARTIPUZZGEAR
	};

	if(P_GiveArtifact(player, artifactType, artifact))
	{
		if(artifact->special)
		{
			P_ExecuteLineSpecial(artifact->special, artifact->args,
				NULL, 0, NULL);
			artifact->special = 0;
		}
		player->bonuscount += BONUSADD;
		if(artifactType < arti_firstpuzzitem)
		{
			SetDormantArtifact(artifact);
			S_StartSound(artifact, SFX_PICKUP_ARTIFACT);
			P_SetMessage(player, artifactMessages[artifactType], false);
		}
		else
		{ // Puzzle item
			S_StartSound(NULL, SFX_PICKUP_ITEM);
			P_SetMessage(player, artifactMessages[artifactType], true);
			if(!netgame || deathmatch)
			{ // Remove puzzle items if not cooperative netplay
				P_RemoveMobj(artifact);
			}
		}
	}
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveArtifact
//
// Returns true if artifact accepted.
//
//---------------------------------------------------------------------------

boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo)
{
	int i;
	int j;
	boolean slidePointer;

	slidePointer = false;
	i = 0;
	while(player->inventory[i].type != arti && i < player->inventorySlotNum)
	{
		i++;
	}
	if(i == player->inventorySlotNum)
	{
		if(arti < arti_firstpuzzitem)
		{
			i = 0;
			while(player->inventory[i].type < arti_firstpuzzitem
			&& i < player->inventorySlotNum)
			{
				i++;
			}
			if(i != player->inventorySlotNum)
			{
				for(j = player->inventorySlotNum; j > i; j--)
				{
					player->inventory[j].count = player->inventory[j-1].count;
					player->inventory[j].type = player->inventory[j-1].type;
					slidePointer = true;
				}
			}
		}
		player->inventory[i].count = 1;
		player->inventory[i].type = arti;
		player->inventorySlotNum++;
	}
	else
	{
		if(arti >= arti_firstpuzzitem && netgame && !deathmatch)
		{ // Can't carry more than 1 puzzle item in coop netplay
			return false;
		}
		if(player->inventory[i].count >= 25)
		{ // Player already has 25 of this item
			return false;
		}
		player->inventory[i].count++;
	}
	if(!player->artifactCount)
	{
		player->readyArtifact = arti;
	}
	else if(player == &players[consoleplayer] && slidePointer
		&& i <= inv_ptr)
	{
		inv_ptr++;
		curpos++;
		if(curpos > 6)
		{
			curpos = 6;
		}
	}
	player->artifactCount++;
	return(true);
}

//==========================================================================
//
// SetDormantArtifact
//
// Removes the MF_SPECIAL flag and initiates the artifact pickup
// animation.
//
//==========================================================================

static void SetDormantArtifact(mobj_t *arti)
{
	arti->flags &= ~MF_SPECIAL;
	if(deathmatch && !(arti->flags2 & MF2_DROPPED))
	{
		if(arti->type == MT_ARTIINVULNERABILITY)
		{
			P_SetMobjState(arti, S_DORMANTARTI3_1);
		}
		else if(arti->type == MT_SUMMONMAULATOR
			|| arti->type == MT_ARTIFLY)
		{
			P_SetMobjState(arti, S_DORMANTARTI2_1);
		}
		else
		{
			P_SetMobjState(arti, S_DORMANTARTI1_1);
		}
	}
	else
	{ // Don't respawn
		P_SetMobjState(arti, S_DEADARTI1);
	}
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreArtifact
//
//---------------------------------------------------------------------------

void A_RestoreArtifact(mobj_t *arti)
{
	arti->flags |= MF_SPECIAL;
	P_SetMobjState(arti, arti->info->spawnstate);
	S_StartSound(arti, SFX_RESPAWN);
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing1
//
// Make a special thing visible again.
//
//---------------------------------------------------------------------------

void A_RestoreSpecialThing1(mobj_t *thing)
{
	thing->flags2 &= ~MF2_DONTDRAW;
	S_StartSound(thing, SFX_RESPAWN);
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing2
//
//---------------------------------------------------------------------------

void A_RestoreSpecialThing2(mobj_t *thing)
{
	thing->flags |= MF_SPECIAL;
	P_SetMobjState(thing, thing->info->spawnstate);
}

//---------------------------------------------------------------------------
//
// PROC P_TouchSpecialThing
//
//---------------------------------------------------------------------------

void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
{
	player_t *player;
	fixed_t delta;
	int sound;
	boolean respawn;

	delta = special->z-toucher->z;
	if(delta > toucher->height || delta < -32*FRACUNIT)
	{ // Out of reach
		return;
	}
	if(toucher->health <= 0)
	{ // Toucher is dead
		return;
	}
	sound = SFX_PICKUP_ITEM;
	player = toucher->player;
	respawn = true;
	switch(special->sprite)
	{
		// Items
		case SPR_PTN1: // Item_HealingPotion
			if(!P_GiveBody(player, 10))
			{
				return;
			}
			P_SetMessage(player, TXT_ITEMHEALTH, false);
			break;
		case SPR_ARM1:
			if(!P_GiveArmor(player, ARMOR_ARMOR, -1))
			{
				return;
			}
			P_SetMessage(player, TXT_ARMOR1, false);
			break;
		case SPR_ARM2:
			if(!P_GiveArmor(player, ARMOR_SHIELD, -1))
			{
				return;
			}
			P_SetMessage(player, TXT_ARMOR2, false);
			break;
		case SPR_ARM3:
			if(!P_GiveArmor(player, ARMOR_HELMET, -1))
			{
				return;
			}
			P_SetMessage(player, TXT_ARMOR3, false);
			break;
		case SPR_ARM4:
			if(!P_GiveArmor(player, ARMOR_AMULET, -1))
			{
				return;
			}
			P_SetMessage(player, TXT_ARMOR4, false);
			break;

		// Keys
		case SPR_KEY1:
		case SPR_KEY2:
		case SPR_KEY3:
		case SPR_KEY4:
		case SPR_KEY5:
		case SPR_KEY6:
		case SPR_KEY7:
		case SPR_KEY8:
		case SPR_KEY9:
		case SPR_KEYA:
		case SPR_KEYB:
			if(!P_GiveKey(player, special->sprite-SPR_KEY1))
			{
				return;
			}
			P_SetMessage(player, TextKeyMessages[special->sprite-SPR_KEY1],
				true);
			sound = SFX_PICKUP_KEY;

			// Check and process the special now in case the key doesn't
			// get removed for coop netplay
			if(special->special)
			{
				P_ExecuteLineSpecial(special->special, special->args,
					NULL, 0, toucher);
				special->special = 0;
			}

			if(!netgame)
			{ // Only remove keys in single player game
				break;
			}
			player->bonuscount += BONUSADD;
			if(player == &players[consoleplayer])
			{
				S_StartSound(NULL, sound);
				SB_PaletteFlash(false);
			}
			return;

		// Artifacts
		case SPR_PTN2:
			TryPickupArtifact(player, arti_health, special);
			return;
		case SPR_SOAR:
			TryPickupArtifact(player, arti_fly, special);
			return;
		case SPR_INVU:
			TryPickupArtifact(player, arti_invulnerability, special);
			return;
		case SPR_SUMN:
			TryPickupArtifact(player, arti_summon, special);
			return;
		case SPR_PORK:
			TryPickupArtifact(player, arti_egg, special);
			return;
		case SPR_SPHL:
			TryPickupArtifact(player, arti_superhealth, special);
			return;
		case SPR_HRAD:
			TryPickupArtifact(player, arti_healingradius, special);
			return;
		case SPR_TRCH:
			TryPickupArtifact(player, arti_torch, special);
			return;
		case SPR_ATLP:
			TryPickupArtifact(player, arti_teleport, special);
			return;
		case SPR_TELO:
			TryPickupArtifact(player, arti_teleportother, special);
			return;
		case SPR_PSBG:
			TryPickupArtifact(player, arti_poisonbag, special);
			return;
		case SPR_SPED:
			TryPickupArtifact(player, arti_speed, special);
			return;
		case SPR_BMAN:
			TryPickupArtifact(player, arti_boostmana, special);
			return;
		case SPR_BRAC:
			TryPickupArtifact(player, arti_boostarmor, special);
			return;
		case SPR_BLST:
			TryPickupArtifact(player, arti_blastradius, special);
			return;

		// Puzzle artifacts
		case SPR_ASKU:
			TryPickupArtifact(player, arti_puzzskull, special);
			return;
		case SPR_ABGM:
			TryPickupArtifact(player, arti_puzzgembig, special);
			return;
		case SPR_AGMR:
			TryPickupArtifact(player, arti_puzzgemred, special);
			return;
		case SPR_AGMG:
			TryPickupArtifact(player, arti_puzzgemgreen1, special);
			return;
		case SPR_AGG2:
			TryPickupArtifact(player, arti_puzzgemgreen2, special);
			return;
		case SPR_AGMB:
			TryPickupArtifact(player, arti_puzzgemblue1, special);
			return;
		case SPR_AGB2:
			TryPickupArtifact(player, arti_puzzgemblue2, special);
			return;
		case SPR_ABK1:
			TryPickupArtifact(player, arti_puzzbook1, special);
			return;
		case SPR_ABK2:
			TryPickupArtifact(player, arti_puzzbook2, special);
			return;
		case SPR_ASK2:
			TryPickupArtifact(player, arti_puzzskull2, special);
			return;
		case SPR_AFWP:
			TryPickupArtifact(player, arti_puzzfweapon, special);
			return;
		case SPR_ACWP:
			TryPickupArtifact(player, arti_puzzcweapon, special);
			return;
		case SPR_AMWP:
			TryPickupArtifact(player, arti_puzzmweapon, special);
			return;
		case SPR_AGER:
			TryPickupArtifact(player, arti_puzzgear1, special);
			return;
		case SPR_AGR2:
			TryPickupArtifact(player, arti_puzzgear2, special);
			return;
		case SPR_AGR3:
			TryPickupArtifact(player, arti_puzzgear3, special);
			return;
		case SPR_AGR4:
			TryPickupArtifact(player, arti_puzzgear4, special);
			return;

		// Mana
		case SPR_MAN1:
			if(!P_GiveMana(player, MANA_1, 15))
			{
				return;
			}
			P_SetMessage(player, TXT_MANA_1, false);
			break;
		case SPR_MAN2: 
			if(!P_GiveMana(player, MANA_2, 15))
			{
				return;
			}
			P_SetMessage(player, TXT_MANA_2, false);
			break;
		case SPR_MAN3: // Double Mana Dodecahedron
			if(!P_GiveMana(player, MANA_1, 20))
			{
				if(!P_GiveMana(player, MANA_2, 20))
				{
					return;
				}
			}
			else
			{
				P_GiveMana(player, MANA_2, 20);
			}
			P_SetMessage(player, TXT_MANA_BOTH, false);
			break;

		// 2nd and 3rd Mage Weapons
		case SPR_WMCS: // Frost Shards
			TryPickupWeapon(player, PCLASS_MAGE, WP_SECOND,
				special, TXT_WEAPON_M2);
			return;
		case SPR_WMLG: // Arc of Death
			TryPickupWeapon(player, PCLASS_MAGE, WP_THIRD,
				special, TXT_WEAPON_M3);
			return;

		// 2nd and 3rd Fighter Weapons
		case SPR_WFAX: // Timon's Axe
			TryPickupWeapon(player, PCLASS_FIGHTER, WP_SECOND,
				special, TXT_WEAPON_F2);
			return;
		case SPR_WFHM: // Hammer of Retribution
			TryPickupWeapon(player, PCLASS_FIGHTER, WP_THIRD,
				special, TXT_WEAPON_F3);
			return;

		// 2nd and 3rd Cleric Weapons
		case SPR_WCSS: // Serpent Staff
			TryPickupWeapon(player, PCLASS_CLERIC, WP_SECOND,
				special, TXT_WEAPON_C2);
			return;
		case SPR_WCFM: // Firestorm
			TryPickupWeapon(player, PCLASS_CLERIC, WP_THIRD,
				special, TXT_WEAPON_C3);
			return;

		// Fourth Weapon Pieces
		case SPR_WFR1:
			TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE1,
				special);
			return;
		case SPR_WFR2:
			TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE2,
				special);
			return;
		case SPR_WFR3:
			TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE3,
				special);
			return;
		case SPR_WCH1:
			TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE1,
				special);
			return;
		case SPR_WCH2:
			TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE2,
				special);
			return;
		case SPR_WCH3:
			TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE3,
				special);
			return;
		case SPR_WMS1:
			TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE1,
				special);
			return;
		case SPR_WMS2:
			TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE2,
				special);
			return;
		case SPR_WMS3:
			TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE3,
				special);
			return;

		default:
			I_Error("P_SpecialThing: Unknown gettable thing");
	}
	if(special->special)
	{
		P_ExecuteLineSpecial(special->special, special->args, NULL,
			0, toucher);
		special->special = 0;
	}
	if(deathmatch && respawn && !(special->flags2&MF2_DROPPED))
	{
		P_HideSpecialThing(special);
	}
	else
	{
		P_RemoveMobj(special);
	}
	player->bonuscount += BONUSADD;
	if(player == &players[consoleplayer])
	{
		S_StartSound(NULL, sound);
		SB_PaletteFlash(false);
	}
}

// Search thinker list for minotaur
mobj_t *ActiveMinotaur(player_t *master)
{
	mobj_t *mo;
	player_t *plr;
	thinker_t *think;
	unsigned int *starttime;

	for(think = thinkercap.next; think != &thinkercap; think = think->next)
	{
		if(think->function != P_MobjThinker) continue;
		mo = (mobj_t *)think;
		if(mo->type != MT_MINOTAUR) continue;
		if(mo->health <= 0) continue;
		if(!(mo->flags&MF_COUNTKILL)) continue;		// for morphed minotaurs
		if(mo->flags&MF_CORPSE) continue;
		starttime = (unsigned int *)mo->args;
		if ((leveltime - *starttime) >= MAULATORTICS) continue;
		plr = ((mobj_t *)mo->special1)->player;
		if(plr == master) return(mo);
	}
	return(NULL);
}


//---------------------------------------------------------------------------
//
// PROC P_KillMobj
//
//---------------------------------------------------------------------------

void P_KillMobj(mobj_t *source, mobj_t *target)
{
	int dummy;
	mobj_t *master;

	target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_NOGRAVITY);
	target->flags |= MF_CORPSE|MF_DROPOFF;
	target->flags2 &= ~MF2_PASSMOBJ;
	target->height >>= 2;
	if((target->flags&MF_COUNTKILL || target->type == MT_ZBELL) 
		 && target->special)
	{ // Initiate monster death actions
		if(target->type == MT_SORCBOSS)
		{
			dummy = 0;
			P_StartACS(target->special, 0, (byte *)&dummy, target,
				NULL, 0);
		}
		else
		{
			P_ExecuteLineSpecial(target->special, target->args,
				NULL, 0, target);
		}
	}
	if(source && source->player)
	{ // Check for frag changes
		if(target->player)
		{
			if(target == source)
			{ // Self-frag
				target->player->frags[target->player-players]--;
				if(cmdfrag && netgame
				&& source->player == &players[consoleplayer])
				{ // Send out a frag count packet
					NET_SendFrags(source->player);
				}
			}
			else
			{
				source->player->frags[target->player-players]++;
				if(cmdfrag && netgame
				&& source->player == &players[consoleplayer])
				{ // Send out a frag count packet
					NET_SendFrags(source->player);
				}
			}
		}
	}
	if(target->player)
	{ // Player death
		if(!source)
		{ // Self-frag
			target->player->frags[target->player-players]--;
			if(cmdfrag && netgame
			&& target->player == &players[consoleplayer])
			{ // Send out a frag count packet
				NET_SendFrags(target->player);
			}
		}
		target->flags &= ~MF_SOLID;
		target->flags2 &= ~MF2_FLY;
		target->player->powers[pw_flight] = 0;
		target->player->playerstate = PST_DEAD;
		P_DropWeapon(target->player);
		if(target->flags2&MF2_FIREDAMAGE)
		{ // Player flame death
			switch(target->player->class)
			{
				case PCLASS_FIGHTER:
					S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_F_FDTH1);
					return;
				case PCLASS_CLERIC:
					S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_C_FDTH1);
					return;
				case PCLASS_MAGE:
					S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_M_FDTH1);
					return;
				default:
					break;
			}
		}
		if(target->flags2&MF2_ICEDAMAGE)
		{ // Player ice death
			target->flags &= ~(7<<MF_TRANSSHIFT); //no translation
			target->flags |= MF_ICECORPSE;
			switch(target->player->class)
			{
				case PCLASS_FIGHTER:
					P_SetMobjState(target, S_FPLAY_ICE);
					return;
				case PCLASS_CLERIC:
					P_SetMobjState(target, S_CPLAY_ICE);
					return;
				case PCLASS_MAGE:
					P_SetMobjState(target, S_MPLAY_ICE);
					return;
				case PCLASS_PIG:
					P_SetMobjState(target, S_PIG_ICE);
					return;
				default:
					break;
			}
		}
	}
	if(target->flags2&MF2_FIREDAMAGE)
	{
		if(target->type == MT_FIGHTER_BOSS 
			|| target->type == MT_CLERIC_BOSS
			|| target->type == MT_MAGE_BOSS)
		{
			switch(target->type)
			{
				case MT_FIGHTER_BOSS:
					S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_F_FDTH1);
					return;
				case MT_CLERIC_BOSS:
					S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_C_FDTH1);
					return;
				case MT_MAGE_BOSS:
					S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH);
					P_SetMobjState(target, S_PLAY_M_FDTH1);
					return;
				default:
					break;
			}
		}
		else if(target->type == MT_TREEDESTRUCTIBLE)
		{
			P_SetMobjState(target, S_ZTREEDES_X1);
			target->height = 24*FRACUNIT;
			S_StartSound(target, SFX_TREE_EXPLODE);
			return;
		}
	}
	if(target->flags2&MF2_ICEDAMAGE)
	{
		target->flags |= MF_ICECORPSE;
		switch(target->type)
		{
			case MT_BISHOP:
				P_SetMobjState(target, S_BISHOP_ICE);
				return;		
			case MT_CENTAUR:
			case MT_CENTAURLEADER:
				P_SetMobjState(target, S_CENTAUR_ICE);
				return;		
			case MT_DEMON:
			case MT_DEMON2:
				P_SetMobjState(target, S_DEMON_ICE);
				return;		
			case MT_SERPENT:
			case MT_SERPENTLEADER:
				P_SetMobjState(target, S_SERPENT_ICE);
				return;		
			case MT_WRAITH:
			case MT_WRAITHB:
				P_SetMobjState(target, S_WRAITH_ICE);
				return;
			case MT_ETTIN:
				P_SetMobjState(target, S_ETTIN_ICE1);
				return;
			case MT_FIREDEMON:
				P_SetMobjState(target, S_FIRED_ICE1);
				return;
			case MT_FIGHTER_BOSS:
				P_SetMobjState(target, S_FIGHTER_ICE);
				return;
			case MT_CLERIC_BOSS:
				P_SetMobjState(target, S_CLERIC_ICE);
				return;
			case MT_MAGE_BOSS:
				P_SetMobjState(target, S_MAGE_ICE);
				return;
			case MT_PIG:
				P_SetMobjState(target, S_PIG_ICE);
				return;
			default:
				target->flags &= ~MF_ICECORPSE;
				break;
		}
	}

	if(target->type == MT_MINOTAUR)
	{
		master = (mobj_t *)target->special1;
		if(master->health > 0)
		{
			if (!ActiveMinotaur(master->player))
			{
				master->player->powers[pw_minotaur] = 0;
			}
		}		
	}
	else if(target->type == MT_TREEDESTRUCTIBLE)
	{
		target->height = 24*FRACUNIT;
	}
	if(target->health < -(target->info->spawnhealth>>1)
		&& target->info->xdeathstate)
	{ // Extreme death
		P_SetMobjState(target, target->info->xdeathstate);
	}
	else
	{ // Normal death
		if ((target->type==MT_FIREDEMON) &&
			(target->z <= target->floorz + 2*FRACUNIT) &&
			(target->info->xdeathstate))
		{
			// This is to fix the imps' staying in fall state
			P_SetMobjState(target, target->info->xdeathstate);
		}
		else
		{
			P_SetMobjState(target, target->info->deathstate);
		}
	}
	target->tics -= P_Random()&3;
//	I_StartSound(&actor->r, actor->info->deathsound);
}

//---------------------------------------------------------------------------
//
// FUNC P_MinotaurSlam
//
//---------------------------------------------------------------------------

void P_MinotaurSlam(mobj_t *source, mobj_t *target)
{
	angle_t angle;
	fixed_t thrust;

	angle = R_PointToAngle2(source->x, source->y, target->x, target->y);
	angle >>= ANGLETOFINESHIFT;
	thrust = 16*FRACUNIT+(P_Random()<<10);
	target->momx += FixedMul(thrust, finecosine[angle]);
	target->momy += FixedMul(thrust, finesine[angle]);
	P_DamageMobj(target, NULL, source, HITDICE(4));
	if(target->player)
	{
		target->reactiontime = 14+(P_Random()&7);
	}
	source->args[0] = 0;			// Stop charging
}


//---------------------------------------------------------------------------
//
// FUNC P_MorphPlayer
//
// Returns true if the player gets turned into a pig
//
//---------------------------------------------------------------------------

boolean P_MorphPlayer(player_t *player)
{
	mobj_t *pmo;
	mobj_t *fog;
	mobj_t *beastMo;
	fixed_t x;
	fixed_t y;
	fixed_t z;
	angle_t angle;
	int oldFlags2;

	if(player->powers[pw_invulnerability])
	{ // Immune when invulnerable
		return(false);
	}
	if(player->morphTics)
	{ // Player is already a beast
		return false;
	}
	pmo = player->mo;
	x = pmo->x;
	y = pmo->y;
	z = pmo->z;
	angle = pmo->angle;
	oldFlags2 = pmo->flags2;
	P_SetMobjState(pmo, S_FREETARGMOBJ);
	fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
	S_StartSound(fog, SFX_TELEPORT);
	beastMo = P_SpawnMobj(x, y, z, MT_PIGPLAYER);
	beastMo->special1 = player->readyweapon;
	beastMo->angle = angle;
	beastMo->player = player;
	player->health = beastMo->health = MAXMORPHHEALTH;
	player->mo = beastMo;
	memset(&player->armorpoints[0], 0, NUMARMOR*sizeof(int));
	player->class = PCLASS_PIG;
	if(oldFlags2&MF2_FLY)
	{
		beastMo->flags2 |= MF2_FLY;
	}
	player->morphTics = MORPHTICS;
	P_ActivateMorphWeapon(player);
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_MorphMonster
//
//---------------------------------------------------------------------------

boolean P_MorphMonster(mobj_t *actor)
{
	mobj_t *master, *monster, *fog;
	mobjtype_t moType;
	fixed_t x;
	fixed_t y;
	fixed_t z;
	mobj_t oldMonster;

	if(actor->player) return(false);
	if(!(actor->flags&MF_COUNTKILL)) return false;
	if(actor->flags2&MF2_BOSS) return false;
	moType = actor->type;
	switch(moType)
	{
		case MT_PIG:
			return(false);
		case MT_FIGHTER_BOSS:
		case MT_CLERIC_BOSS:
		case MT_MAGE_BOSS:
			return(false);
		default:
			break;
	}

	oldMonster = *actor;
	x = oldMonster.x;
	y = oldMonster.y;
	z = oldMonster.z;
	P_RemoveMobjFromTIDList(actor);
	P_SetMobjState(actor, S_FREETARGMOBJ);
	fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
	S_StartSound(fog, SFX_TELEPORT);
	monster = P_SpawnMobj(x, y, z, MT_PIG);
	monster->special2 = moType;
	monster->special1 = MORPHTICS+P_Random();
	monster->flags |= (oldMonster.flags&MF_SHADOW);
	monster->target = oldMonster.target;
	monster->angle = oldMonster.angle;
	monster->tid = oldMonster.tid;
	monster->special = oldMonster.special;
	P_InsertMobjIntoTIDList(monster, oldMonster.tid);
	memcpy(monster->args, oldMonster.args, 5);

	// check for turning off minotaur power for active icon
	if (moType==MT_MINOTAUR)
	{
		master = (mobj_t *)oldMonster.special1;
		if(master->health > 0)
		{
			if (!ActiveMinotaur(master->player))
			{
				master->player->powers[pw_minotaur] = 0;
			}
		}		
	}
	return(true);
}

//---------------------------------------------------------------------------
//
// PROC P_AutoUseHealth
//
//---------------------------------------------------------------------------

void P_AutoUseHealth(player_t *player, int saveHealth)
{
	int i;
	int count;
	int normalCount;
	int normalSlot=0;
	int superCount;
	int superSlot=0;

	normalCount = superCount = 0;
	for(i = 0; i < player->inventorySlotNum; i++)
	{
		if(player->inventory[i].type == arti_health)
		{
			normalSlot = i;
			normalCount = player->inventory[i].count;
		}
		else if(player->inventory[i].type == arti_superhealth)
		{
			superSlot = i;
			superCount = player->inventory[i].count;
		}
	}
	if((gameskill == sk_baby) && (normalCount*25 >= saveHealth))
	{ // Use quartz flasks
		count = (saveHealth+24)/25;
		for(i = 0; i < count; i++)
		{
			player->health += 25;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
	}
	else if(superCount*100 >= saveHealth)
	{ // Use mystic urns
		count = (saveHealth+99)/100;
		for(i = 0; i < count; i++)
		{
			player->health += 100;
			P_PlayerRemoveArtifact(player, superSlot);
		}
	}
	else if((gameskill == sk_baby)
		&& (superCount*100+normalCount*25 >= saveHealth))
	{ // Use mystic urns and quartz flasks
		count = (saveHealth+24)/25;
		saveHealth -= count*25;
		for(i = 0; i < count; i++)
		{
			player->health += 25;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
		count = (saveHealth+99)/100;
		for(i = 0; i < count; i++)
		{
			player->health += 100;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
	}
	player->mo->health = player->health;
}

/*
=================
=
= P_DamageMobj
=
= Damages both enemies and players
= inflictor is the thing that caused the damage
= 		creature or missile, can be NULL (slime, etc)
= source is the thing to target after taking damage
=		creature or NULL
= Source and inflictor are the same for melee attacks
= source can be null for barrel explosions and other environmental stuff
==================
*/

void P_DamageMobj
(
	mobj_t *target,
	mobj_t *inflictor,
	mobj_t *source,
	int	damage
)
{
	unsigned ang;
	int saved;
	fixed_t savedPercent;
	player_t *player;
	mobj_t *master;
	fixed_t thrust;
	int temp;
	int i;

	if(!(target->flags&MF_SHOOTABLE))
	{
		// Shouldn't happen
		return;
	}
	if(target->health <= 0)
	{
		if (inflictor && inflictor->flags2&MF2_ICEDAMAGE)
		{
			return;
		}
		else if (target->flags&MF_ICECORPSE) // frozen
		{
			target->tics = 1;
			target->momx = target->momy = 0;
		}
		return;
	}
	if ((target->flags2&MF2_INVULNERABLE) && damage < 10000)
	{ // mobj is invulnerable
		if(target->player) return;	// for player, no exceptions
		if(inflictor)
		{
			switch(inflictor->type)
			{
				// These inflictors aren't foiled by invulnerability
				case MT_HOLY_FX:
				case MT_POISONCLOUD:
				case MT_FIREBOMB:
					break;
				default:
					return;
			}
		}
		else
		{
			return;
		}
	}
	if(target->player)
	{
		if(damage < 1000 && ((target->player->cheats&CF_GODMODE)
			|| target->player->powers[pw_invulnerability]))
		{
			return;
		}
	}
	if(target->flags&MF_SKULLFLY)
	{
		target->momx = target->momy = target->momz = 0;
	}
	if(target->flags2&MF2_DORMANT)
	{
		// Invulnerable, and won't wake up
		return;
	}
	player = target->player;
	if(player && gameskill == sk_baby)
	{
		// Take half damage in trainer mode
		damage >>= 1;
	}
	// Special damage types
	if(inflictor)
	{
		switch(inflictor->type)
		{
			case MT_EGGFX:
				if(player)
				{
					P_MorphPlayer(player);
				}
				else
				{
					P_MorphMonster(target);
				}
				return; // Always return
			case MT_TELOTHER_FX1:
			case MT_TELOTHER_FX2:
			case MT_TELOTHER_FX3:
			case MT_TELOTHER_FX4:
			case MT_TELOTHER_FX5:
				if ((target->flags&MF_COUNTKILL) &&
					(target->type != MT_SERPENT) &&
					(target->type != MT_SERPENTLEADER) &&
					(!(target->flags2 & MF2_BOSS)))
				{
					P_TeleportOther(target);
				}
				return;
			case MT_MINOTAUR:
				if(inflictor->flags&MF_SKULLFLY)
				{ // Slam only when in charge mode
					P_MinotaurSlam(inflictor, target);
					return;
				}
				break;
			case MT_BISH_FX:
				// Bishops are just too nasty
				damage >>= 1;
				break;
			case MT_SHARDFX1:
				switch(inflictor->special2)
				{
					case 3:
						damage <<= 3;
						break;
					case 2:
						damage <<= 2;
						break;
					case 1:
						damage <<= 1;
						break;
					default:
						break;
				}
				break;
			case MT_CSTAFF_MISSILE:
				// Cleric Serpent Staff does poison damage
				if(target->player)
				{
					P_PoisonPlayer(target->player, source, 20);
					damage >>= 1;
				}
				break;
			case MT_ICEGUY_FX2:
				damage >>= 1;
				break;
			case MT_POISONDART:
				if(target->player)
				{
					P_PoisonPlayer(target->player, source, 20);
					damage >>= 1;
				}
				break;
			case MT_POISONCLOUD:
				if(target->player)
				{
					if(target->player->poisoncount < 4)
					{
						P_PoisonDamage(target->player, source,
							15+(P_Random()&15), false); // Don't play painsound
						P_PoisonPlayer(target->player, source, 50);
						S_StartSound(target, SFX_PLAYER_POISONCOUGH);
					}	
					return;
				}
				else if(!(target->flags&MF_COUNTKILL))
				{ // only damage monsters/players with the poison cloud
					return;
				}
				break;
			case MT_FSWORD_MISSILE:
				if(target->player)
				{
					damage -= damage>>2;
				}
				break;
			default:
				break;
		}
	}
	// Push the target unless source is using the gauntlets
	if(inflictor && (!source || !source->player)
		&& !(inflictor->flags2&MF2_NODMGTHRUST))
	{
		ang = R_PointToAngle2(inflictor->x, inflictor->y,
			target->x, target->y);
		//thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
		thrust = damage*(FRACUNIT>>3)*150/target->info->mass;
		// make fall forwards sometimes
		if((damage < 40) && (damage > target->health)
			&& (target->z-inflictor->z > 64*FRACUNIT) && (P_Random()&1))
		{
			ang += ANG180;
			thrust *= 4;
		}
		ang >>= ANGLETOFINESHIFT;
		target->momx += FixedMul(thrust, finecosine[ang]);
		target->momy += FixedMul(thrust, finesine[ang]);
	}

	//
	// player specific
	//
	if(player)
	{
		savedPercent = AutoArmorSave[player->class]
			+player->armorpoints[ARMOR_ARMOR]+player->armorpoints[ARMOR_SHIELD]
			+player->armorpoints[ARMOR_HELMET]
			+player->armorpoints[ARMOR_AMULET];
		if(savedPercent)
		{ // armor absorbed some damage
			if(savedPercent > 100*FRACUNIT)
			{
				savedPercent = 100*FRACUNIT;
			}
			for(i = 0; i < NUMARMOR; i++)
			{
				if(player->armorpoints[i])
				{
					player->armorpoints[i] -= 
						FixedDiv(FixedMul(damage<<FRACBITS,
						ArmorIncrement[player->class][i]), 300*FRACUNIT);
					if(player->armorpoints[i] < 2*FRACUNIT)
					{
						player->armorpoints[i] = 0;
					}
				}
			}
			saved = FixedDiv(FixedMul(damage<<FRACBITS, savedPercent),
				100*FRACUNIT);
			if(saved > savedPercent*2)
			{	
				saved = savedPercent*2;
			}
			damage -= saved>>FRACBITS;
		}
		if(damage >= player->health
			&& ((gameskill == sk_baby) || deathmatch)
			&& !player->morphTics)
		{ // Try to use some inventory health
			P_AutoUseHealth(player, damage-player->health+1);
		}
		player->health -= damage; // mirror mobj health here for Dave
		if(player->health < 0)
		{
			player->health = 0;
		}
		player->attacker = source;
		player->damagecount += damage; // add damage after armor / invuln
		if(player->damagecount > 100)
		{
			player->damagecount = 100; // teleport stomp does 10k points...
		}
		temp = damage < 100 ? damage : 100;
		if(player == &players[consoleplayer])
		{
			//rww begin
			//I_Tactile(40, 10, 40+temp*2);
			//rww end
			SB_PaletteFlash(false);
		}
	}

	//
	// do the damage
	//
	target->health -= damage;
	if(target->health <= 0)
	{ // Death
		if(inflictor)
		{ // check for special fire damage or ice damage deaths
			if(inflictor->flags2&MF2_FIREDAMAGE)
			{
				if(player && !player->morphTics)
				{ // Check for flame death
					if(target->health > -50 && damage > 25)
					{
						target->flags2 |= MF2_FIREDAMAGE;
					}
				}
				else
				{
					target->flags2 |= MF2_FIREDAMAGE;
				}
			}
			else if(inflictor->flags2&MF2_ICEDAMAGE)
			{
				target->flags2 |= MF2_ICEDAMAGE;
			}
		}
		if(source && (source->type == MT_MINOTAUR))
		{ // Minotaur's kills go to his master
			master = (mobj_t *)(source->special1);
			// Make sure still alive and not a pointer to fighter head
			if (master->player && (master->player->mo == master))
			{
				source = master;
			}
		}
		if(source && (source->player) &&
			(source->player->readyweapon == WP_FOURTH))
		{
			// Always extreme death from fourth weapon
			target->health = -5000;
		}
		P_KillMobj(source, target);
		return;
	}
	if((P_Random() < target->info->painchance)
		&& !(target->flags&MF_SKULLFLY))
	{
		if(inflictor && (inflictor->type >= MT_LIGHTNING_FLOOR
			&& inflictor->type <= MT_LIGHTNING_ZAP))
		{
			if(P_Random() < 96)
			{
				target->flags |= MF_JUSTHIT; // fight back!
				P_SetMobjState(target, target->info->painstate);
			}
			else
			{ // "electrocute" the target
				target->frame |= FF_FULLBRIGHT;
				if(target->flags&MF_COUNTKILL && P_Random() < 128
				&& !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT))
				{
					if ((target->type == MT_CENTAUR) ||
						(target->type == MT_CENTAURLEADER) ||
						(target->type == MT_ETTIN))
					{
						S_StartSound(target, SFX_PUPPYBEAT);
					}
				}
			}
		}
		else
		{
			target->flags |= MF_JUSTHIT; // fight back!
			P_SetMobjState(target, target->info->painstate);	
			if(inflictor && inflictor->type == MT_POISONCLOUD)
			{
				if(target->flags&MF_COUNTKILL && P_Random() < 128
				&& !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT))
				{
					if ((target->type == MT_CENTAUR) ||
						(target->type == MT_CENTAURLEADER) ||
						(target->type == MT_ETTIN))
					{
						S_StartSound(target, SFX_PUPPYBEAT);
					}
				}
			}
		}
	}
	target->reactiontime = 0; // we're awake now...
	if(!target->threshold && source && !(source->flags2&MF2_BOSS)
		&& !(target->type == MT_BISHOP) && !(target->type == MT_MINOTAUR))
	{
		// Target actor is not intent on another actor,
		// so make him chase after source
		if((target->type == MT_CENTAUR && source->type == MT_CENTAURLEADER)
			|| (target->type == MT_CENTAURLEADER 
				&& source->type == MT_CENTAUR))
		{
			return;
		}
		target->target = source;
		target->threshold = BASETHRESHOLD;
		if(target->state == &states[target->info->spawnstate]
			&& target->info->seestate != S_NULL)
		{
			P_SetMobjState(target, target->info->seestate);
		}
	}
}

//==========================================================================
//
// P_FallingDamage
//
//==========================================================================

void P_FallingDamage(player_t *player)
{
	int damage;
	int mom;
	int dist;

	mom = abs(player->mo->momz);	
	dist = FixedMul(mom, 16*FRACUNIT/23);

	if(mom >= 63*FRACUNIT)
	{ // automatic death
		P_DamageMobj(player->mo, NULL, NULL, 10000);
		return;
	}
	damage = ((FixedMul(dist, dist)/10)>>FRACBITS)-24;
	if(player->mo->momz > -39*FRACUNIT && damage > player->mo->health
		&& player->mo->health != 1)
	{ // No-death threshold
		damage = player->mo->health-1;
	}
	S_StartSound(player->mo, SFX_PLAYER_LAND);
	P_DamageMobj(player->mo, NULL, NULL, damage);
}

//==========================================================================
//
// P_PoisonPlayer - Sets up all data concerning poisoning
//
//==========================================================================

void P_PoisonPlayer(player_t *player, mobj_t *poisoner, int poison)
{
	if((player->cheats&CF_GODMODE) || player->powers[pw_invulnerability])
	{
		return;
	}
	player->poisoncount += poison;
	player->poisoner = poisoner;
	if(player->poisoncount > 100)
	{
		player->poisoncount = 100;
	}
}

//==========================================================================
//
// P_PoisonDamage - Similar to P_DamageMobj
//
//==========================================================================

void P_PoisonDamage(player_t *player, mobj_t *source, int damage,
	boolean playPainSound)
{
	mobj_t *target;
	mobj_t *inflictor;

	target = player->mo;
	inflictor = source;
	if(target->health <= 0)
	{
		return;
	}
	if(target->flags2&MF2_INVULNERABLE && damage < 10000)
	{ // mobj is invulnerable
		return;
	}
	if(player && gameskill == sk_baby)
	{
		// Take half damage in trainer mode
		damage >>= 1;
	}
	if(damage < 1000 && ((player->cheats&CF_GODMODE)
		|| player->powers[pw_invulnerability]))
	{
		return;
	}
	if(damage >= player->health
		&& ((gameskill == sk_baby) || deathmatch)
		&& !player->morphTics)
	{ // Try to use some inventory health
		P_AutoUseHealth(player, damage-player->health+1);
	}
	player->health -= damage; // mirror mobj health here for Dave
	if(player->health < 0)
	{
		player->health = 0;
	}
	player->attacker = source;

	//
	// do the damage
	//
	target->health -= damage;
	if(target->health <= 0)
	{ // Death
		target->special1 = damage;
		if(player && inflictor && !player->morphTics)
		{ // Check for flame death
			if((inflictor->flags2&MF2_FIREDAMAGE)
				&& (target->health > -50) && (damage > 25))
			{
				target->flags2 |= MF2_FIREDAMAGE;
			}
			if(inflictor->flags2&MF2_ICEDAMAGE)
			{
				target->flags2 |= MF2_ICEDAMAGE;
			}
		}
		P_KillMobj(source, target);
		return;
	}
	if(!(leveltime&63) && playPainSound)
	{
		P_SetMobjState(target, target->info->painstate);
	}
/*
	if((P_Random() < target->info->painchance)
		&& !(target->flags&MF_SKULLFLY))
	{
		target->flags |= MF_JUSTHIT; // fight back!
		P_SetMobjState(target, target->info->painstate);
	}
*/
}
