// Emacs style mode select	 -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
//		Handling interactions (i.e., collisions).
//
//-----------------------------------------------------------------------------




// Data.
#include "doomdef.h"
#include "dstrings.h"

#include "doomstat.h"

// [BC] Additions
#include "m_random.h"
#include "i_system.h"
#include "i_music.h"
#include "am_map.h"
#include "b_bot.h"
#include "b_info.h"
#include "c_console.h"
#include "c_dispatch.h"
#include "p_local.h"
#include "s_sound.h"
#include "p_acs.h"
#include "p_inter.h"
#include "p_lnspec.h"
#include "r_defs.h"
#include "doomdef.h"
#include "doomtype.h"
#include "info.h"
#include "v_text.h"
#include "sv_main.h"
#include "m_cheat.h"

#define BONUSADD		6

// a weapon is found with two clip loads,
// a big item has five clip loads
int 	maxammo[NUMAMMO] = {200, 50, 300, 50};
int 	clipammo[NUMAMMO] = {10, 4, 20, 1};


void PickupMessage (AActor *toucher, const char *message)
{
	// Some maps have multiple items stacked on top of each other.
	// It looks odd to display pickup messages for all of them.
	static int lastmessagetic;
	static const char *lastmessage = NULL;

	if (toucher == players[consoleplayer].camera
		&& (lastmessagetic != gametic || lastmessage != message))
	{
		lastmessagetic = gametic;
		lastmessage = message;
		Printf (PRINT_LOW, "%s\n", message);
	}
}

// Returns mobjtype_t of the
// current weapon the player is carrying.
mobjtype_t P_WeaponDrop (player_s *p)
{
	return weaponinfo[p->readyweapon].droptype;
}

//
// GET STUFF
//

//
// P_GiveAmmo
// Num is the number of clip loads,
// not the individual count (0= 1/2 clip).
// Returns false if the ammo can't be picked up at all
//

BOOL P_GiveAmmo (player_t *player, ammotype_t ammo, int num)
{
	int oldammo;
	int playernum;

	if (netstate == NS_CLIENT)
	{
		if (ammo < 0 || ammo > NUMAMMO)
			I_Error ("P_GiveAmmo: bad type %i", ammo);
					
/*		oldammo = player->ammo[ammo];
		player->ammo[ammo] += num;	

		if (player->ammo[ammo] > player->maxammo[ammo])
			player->ammo[ammo] = player->maxammo[ammo];
*/		
/*		oldammo = player->ammo[ammo];
		oldammo += num;	

		if (oldammo > player->maxammo[ammo])
			oldammo = player->maxammo[ammo];
*/
		return true;
	}
		
	if (ammo == am_noammo)
		return false;
				
	if (ammo < 0 || ammo > NUMAMMO)
		I_Error ("P_GiveAmmo: bad type %i", ammo);
				
	if ( player->ammo[ammo] == player->maxammo[ammo]  )
		return false;
				
	if (num)
		num *= clipammo[ammo];
	else
		num = clipammo[ammo]/2;
	
	if (gameskill.value == sk_baby || gameskill.value == sk_nightmare
		|| dmflags2 & DF2_YES_DOUBLEAMMO)	// [BC]
	{
		// give double ammo in trainer mode,
		// you'll need in nightmare
		num <<= 1;
	}
	
				
	oldammo = player->ammo[ammo];
	player->ammo[ammo] += num;

	if (player->ammo[ammo] > player->maxammo[ammo])
		player->ammo[ammo] = player->maxammo[ammo];

	if (netstate == NS_SERVER)
	{
		for (playernum=0; playernum<MAXPLAYERS; playernum++)
			if (player == &players[playernum])
				break;
	
		client_t *cl = &clients[playernum];

		MSG_WriteByte (&cl->reliablebuf, svc_giveammo);
		MSG_WriteByte (&cl->reliablebuf, (byte)ammo);
		MSG_WriteShort (&cl->reliablebuf, num);
	}

	// If non zero ammo, 
	// don't change up weapons,
	// player was lower on purpose.
	if (oldammo)
		return true;	

	// We were down to zero,
	// so select a new weapon.
	// Preferences are not user selectable.
	switch (ammo)
	{
	  case am_clip:
		if (player->readyweapon == wp_fist)
		{
			if (player->weaponowned[wp_chaingun])
				player->pendingweapon = wp_chaingun;
			else
				player->pendingweapon = wp_pistol;
		}
		break;
		
	  case am_shell:
		if (player->readyweapon == wp_fist
			|| player->readyweapon == wp_pistol)
		{
			if (player->weaponowned[wp_supershotgun])	// [BC] Something I feel should've been done upon Doom II's creation
				player->pendingweapon = wp_supershotgun;
			else if (player->weaponowned[wp_shotgun])
				player->pendingweapon = wp_shotgun;
		}
		break;
		
	  case am_cell:
		if (player->readyweapon == wp_fist
			|| player->readyweapon == wp_pistol)
		{
			if (player->weaponowned[wp_plasma])
				player->pendingweapon = wp_plasma;
			else if (player->weaponowned[wp_railgun]) // [BC]
				player->pendingweapon = wp_railgun;
		}
		break;
		
	  case am_misl:
		if (player->readyweapon == wp_fist)
		{
			if (player->weaponowned[wp_grenlauncher]) // [BC] Grenade launcher is safer, so it goes ahead
				player->pendingweapon = wp_grenlauncher;
			else if (player->weaponowned[wp_missile])
				player->pendingweapon = wp_missile;
		}
	  default:
		break;
	}

	if (netstate == NS_SERVER)
	{
		MSG_WriteByte (&clients[playernum].reliablebuf, svc_changeweapon);
		MSG_WriteByte (&clients[playernum].reliablebuf, (byte)player->pendingweapon);
	}

	return true;
}


//
// P_GiveWeapon
// The weapon name may have a MF_DROPPED flag ored in.
//
BOOL P_GiveWeapon (player_t *player, weapontype_t weapon, BOOL dropped)
{
	BOOL gaveammo;
	BOOL gaveweapon;
	int  playernum;

	// [RH] Don't get the weapon if no graphics for it
	state_t *state = states + weaponinfo[weapon].readystate;
	if ((state->frame & FF_FRAMEMASK) >= sprites[state->sprite].numframes)
		return false;

	if (netstate == NS_CLIENT)
	{
		if (player->weaponowned[weapon])
			gaveweapon = false;
		else
		{
			player->weaponowned[weapon] = true;
			gaveweapon = true;
		}
	
		return gaveweapon;
	}

	if (netstate != NS_SINGLEPLAYER &&
		((!deathmatch.value && !alwaysapplydmflags.value) || dmflags & DF_WEAPONS_STAY
		|| oldschool.value) &&
		!dropped)
	{
		// leave placed weapons forever on net games
		if (player->weaponowned[weapon])
			return false;

		player->bonuscount = BONUSADD;
		player->weaponowned[weapon] = true;

		if (deathmatch.value || teamgame.value)	// [BC]
			P_GiveAmmo (player, weaponinfo[weapon].ammo, 5);
		else
			P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);

		if (!player->userinfo.neverswitch || oldschool.value)
		{
			player->pendingweapon = weapon;

			if (netstate == NS_SERVER)
			{
				for (playernum=0; playernum<MAXPLAYERS; playernum++)
					if (player == &players[playernum])
						break;

				MSG_WriteByte (&clients[playernum].reliablebuf, svc_changeweapon);
				MSG_WriteByte (&clients[playernum].reliablebuf, (byte)player->pendingweapon);
			}
		}

		if (netstate == NS_SERVER)
		{
			SV_Sound (player->mo, CHAN_ITEM, "misc/w_pkup", ATTN_NORM);

			for (playernum=0; playernum<MAXPLAYERS; playernum++)
				if (player == &players[playernum])
					break;

			for (int i=0; i<MAXPLAYERS; i++)
			{
				client_t *cl = &clients[i];

				if (cl->cls < cs_spawned)
					continue;

				MSG_WriteByte (&cl->reliablebuf, svc_touchspecial);
				MSG_WriteByte (&cl->reliablebuf, playernum);
				switch (weapon)
				{
				case wp_bfg:
					MSG_WriteByte (&cl->reliablebuf, SPR_BFUG);
					break;
				case wp_chaingun:
					MSG_WriteByte (&cl->reliablebuf, SPR_MGUN);
					break;
				case wp_chainsaw:
					MSG_WriteByte (&cl->reliablebuf, SPR_CSAW);
					break;
				case wp_missile:
					MSG_WriteByte (&cl->reliablebuf, SPR_LAUN);
					break;
				case wp_plasma:
					MSG_WriteByte (&cl->reliablebuf, SPR_PLAS);
					break;
				case wp_shotgun:
					MSG_WriteByte (&cl->reliablebuf, SPR_SHOT);
					break;
				case wp_supershotgun:
					MSG_WriteByte (&cl->reliablebuf, SPR_SGN2);
					break;				
				case wp_grenlauncher:
					MSG_WriteByte (&cl->reliablebuf, SPR_GLAU);
					break;			
				case wp_railgun:
					MSG_WriteByte (&cl->reliablebuf, SPR_RAIL);
					break;				
				case wp_bfg10k:
					MSG_WriteByte (&cl->reliablebuf, SPR_SGN2);
					break;				

				}
			}
			return false;
		}
		else
		{
			S_Sound (player->mo, CHAN_ITEM, "misc/w_pkup", 1, ATTN_NORM);
			return false;
		}
	}
		
	if (weaponinfo[weapon].ammo != am_noammo)
	{
		// give one clip with a dropped weapon,
		// two clips with a found weapon
		if (dropped)
			gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
		else
			gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
	}
	else
		gaveammo = false;
		
	if (player->weaponowned[weapon])
		gaveweapon = false;
	else
	{
		gaveweapon = true;
		player->weaponowned[weapon] = true;

		if (!player->userinfo.neverswitch || oldschool.value)
		{
			player->pendingweapon = weapon;

			if (netstate == NS_SERVER)
			{
				for (playernum=0; playernum<MAXPLAYERS; playernum++)
					if (player == &players[playernum])
						break;

				MSG_WriteByte (&clients[playernum].reliablebuf, svc_changeweapon);
				MSG_WriteByte (&clients[playernum].reliablebuf, (byte)player->pendingweapon);
			}
		}
	}
		
	return (gaveweapon || gaveammo);
}

 
// [BC]
// P_CurrentRune
// Returns mobjtype_t value of current
// rune player is carrying. Used for rune dropping.

mobjtype_t P_CurrentRune (player_t *player)
{
	switch (player->rune)
	{
	case ru_strength:
		return MT_STRENGTH;
	case ru_drain:
		return MT_DRAIN;
	case ru_rage:
		return MT_RAGE;
	case ru_spread:
		return MT_SPREAD;
	case ru_resistance:
		return MT_RESISTANCE;
	case ru_regeneration:
		return MT_REGENERATION;
	case ru_prosperity:
		return MT_PROSPERITY;
	case ru_reflection:
		return MT_REFLECTION;
	case ru_highjump:
		return MT_HIJUMP;
	case ru_haste:
		return MT_HASTE;
	}

	return MT_MAXHTHBONUS;
}

// 
// [BC] P_GiveRune
// 
// Give a player a rune and drop current one if we want to do that
BOOL P_GiveRune (player_t *player, runetype_t rune, bool drop)
{
	AActor *mo = NULL;
	mobjtype_t current;

	if (player->rune == rune)
		return false;

	// [BC] Don't pick up anything for a bit
	if (player->runeTics > 1*TICRATE)
		return false;

	if (player->runeTics && (rune == player->lastrune))
		return false;

	if (player->rune == ru_none)
		player->lastrune = ru_none;

	if (drop && player->rune != ru_none)
	{
		current = P_CurrentRune (player);
		player->lastrune = player->rune;
		mo = new AActor(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, current);
		mo->momz = player->mo->momz + 5*FRACUNIT;
		mo->flags |= MF_DROPPED;
		mo->flags &= ~MF_NOGRAVITY;
	}
	
	player->rune = rune;
	player->runeTics = 2*TICRATE;
	return true;
} 

//
// P_GiveBody
// Returns false if the body isn't needed at all
//
BOOL P_GiveBody (player_t *player, int num)
{
	// [BC] All MAXHEALTH's changed to player's own maxlowhealth which can be increased
	if (player->health >= player->maxlowhealth && player->rune != ru_prosperity)// [BC]
		return false;
				
	player->health += num;
	if (player->health > player->maxhighhealth)
		player->health = player->maxhighhealth;
	if (player->health > player->maxlowhealth && player->rune != ru_prosperity)
	{	// [BC] Mandatory 200% max for balance purposes
		if (dmflags2 & DF2_YES_200MAX && player->health > 200)
			player->health = 200;
		else
			player->health = player->maxlowhealth;
	}

	player->mo->health = player->health;
		
	return true;
}



//
// P_GiveArmor
// Returns false if the armor is worse
// than the current armor.
// 
BOOL P_GiveArmor (player_t *player, int armortype)
{
	int hits;
	if (armortype == 3)	// [BC] Red armor gives 200%
		hits = 200;
	else
		hits = armortype*100;
	if (player->armorpoints == hits && player->armortype < armortype)  // [BC] Pick up red armor if player has blue
	{
		player->armortype = armortype;
		player->armorpoints += hits;
		if (player->armortype == 1)	// [BC]
			player->armorpoints = hits;
		else if (player->armorpoints > player->maxarmor)
			player->armorpoints = player->maxarmor;
		if (player->armorpoints > 200 && dmflags2 & DF2_YES_200MAX)
			player->armorpoints = 200;
		return true;
	}

	if (player->armorpoints >= hits)
		return false;	// don't pick up
				
	player->armortype = armortype;
	if (player->armortype == 1)	// [BC]
		player->armorpoints = hits;
	else
		player->armorpoints += hits;
	if (player->armorpoints > player->maxarmor)	// [BC]
		player->armorpoints = player->maxarmor;
	if (dmflags2 & DF2_YES_200MAX && player->armorpoints > 200)
		player->armorpoints = 200;
	return true;
}

//
// P_GiveCard
//
void P_GiveCard (player_t *player, card_t card)
{
	if (player->cards[card])
		return;
	
	player->bonuscount = BONUSADD;
	player->cards[card] = 1;
}


//
// P_GivePower
//
BOOL P_GivePower (player_t *player, int /*powertype_t*/ power)
{
	if (power == pw_invulnerability)
	{
		player->powers[power] = INVULNTICS;
		return true;
	}
	
	if (power == pw_invisibility)
	{
		player->powers[power] = INVISTICS;
		player->mo->flags |= MF_SHADOW;
		return true;
	}
	
	if (power == pw_infrared)
	{
		player->powers[power] = LIGHTTICS;	// [BC]
		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;
	}
// [BC]
	if (power == pw_turbosphere)
	{
		player->powers[power] = TURBOTICS;
		return true;
	}
		
	if (power == pw_floatingfeet)
	{
		player->powers[power] = ANTITICS;
		return true;
	}

	if (power == pw_freezer)
	{
		player->powers[power] = FREEZETICS;
		player->cheats &= CF_FREEZE;
		return true;
	}

	if (power == pw_infragoggles)
	{
		player->powers[power] = INFRATICS;
		return true;
	}

	if (power == pw_infratracker)
	{
		player->powers[power] = TRACKERTICS;
		return true;
	}

	if (power == pw_translucency)
	{
		player->powers[power] = TRANSTICS;
		player->mo->translucency = TRANSLUC10;
		return true;
	}

	if (power == pw_doomsphere)
	{
		player->powers[power] = DOOMTICS;
		return true;
	}

	if (power == pw_terminator)
	{
		player->powers[power] = true;
		return true;
	}
		
	if (player->powers[power])
		return false;	// already got it
				
	player->powers[power] = 1;
	return true;
}

//
// [BC] P_GiveMedal
//
// Give a player a medal, and also handle giving
// the player an icon to display over their head.
//

void P_GiveMedal (player_s *player, medaltype_t medal)
{
	if (oldschool.value)
		return;

	if (!deathmatch.value && !teamgame.value)
		return;

	if (netstate == NS_CLIENT)
		return;

	player->medals[medal]++;
	player->medalTics = FLOATYTICS;
	player->icon = new AActor (player->mo->x, player->mo->y, player->mo->z, MT_FLOATYICON);
	P_SetMobjState (player->icon, medalinfo[medal].frame);
	player->medalString = medalinfo[medal].string;
	player->icon->special1 = FLOATYTICS;
	player->icon->tracer = player->mo;
	if (medal == md_llama)
	{
		S_Sound (player->mo, CHAN_AUTO, "misc/llama", 1, ATTN_NORM);
		if (netstate == NS_SERVER)
			SV_Sound (player->mo, CHAN_AUTO, "misc/llama", ATTN_NORM);
	}

	else if (medalinfo[medal].textcolor == CR_RED)
		player->score += 500;	// Whoa!
	else
		player->score += 50;

	if (netstate == NS_SERVER)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			client_t *cl = &clients[i];

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte (&cl->reliablebuf, svc_givemedal);
			MSG_WriteByte (&cl->reliablebuf, player - players);
			MSG_WriteByte (&cl->reliablebuf, (byte)medal);
		}
	}

	// [BC] Implement scorelimit
	if (scorelimit.value && deathmatch.value &&
		(int)scorelimit.value <= player->score)
	{
		Printf (PRINT_HIGH, "Scorelimit hit.\n");
		levelTics = 1*TICRATE;
		return;
	}

	if (player->skullb)
		P_Taunt (player);
}

//
// [BC] P_Taunt
//
void P_Taunt (player_t *player)
{
	AActor *mo = player->mo;

	S_Sound (mo, CHAN_AUTO, "*taunt", 1, ATTN_NORM);
	if (netstate == NS_SERVER)
		SV_SoundAvoidCl (player, mo, CHAN_AUTO, "*taunt", ATTN_NORM);
}

//
// [BC] P_Suicide
//
void P_Suicide (player_t *player)
{
	AActor *mo = player->mo;

	P_DamageMobj (mo, NULL, NULL, mo->health+mo->info->spawnhealth, MOD_SUICIDE, DMG_NO_INVUL|DMG_NO_ARMOR);
}

//
// [BC] P_FindTerminator
//
void P_FindTerminator ()
{
	AActor *ball;
	int i, selections;
	mapthing2_t *spot;

	if (!deathmatch.value || netstate == NS_CLIENT)
		return;

	selections = deathmatch_p - deathmatchstarts;
	// [RH] We can get by with just 1 deathmatch start
	if (selections < 1)
		I_Error ("No deathmatch starts");

	i = P_Random (pr_dmspawn) % selections;
	spot = &deathmatchstarts[i];

	ball = new AActor (spot->x << FRACBITS, spot->y << FRACBITS, ONFLOORZ, MT_TERMINATOR);
}

//
// [BC] P_TouchSpecialThing
//
// Overloaded for the client version
void P_TouchSpecialThing (spritenum_t sprite, AActor *toucher)
{
	player_t*	player;
	int 		i;
	int 		sound;
					
	sound = 0; 
	player = toucher->player;

	// Identify by sprite.
	switch (sprite)
	{
		// armor
	  case SPR_ARM1:
		if (!P_GiveArmor (player, deh.GreenAC))
			return;
		PickupMessage (toucher, GOTARMOR);
		break;
				
	  case SPR_ARM2:
		if (!P_GiveArmor (player, deh.BlueAC))
			return;
		PickupMessage (toucher, GOTMEGA);
		break;
		// [BC] 
	  case SPR_ARM3:
		if (!P_GiveArmor (player, deh.RedAC))
			return;
		PickupMessage (toucher, GOTFIRE);
		break;

		// bonus items
	  case SPR_BON1:
		player->health++;				// can go over 100%
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHBONUS);
		break;
		
	  case SPR_BON2:
		player->armorpoints++;			// can go over 100%
		if (player->armorpoints > player->maxarmor)	// [BC]
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMBONUS);
		break;

		// [BC] Max health/armor items
	  case SPR_BON3:
		player->health++;
		player->maxlowhealth++;
		player->maxhighhealth++;

		if (player->maxhighhealth > 250)
			player->maxhighhealth = 250;
		if (player->maxlowhealth > 250)
			player->maxlowhealth = 250;
		if (player->health > player->maxhighhealth)
			player->health = player->maxhighhealth;

		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHMAXBONUS);
		break;

	  case SPR_BON4:
		player->armorpoints++;
		player->maxarmor++;

		if (player->maxarmor > 250)
			player->maxarmor = 250;
		if (player->armorpoints > player->maxarmor)
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMMAXBONUS);
		break;

	  case SPR_SOUL:
		player->health += deh.SoulsphereHealth;
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTSUPER);
		sound = 1;
		break;
		
	  case SPR_MEGA:
		player->health += deh.MegasphereHealth;	// [BC]
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		P_GiveArmor (player,deh.BlueAC);
		PickupMessage (toucher, GOTMSPHERE);
		sound = 1;
		break;
		
		// cards
		// leave cards for everyone
	  case SPR_BKEY:
		if (!player->cards[it_bluecard])
			PickupMessage (toucher, GOTBLUECARD);
		P_GiveCard (player, it_bluecard);
		sound = 3;
		if (teamgame.value)
			break;
		return;
		
	  case SPR_YKEY:
		if (!player->cards[it_yellowcard])
			PickupMessage (toucher, GOTYELWCARD);
		P_GiveCard (player, it_yellowcard);
		sound = 3;
		if (teamgame.value)
			break;
		return;
		
	  case SPR_RKEY:
		if (!player->cards[it_redcard])
			PickupMessage (toucher, GOTREDCARD);
		P_GiveCard (player, it_redcard);
		sound = 3;
		if (teamgame.value)
			break;
		return;

	  case SPR_BFLA:
	  case SPR_BSKU:
		if (!player->cards[it_blueskull])
			PickupMessage (toucher, GOTBLUESKUL);
		P_GiveCard (player, it_blueskull);
		sound = 3;
		if (teamgame.value)
			break;
		return;
		
	  case SPR_YSKU:
		if (!player->cards[it_yellowskull])
			PickupMessage (toucher, GOTYELWSKUL);
		P_GiveCard (player, it_yellowskull);
		sound = 3;
		if (teamgame.value)
			break;
		return;
		
	  case SPR_RFLA:
	  case SPR_RSKU:
		if (!player->cards[it_redskull])
			PickupMessage (toucher, GOTREDSKULL);
		P_GiveCard (player, it_redskull);
		sound = 3;
		if (teamgame.value)
			break;
		return;
		
		// medikits, heals
	  case SPR_STIM:
		if (!P_GiveBody (player, 10))
			return;
		PickupMessage (toucher, GOTSTIM);
		break;
		
	  case SPR_MEDI:
		if (!P_GiveBody (player, 25))
			return;

		if (player->health < 25)
			PickupMessage (toucher, GOTMEDINEED);
		else
			PickupMessage (toucher, GOTMEDIKIT);
		break;

		
		// power ups
	  case SPR_PINV:
		if (!P_GivePower (player, pw_invulnerability))
			return;
		PickupMessage (toucher, GOTINVUL);
		sound = 1;
		break;
		
	  case SPR_PSTR:
		if (!P_GivePower (player, pw_strength))
			return;
		PickupMessage (toucher, GOTBERSERK);
//		if (player->readyweapon != wp_fist)
//			player->pendingweapon = wp_fist;
		sound = 1;
		break;
		
	  case SPR_PINS:
		if (!P_GivePower (player, pw_invisibility))
			return;
		PickupMessage (toucher, GOTINVIS);
		sound = 1;
		break;
		
	  case SPR_SUIT:
		if (!P_GivePower (player, pw_ironfeet))
			return;
		PickupMessage (toucher, GOTSUIT);
		sound = 1;
		break;
		
	  case SPR_PMAP:
		if (!P_GivePower (player, pw_allmap))
			return;
		PickupMessage (toucher, GOTMAP);
		sound = 1;
		break;
		
	  case SPR_PVIS:
		if (!P_GivePower (player, pw_infrared))
			return;
		PickupMessage (toucher, GOTVISOR);
		sound = 1;
		break;
// [BC]
	  case SPR_TURB:
		if (!P_GivePower (player, pw_turbosphere))
			return;
		PickupMessage (toucher, GOTTURBO);
		sound = 1;
		break;

	  case SPR_ANTI:
		if (!P_GivePower (player, pw_floatingfeet))
			return;
		PickupMessage (toucher, GOTANTI);
		sound = 1;
		break;

	  case SPR_TIME:
		if (!P_GivePower (player, pw_freezer))
			return;
		PickupMessage (toucher, GOTTIME);
		sound = 1;
		break;

	  case SPR_INFR:
		if (!P_GivePower (player, pw_infragoggles))
			return;
		PickupMessage (toucher, GOTINFRARED);
		sound = 1;
		break;

	  case SPR_TRAK:
		if (!P_GivePower (player, pw_infratracker))
			return;
		PickupMessage (toucher, GOTTRACKER);
		sound = 1;
		break;

	  case SPR_INVS:
		if (!P_GivePower (player, pw_translucency))
			return;
		PickupMessage (toucher, GOTTRANSLUCENCY);
		sound = 1;
		break;

	  case SPR_DOOM:
		if (!P_GivePower (player, pw_doomsphere))
			return;
		PickupMessage (toucher, GOTDOOMSPHERE);
		sound = 1;
		break;

	  case SPR_ARNO:
		if (!P_GivePower (player, pw_terminator))
			return;
		player->health = deh.MegasphereHealth;
		player->mo->health = player->health;
		P_GiveArmor (player,deh.BlueAC);
		PickupMessage (toucher, GOTARNOLD);
		sound = 1;
		break;

	// [BC] Runes
	  case SPR_RUN1:
		if (!P_GiveRune (player, ru_strength))
			return;
		PickupMessage (toucher, GOTSTRENGTH);
		sound = 1;
		break;

	  case SPR_RUN2:
		if (!P_GiveRune (player, ru_drain))
			return;
		PickupMessage (toucher, GOTDRAIN);
		sound = 1;
		break;

	  case SPR_RUN3:
		if (!P_GiveRune (player, ru_rage))
			return;
		PickupMessage (toucher, GOTRAGE);
		sound = 1;
		break;

	  case SPR_RUN4:
		if (!P_GiveRune (player, ru_resistance))
			return;
		PickupMessage (toucher, GOTRESISTANCE);
		sound = 1;
		break;

	  case SPR_RUN5:
		if (!P_GiveRune (player, ru_regeneration))
			return;
		PickupMessage (toucher, GOTREGENERATION);
		sound = 1;
		break;

	  case SPR_RUN6:
		if (!P_GiveRune (player, ru_prosperity))
			return;
		PickupMessage (toucher, GOTPROSPERITY);
		sound = 1;
		break;

	  case SPR_RUN7:
		if (!P_GiveRune (player, ru_highjump))
			return;
		PickupMessage (toucher, GOTHIJUMP);
		sound = 1;
		break;

	  case SPR_RUN8:
		if (!P_GiveRune (player, ru_haste))
			return;
		PickupMessage (toucher, GOTHISPEED);
		sound = 1;
		break;

	  case SPR_RUN9:
		if (!P_GiveRune (player, ru_spread))
			return;
		PickupMessage (toucher, GOTSPREAD);
		sound = 1;
		break;

	  case SPR_RUNB:
		if (!P_GiveRune (player, ru_reflection))
			return;
		PickupMessage (toucher, GOTREFLECTION);
		sound = 1;
		break;

		// ammo
	  case SPR_CLIP:
		/*
		if (special->flags & MF_DROPPED)
		{
			if (!P_GiveAmmo (player,am_clip,0))
				return;
		}
		else
		{
		*/
			if (!P_GiveAmmo (player,am_clip,1))
				return;
//		}
		PickupMessage (toucher, GOTCLIP);
		break;
		
	  case SPR_AMMO:
		if (!P_GiveAmmo (player, am_clip,5))
			return;
		PickupMessage (toucher, GOTCLIPBOX);
		break;
		
	  case SPR_ROCK:
		if (!P_GiveAmmo (player, am_misl,1))
			return;
		PickupMessage (toucher, GOTROCKET);
		break;
		
	  case SPR_BROK:
		if (!P_GiveAmmo (player, am_misl,5))
			return;
		PickupMessage (toucher, GOTROCKBOX);
		break;
		
	  case SPR_CELL:
		if (!P_GiveAmmo (player, am_cell,1))
			return;
		PickupMessage (toucher, GOTCELL);
		break;
		
	  case SPR_CELP:
		if (!P_GiveAmmo (player, am_cell,5))
			return;
		PickupMessage (toucher, GOTCELLBOX);
		break;
		
	  case SPR_SHEL:
		if (!P_GiveAmmo (player, am_shell,1))
			return;
		PickupMessage (toucher, GOTSHELLS);
		break;
		
	  case SPR_SBOX:
		if (!P_GiveAmmo (player, am_shell,5))
			return;
		PickupMessage (toucher, GOTSHELLBOX);
		break;
		
	  case SPR_BPAK:
		if (!player->backpack)
		{
			for (i=0 ; i<NUMAMMO ; i++)
				player->maxammo[i] *= 2;
			player->backpack = true;
		}
		for (i=0 ; i<NUMAMMO ; i++)
			P_GiveAmmo (player, (ammotype_t)i, 1);
		PickupMessage (toucher, GOTBACKPACK);
		break;
		
		// weapons
	  case SPR_BFUG:
		if (!P_GiveWeapon (player, wp_bfg, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTBFG9000);
		sound = 2;		
		break;
		
	  case SPR_MGUN:
		if (!P_GiveWeapon (player, wp_chaingun, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTCHAINGUN);
		sound = 2;		
		break;
		
	  case SPR_CSAW:
		if (!P_GiveWeapon (player, wp_chainsaw, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTCHAINSAW);
		sound = 2;		
		break;
		
	  case SPR_LAUN:
		if (!P_GiveWeapon (player, wp_missile, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTLAUNCHER);
		sound = 2;		
		break;
		
	  case SPR_PLAS:
		if (!P_GiveWeapon (player, wp_plasma, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTPLASMA);
		sound = 2;		
		break;
		
	  case SPR_SHOT:
		if (!P_GiveWeapon (player, wp_shotgun, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTSHOTGUN);
		sound = 2;		
		break;
				
	  case SPR_SGN2:
		if (!P_GiveWeapon (player, wp_supershotgun, 0/*special->flags & MF_DROPPED*/))
			return;
		PickupMessage (toucher, GOTSHOTGUN2);
		sound = 2;		
		break;

	  case SPR_GLAU:
		if (!P_GiveWeapon (player, wp_grenlauncher, 0))//, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTGRENLAUN);
		sound = 2;		
		break;

	  case SPR_RAIL:
		if (!P_GiveWeapon (player, wp_railgun, 0))//special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTRAILGUN);
		sound = 2;		
		break;
				
	  default:
		Printf (PRINT_HIGH, "P_TouchSpecialThing: Unknown gettable thing: %d\n", sprite);
	}

	player->bonuscount = BONUSADD;
}


//
// P_TouchSpecialThing
//
void P_TouchSpecialThing (AActor *special, AActor *toucher)
{
	player_t*	player;
	int 		i;
	fixed_t 	delta;
	int 		sound, oldhealth;	// [BC]
				
	delta = special->z - toucher->z;

	if (delta > toucher->height || delta < -8*FRACUNIT)
	{
		// out of reach
		return;
	}
		
	sound = 0; 
	player = toucher->player;
	oldhealth = toucher->health;	// [BC]

	// Dead thing touching.
	// Can happen with a sliding player corpse.
	if (toucher->health <= 0)
		return;

	// [BC] Handle teamgame stuff
	if (teamgame.value && player->team != TM_NONE)
	{
		if (((special->flags2 & MF2_BLUE && player->team == TM_RED)	// [BC] Whew!
			|| (special->flags2 & MF2_RED && player->team == TM_BLUE)) &&
			!(special->flags & MF_DROPPED))
			return;
	}

	// [BC] NULL bot destinations
	for (i=0; i<MAXPLAYERS; i++)
	{
		if (players[i].skullb && players[i].skullb->item == special)
			players[i].skullb->item = NULL;
		if (players[i].skullb && players[i].skullb->goal == special)
			players[i].skullb->goal = NULL;
	}

	if (special->type == MT_RANDOMPOWERUP)
	{
		switch (special->sprite)
		{
		case SPR_PINV:
			if (!P_GivePower (player, pw_invulnerability))
				return;
			PickupMessage (toucher, GOTINVUL);
			sound = 1;
			break;
				
		case SPR_PINS:
			if (!P_GivePower (player, pw_invisibility))
				return;
			PickupMessage (toucher, GOTINVIS);
			sound = 1;
			break;

		case SPR_SOUL:
			player->health += deh.SoulsphereHealth;
			if (player->health > player->maxhighhealth)
				player->health = player->maxhighhealth;
			player->mo->health = player->health;
			PickupMessage (toucher, GOTSUPER);
			sound = 1;
			break;
		
		case SPR_MEGA:
			player->health += deh.MegasphereHealth;
			if (player->health > player->maxhighhealth)
				player->health = player->maxhighhealth;
			player->mo->health = player->health;
			P_GiveArmor (player,deh.BlueAC);
			PickupMessage (toucher, GOTMSPHERE);
			sound = 1;
			break;

		case SPR_TURB:
			if (!P_GivePower (player, pw_turbosphere))
				return;
			PickupMessage (toucher, GOTTURBO);
			sound = 1;
			break;

		case SPR_TIME:
			if (!P_GivePower (player, pw_freezer))
				return;
			PickupMessage (toucher, GOTTIME);
			sound = 1;
			break;

		case SPR_INVS:
			if (!P_GivePower (player, pw_translucency))
				return;
			PickupMessage (toucher, GOTTRANSLUCENCY);
			sound = 1;
			break;

		case SPR_DOOM:
			if (!P_GivePower (player, pw_doomsphere))
				return;
			PickupMessage (toucher, GOTDOOMSPHERE);
			sound = 1;
			break;
		}
	}
	// [BC] Now we go by mobjtype
	else switch (special->type)
	{
		// armor
	  case MT_ARMOR:
		if (!P_GiveArmor (player, deh.GreenAC))
			return;
		PickupMessage (toucher, GOTARMOR);
		break;
				
	  case MT_MEGAARMOR:
		if (!P_GiveArmor (player, deh.BlueAC))
			return;
		PickupMessage (toucher, GOTMEGA);
		break;
		// [BC] 
	  case MT_REDARMOR:
		if (!P_GiveArmor (player, deh.RedAC))
			return;
		PickupMessage (toucher, GOTFIRE);
		break;

		// bonus items
	  case MT_HTHBONUS:
		player->health++;				// can go over 100%
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHBONUS);
		break;
		
	  case MT_ARMBONUS:
		player->armorpoints++;			// can go over 100%
		if (player->armorpoints > player->maxarmor)	// [BC]
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMBONUS);
		break;

		// [BC] Max health/armor items
	  case MT_MAXHTHBONUS:
		player->health++;
		player->maxlowhealth++;
		player->maxhighhealth++;

		if (player->maxhighhealth > 250)
			player->maxhighhealth = 250;
		if (player->maxlowhealth > 250)
			player->maxlowhealth = 250;
		if (player->health > player->maxhighhealth)
			player->health = player->maxhighhealth;

		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHMAXBONUS);
		break;

	  case MT_MAXARMBONUS:
		player->armorpoints++;
		player->maxarmor++;

		if (player->maxarmor > 250)
			player->maxarmor = 250;
		if (player->armorpoints > player->maxarmor)
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMMAXBONUS);
		break;

	  case MT_SOULSPHERE:
		player->health += deh.SoulsphereHealth;
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTSUPER);
		sound = 1;
		break;
		
	  case MT_MEGA:
		player->health += deh.MegasphereHealth;	// [BC]
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		P_GiveArmor (player,deh.BlueAC);
		PickupMessage (toucher, GOTMSPHERE);
		sound = 1;
		break;

		// cards
		// leave cards for everyone
	  case MT_BKEY:
		if (!player->cards[it_bluecard])
			PickupMessage (toucher, GOTBLUECARD);
		P_GiveCard (player, it_bluecard);
		sound = 3;
		 // [BC] Pick up keys in single player and teamgame mode
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_YKEY:
		if (!player->cards[it_yellowcard])
			PickupMessage (toucher, GOTYELWCARD);
		P_GiveCard (player, it_yellowcard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_RKEY:
		if (!player->cards[it_redcard])
			PickupMessage (toucher, GOTREDCARD);
		P_GiveCard (player, it_redcard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_BSKU:
		if (!player->cards[it_blueskull])
			PickupMessage (toucher, GOTBLUESKUL);
		P_GiveCard (player, it_blueskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_YSKU:
		if (!player->cards[it_yellowskull])
			PickupMessage (toucher, GOTYELWSKUL);
		P_GiveCard (player, it_yellowskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_RSKU:
		if (!player->cards[it_redskull])
			PickupMessage (toucher, GOTREDSKULL);
		P_GiveCard (player, it_redskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;

		// [BC]	New keys
	  case MT_LSKU:
		if (!player->cards[it_blackskull])
			PickupMessage (toucher, GOTBLACKSKULL);
		P_GiveCard (player, it_blackskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_SSKU:
		if (!player->cards[it_silverskull])
			PickupMessage (toucher, GOTSILVERSKULL);
		P_GiveCard (player, it_silverskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_GSKU:
		if (!player->cards[it_goldskull])
			PickupMessage (toucher, GOTGOLDSKULL);
		P_GiveCard (player, it_goldskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_LKEY:
		if (!player->cards[it_blackcard])
			PickupMessage (toucher, GOTBLACKCARD);
		P_GiveCard (player, it_blackcard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_SKEY:
		if (!player->cards[it_silvercard])
			PickupMessage (toucher, GOTSILVERCARD);
		P_GiveCard (player, it_silvercard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;
		
	  case MT_GKEY:
		if (!player->cards[it_goldcard])
			PickupMessage (toucher, GOTGOLDCARD);
		P_GiveCard (player, it_goldcard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;

	  case MT_MKEY:
		if (!player->cards[it_mastercard])
			PickupMessage (toucher, GOTMASTERCARD);
		P_GiveCard (player, it_mastercard);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;

	  case MT_MSKU:
		if (!player->cards[it_masterskull])
			PickupMessage (toucher, GOTMASTERSKULL);
		P_GiveCard (player, it_masterskull);
		sound = 3;
		if (netstate == NS_SINGLEPLAYER || teamgame.value)
			break;
		return;

		// medikits, heals
	  case MT_STIMPACK:
		if (!P_GiveBody (player, 10))
			return;
		PickupMessage (toucher, GOTSTIM);
		break;
		
	  case MT_MEDKIT:
		if (!P_GiveBody (player, 25))
			return;

		if (oldhealth < 25)	// [BC] Silly id!
			PickupMessage (toucher, GOTMEDINEED);
		else
			PickupMessage (toucher, GOTMEDIKIT);
		break;
		
		// power ups
	  case MT_INV:
		if (!P_GivePower (player, pw_invulnerability))
			return;
		PickupMessage (toucher, GOTINVUL);
		sound = 1;
		break;
		
	  case MT_BERSERK:
		if (!P_GivePower (player, pw_strength))
			return;
		PickupMessage (toucher, GOTBERSERK);
		if (player->readyweapon != wp_fist)
		{
			player->pendingweapon = wp_fist;
			if (netstate == NS_SERVER)
			{			
				MSG_WriteByte (&clients[player - players].reliablebuf, svc_changeweapon);
				MSG_WriteByte (&clients[player - players].reliablebuf, (byte)player->pendingweapon);
			}
		}
		sound = 1;
		break;
		
	  case MT_INS:
		if (!P_GivePower (player, pw_invisibility))
			return;
		PickupMessage (toucher, GOTINVIS);
		sound = 1;
		break;
		
	  case MT_RADSUIT:
		if (!P_GivePower (player, pw_ironfeet))
			return;
		PickupMessage (toucher, GOTSUIT);
		sound = 1;
		break;
		
	  case MT_COMPMAP:
		if (!P_GivePower (player, pw_allmap))
			return;
		PickupMessage (toucher, GOTMAP);
		sound = 1;
		break;
		
	  case MT_LIGHTAMP:
		if (!P_GivePower (player, pw_infrared))
			return;
		PickupMessage (toucher, GOTVISOR);
		sound = 1;
		break;

		// [BC] New power-ups
	  case MT_TURBOSPHERE:
		if (!P_GivePower (player, pw_turbosphere))
			return;
		PickupMessage (toucher, GOTTURBO);
		sound = 1;
		break;

	  case MT_FLOATINGFEET:
		if (!P_GivePower (player, pw_floatingfeet))
			return;
		PickupMessage (toucher, GOTANTI);
		sound = 1;
		break;

	  case MT_TIMEFREEZER:
		if (!P_GivePower (player, pw_freezer))
			return;
		PickupMessage (toucher, GOTTIME);
		sound = 1;
		break;

	  case MT_INFRATRACKER:
		if (!P_GivePower (player, pw_infratracker))
			return;
		PickupMessage (toucher, GOTTRACKER);
		sound = 1;
		break;

	  case MT_INVISIBILITY:
		if (!P_GivePower (player, pw_translucency))
			return;
		PickupMessage (toucher, GOTTRANSLUCENCY);
		sound = 1;
		break;

	  case MT_DOOMSPHERE:
		if (!P_GivePower (player, pw_doomsphere))
			return;
		PickupMessage (toucher, GOTDOOMSPHERE);
		sound = 1;
		break;

	  case MT_TERMINATOR:
		if (!P_GivePower (player, pw_terminator))
			return;
		player->health = deh.MegasphereHealth;
		player->mo->health = player->health;
		P_GiveArmor (player,deh.BlueAC);
		PickupMessage (toucher, GOTARNOLD);
		sound = 1;
		break;

		// [BC] Runes
	  case MT_STRENGTH:
		if (!P_GiveRune (player, ru_strength))
			return;
		PickupMessage (toucher, GOTSTRENGTH);
		sound = 1;
		break;

	  case MT_DRAIN:
		if (!P_GiveRune (player, ru_drain))
			return;
		PickupMessage (toucher, GOTDRAIN);
		sound = 1;
		break;

	  case MT_RAGE:
		if (!P_GiveRune (player, ru_rage))
			return;
		PickupMessage (toucher, GOTRAGE);
		sound = 1;
		break;

	  case MT_RESISTANCE:
		if (!P_GiveRune (player, ru_resistance))
			return;
		PickupMessage (toucher, GOTRESISTANCE);
		sound = 1;
		break;

	  case MT_REGENERATION:
		if (!P_GiveRune (player, ru_regeneration))
			return;
		PickupMessage (toucher, GOTREGENERATION);
		sound = 1;
		break;

	  case MT_PROSPERITY:
		if (!P_GiveRune (player, ru_prosperity))
			return;
		PickupMessage (toucher, GOTPROSPERITY);
		sound = 1;
		break;

	  case MT_HIJUMP:
		if (!P_GiveRune (player, ru_highjump))
			return;
		PickupMessage (toucher, GOTHIJUMP);
		sound = 1;
		break;

	  case MT_HASTE:
		if (!P_GiveRune (player, ru_haste))
			return;
		PickupMessage (toucher, GOTHISPEED);
		sound = 1;
		break;

	  case MT_SPREAD:
		if (!P_GiveRune (player, ru_spread))
			return;
		PickupMessage (toucher, GOTSPREAD);
		sound = 1;
		break;

	  case MT_REFLECTION:
		if (!P_GiveRune (player, ru_reflection))
			return;
		PickupMessage (toucher, GOTREFLECTION);
		sound = 1;
		break;

	// ammo
	  case MT_CLIP:
		if (special->flags & MF_DROPPED)
		{
			if (!P_GiveAmmo (player,am_clip,0))
				return;
		}
		else
		{
			if (!P_GiveAmmo (player,am_clip,1))
				return;
		}
		PickupMessage (toucher, GOTCLIP);
		break;
		
	  case MT_AMMOBOX:
		if (!P_GiveAmmo (player, am_clip,5))
			return;
		PickupMessage (toucher, GOTCLIPBOX);
		break;
		
	  case MT_MISSILE:
		if (!P_GiveAmmo (player, am_misl,1))
			return;
		PickupMessage (toucher, GOTROCKET);
		break;
		
	  case MT_MISSILEBOX:
		if (!P_GiveAmmo (player, am_misl,5))
			return;
		PickupMessage (toucher, GOTROCKBOX);
		break;
		
	  case MT_CELL:
		if (!P_GiveAmmo (player, am_cell,1))
			return;
		PickupMessage (toucher, GOTCELL);
		break;
		
	  case MT_CELLPACK:
		if (!P_GiveAmmo (player, am_cell,5))
			return;
		PickupMessage (toucher, GOTCELLBOX);
		break;
		
	  case MT_SHELLS:
		if (!P_GiveAmmo (player, am_shell,1))
			return;
		PickupMessage (toucher, GOTSHELLS);
		break;
		
	  case MT_SHELLBOX:
		if (!P_GiveAmmo (player, am_shell,5))
			return;
		PickupMessage (toucher, GOTSHELLBOX);
		break;
		
	  case MT_BACKPACK:
		if (!player->backpack)
		{
			for (i=0 ; i<NUMAMMO ; i++)
				player->maxammo[i] *= 2;
			player->backpack = true;
		}
		for (i=0 ; i<NUMAMMO ; i++)
			P_GiveAmmo (player, (ammotype_t)i, 1);
		PickupMessage (toucher, GOTBACKPACK);
		break;
		
		// weapons
	  case MT_PISTOL:	// [BC] Pistol can be an item now
		if (!P_GiveWeapon (player, wp_pistol, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTPISTOL);
		sound = 2;		
		break;

	  case MT_BFG9000:
		if (!P_GiveWeapon (player, wp_bfg, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTBFG9000);
		sound = 2;		
		break;
		
	  case MT_CHAINGUN:
		if (!P_GiveWeapon (player, wp_chaingun, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTCHAINGUN);
		sound = 2;		
		break;
		
	  case MT_CHAINSAW:
		if (!P_GiveWeapon (player, wp_chainsaw, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTCHAINSAW);
		sound = 2;		
		break;
		
	  case MT_ROCKETLAUNCHER:
		if (!P_GiveWeapon (player, wp_missile, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTLAUNCHER);
		sound = 2;		
		break;
		
	  case MT_PLASMAGUN:
		if (!P_GiveWeapon (player, wp_plasma, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTPLASMA);
		sound = 2;		
		break;
		
	  case MT_SHOTGUN:
		if (!P_GiveWeapon (player, wp_shotgun, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTSHOTGUN);
		sound = 2;		
		break;
				
	  case MT_SUPERSHOTGUN:
		if (!P_GiveWeapon (player, wp_supershotgun, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTSHOTGUN2);
		sound = 2;		
		break;

		// [BC] New weapons
	  case MT_GRENADELAUNCHER:
		if (!P_GiveWeapon (player, wp_grenlauncher, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTGRENLAUN);
		sound = 2;		
		break;

	  case MT_RAILGUN:
		if (!P_GiveWeapon (player, wp_railgun, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTRAILGUN);
		sound = 2;		
		break;

	  case MT_BFG10000:
		if (!P_GiveWeapon (player, wp_bfg10k, special->flags & MF_DROPPED))
			return;
		PickupMessage (toucher, GOTBFG10000);
		sound = 2;		
		break;

	  default:
		I_Error ("P_SpecialThing: Unknown gettable thing");
	}

	if (netstate == NS_SERVER)
	{
	    for (i=0; i<MAXPLAYERS; i++)
		{
			client_t *cl = &clients[i];

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte (&cl->reliablebuf, svc_touchspecial);
			MSG_WriteByte (&cl->reliablebuf, player - players);
			MSG_WriteByte (&cl->reliablebuf, special->sprite);
		}
	}

	// [RH] Execute an attached special (if any)
	if (special->special)
	{
		LineSpecials[special->special] (NULL, toucher, special->args[0],
			special->args[1], special->args[2], special->args[3], special->args[4], special->args[5]); // [BC]
		special->special = 0;
	}

	// [BC] Handle teamgame stuff
	if (teamgame.value && player->team != TM_NONE)
	{
		if (special->type == teams[player->team].ours)
		{
//			player->team->returnroutine;
			player->cards[teams[player->team].ourkey] = false;
			if (player->skullb)
				player->skullb->state = BST_NOTHING;
			if (player->team == TM_BLUE)
				P_StartTeam1ReturnScripts ();
			else
				P_StartTeam2ReturnScripts ();

			if (netstate == NS_SERVER)
			{
				for (i=0; i<MAXPLAYERS; i++)
				{
					client_t *cl = &clients[i];

					if (cl->cls < cs_spawned)
						continue;

					MSG_WriteByte (&cl->reliablebuf, svc_returnscripts);
					MSG_WriteByte (&cl->reliablebuf, (byte)player->team);

					MSG_WriteByte (&cl->reliablebuf, svc_lostitem);
					MSG_WriteByte (&cl->reliablebuf, (byte)(player - players));
					MSG_WriteByte (&cl->reliablebuf, (byte)teams[player->team].ours);
				}
			}
			teams[player->team].returnTics = 0;
		}
		else if (special->type == teams[player->team].theirs)
		{
			P_StartPickupScripts (toucher);
			if (netstate == NS_SERVER)
			{
				for (i=0; i<MAXPLAYERS; i++)
				{
					client_t *cl = &clients[i];

					if (cl->cls < cs_spawned)
						continue;

					MSG_WriteByte (&cl->reliablebuf, svc_pickupscripts);
					MSG_WriteByte (&cl->reliablebuf, (byte)(player - players));

					MSG_WriteByte (&cl->reliablebuf, svc_giveitem);
					MSG_WriteByte (&cl->reliablebuf, (byte)(player - players));
					MSG_WriteShort (&cl->reliablebuf, (short)teams[player->team].theirs);
				}
			}
			if (player->team == TM_BLUE)
				teams[player->team].returnTics = 0;
			else
				teams[TM_RED].returnTics = 0;
			if (player->skullb)
				player->skullb->state = BST_RETURN;
		}
	}

	// [BC] Update terminator status
	if (terminator.value && netstate == NS_SERVER)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			if (!playeringame[i] || !players[i].powers[pw_terminator])
				continue;

			for (int j=0; j<MAXPLAYERS; j++)
			{
				client_t *cl = &clients[j];

				if (cl->cls < cs_spawned || j == parse_cl)
					continue;

				MSG_WriteByte (&cl->reliablebuf, svc_giveitem);
				MSG_WriteByte (&cl->reliablebuf, i);
				MSG_WriteShort (&cl->reliablebuf, (short)MT_TERMINATOR);
			}
		}
	}

	if (special->flags & MF_COUNTITEM)
	{
		player->itemcount++;
		level.found_items++;
	}
	special->Destroy ();
	player->bonuscount = BONUSADD;

	{
		AActor *ent;

		if (oldschool.value && player->mo != players[consoleplayer].camera)
			return;

		if (netstate == NS_SERVER)
			ent = player->mo;
		else if (player->mo == players[consoleplayer].camera)
			ent = NULL;
		else
			ent = player->mo;

		switch (sound) {
			case 0:
			case 3:
				S_Sound (ent, CHAN_ITEM, "misc/i_pkup", 1, ATTN_NORM);
				if (netstate == NS_SERVER)
					SV_Sound (ent, CHAN_ITEM, "misc/i_pkup", ATTN_NORM);
				break;
			case 1:
				S_Sound (ent, CHAN_ITEM, "misc/p_pkup", 1,
					!ent ? ATTN_SURROUND : ATTN_NORM);
				if (netstate == NS_SERVER)
					SV_Sound (ent, CHAN_ITEM, "misc/p_pkup",
						!ent ? ATTN_SURROUND : ATTN_NORM);
				break;
			case 2:
				S_Sound (ent, CHAN_ITEM, "misc/w_pkup", 1, ATTN_NORM);
				if (netstate == NS_SERVER)
					SV_Sound (ent, CHAN_ITEM, "misc/w_pkup", ATTN_NORM);
				break;
		}
	}
}

//
// P_GiveSpecialThing
//
void P_GiveSpecialThing (mobjtype_t type, player_t *player, bool message, bool update)
{
	int i;
	int oldhealth = player->health;
	AActor *toucher = player->mo;

	switch (type)
	{
		// armor
	  case MT_ARMOR:
		if (!P_GiveArmor (player, deh.GreenAC))
			return;
		PickupMessage (toucher, GOTARMOR);
		break;
				
	  case MT_MEGAARMOR:
		if (!P_GiveArmor (player, deh.BlueAC))
			return;
		PickupMessage (toucher, GOTMEGA);
		break;
		// [BC] 
	  case MT_REDARMOR:
		if (!P_GiveArmor (player, deh.RedAC))
			return;
		PickupMessage (toucher, GOTFIRE);
		break;

		// bonus items
	  case MT_HTHBONUS:
		player->health++;				// can go over 100%
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHBONUS);
		break;
		
	  case MT_ARMBONUS:
		player->armorpoints++;			// can go over 100%
		if (player->armorpoints > player->maxarmor)	// [BC]
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMBONUS);
		break;

		// [BC] Max health/armor items
	  case MT_MAXHTHBONUS:
		player->health++;
		player->maxlowhealth++;
		player->maxhighhealth++;

		if (player->maxhighhealth > 250)
			player->maxhighhealth = 250;
		if (player->maxlowhealth > 250)
			player->maxlowhealth = 250;
		if (player->health > player->maxhighhealth)
			player->health = player->maxhighhealth;

		player->mo->health = player->health;
		PickupMessage (toucher, GOTHTHMAXBONUS);
		break;

	  case MT_MAXARMBONUS:
		player->armorpoints++;
		player->maxarmor++;

		if (player->maxarmor > 250)
			player->maxarmor = 250;
		if (player->armorpoints > player->maxarmor)
			player->armorpoints = player->maxarmor;
		if (!player->armortype)
			player->armortype = deh.GreenAC;
		PickupMessage (toucher, GOTARMMAXBONUS);
		break;

	  case MT_SOULSPHERE:
		player->health += deh.SoulsphereHealth;
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		PickupMessage (toucher, GOTSUPER);
		break;
		
	  case MT_MEGA:
		player->health += deh.MegasphereHealth;	// [BC]
		if (player->health > player->maxhighhealth)	// [BC]
			player->health = player->maxhighhealth;
		player->mo->health = player->health;
		P_GiveArmor (player,deh.BlueAC);
		PickupMessage (toucher, GOTMSPHERE);
		break;

		// cards
		// leave cards for everyone
	  case MT_BKEY:
		if (!player->cards[it_bluecard])
			PickupMessage (toucher, GOTBLUECARD);
		P_GiveCard (player, it_bluecard);
		break;
		
	  case MT_YKEY:
		if (!player->cards[it_yellowcard])
			PickupMessage (toucher, GOTYELWCARD);
		P_GiveCard (player, it_yellowcard);
		break;
		
	  case MT_RKEY:
		if (!player->cards[it_redcard])
			PickupMessage (toucher, GOTREDCARD);
		P_GiveCard (player, it_redcard);
		break;
		
	  case MT_BSKU:
		if (!player->cards[it_blueskull])
			PickupMessage (toucher, GOTBLUESKUL);
		P_GiveCard (player, it_blueskull);
		break;
		
	  case MT_YSKU:
		if (!player->cards[it_yellowskull])
			PickupMessage (toucher, GOTYELWSKUL);
		P_GiveCard (player, it_yellowskull);
		break;
		
	  case MT_RSKU:
		if (!player->cards[it_redskull])
			PickupMessage (toucher, GOTREDSKULL);
		P_GiveCard (player, it_redskull);
		break;

		// [BC]	New keys
	  case MT_LSKU:
		if (!player->cards[it_blackskull])
			PickupMessage (toucher, GOTBLACKSKULL);
		P_GiveCard (player, it_blackskull);
		break;
		
	  case MT_SSKU:
		if (!player->cards[it_silverskull])
			PickupMessage (toucher, GOTSILVERSKULL);
		P_GiveCard (player, it_silverskull);
		break;
		
	  case MT_GSKU:
		if (!player->cards[it_goldskull])
			PickupMessage (toucher, GOTGOLDSKULL);
		P_GiveCard (player, it_goldskull);
		break;
		
	  case MT_LKEY:
		if (!player->cards[it_blackcard])
			PickupMessage (toucher, GOTBLACKCARD);
		P_GiveCard (player, it_blackcard);
		break;
		
	  case MT_SKEY:
		if (!player->cards[it_silvercard])
			PickupMessage (toucher, GOTSILVERCARD);
		P_GiveCard (player, it_silvercard);
		break;
		
	  case MT_GKEY:
		if (!player->cards[it_goldcard])
			PickupMessage (toucher, GOTGOLDCARD);
		P_GiveCard (player, it_goldcard);
		break;

	  case MT_MKEY:
		if (!player->cards[it_mastercard])
			PickupMessage (toucher, GOTMASTERCARD);
		P_GiveCard (player, it_mastercard);
		break;

	  case MT_MSKU:
		if (!player->cards[it_masterskull])
			PickupMessage (toucher, GOTMASTERSKULL);
		P_GiveCard (player, it_masterskull);
		break;

		// medikits, heals
	  case MT_STIMPACK:
		if (!P_GiveBody (player, 10))
			return;
		PickupMessage (toucher, GOTSTIM);
		break;
		
	  case MT_MEDKIT:
		if (!P_GiveBody (player, 25))
			return;

		if (oldhealth < 25)	// [BC] Silly id!
			PickupMessage (toucher, GOTMEDINEED);
		else
			PickupMessage (toucher, GOTMEDIKIT);
		break;
		
		// power ups
	  case MT_INV:
		if (!P_GivePower (player, pw_invulnerability))
			return;
		PickupMessage (toucher, GOTINVUL);
		break;
		
	  case MT_BERSERK:
		if (!P_GivePower (player, pw_strength))
			return;
		PickupMessage (toucher, GOTBERSERK);
		if (player->readyweapon != wp_fist)
		{
			player->pendingweapon = wp_fist;
			if (netstate == NS_SERVER)
			{			
				MSG_WriteByte (&clients[player - players].reliablebuf, svc_changeweapon);
				MSG_WriteByte (&clients[player - players].reliablebuf, (byte)player->pendingweapon);
			}
		}
		break;
		
	  case MT_INS:
		if (!P_GivePower (player, pw_invisibility))
			return;
		PickupMessage (toucher, GOTINVIS);
		break;
		
	  case MT_RADSUIT:
		if (!P_GivePower (player, pw_ironfeet))
			return;
		PickupMessage (toucher, GOTSUIT);
		break;
		
	  case MT_COMPMAP:
		if (!P_GivePower (player, pw_allmap))
			return;
		PickupMessage (toucher, GOTMAP);
		break;
		
	  case MT_LIGHTAMP:
		if (!P_GivePower (player, pw_infrared))
			return;
		PickupMessage (toucher, GOTVISOR);
		break;

		// [BC] New power-ups
	  case MT_TURBOSPHERE:
		if (!P_GivePower (player, pw_turbosphere))
			return;
		PickupMessage (toucher, GOTTURBO);
		break;

	  case MT_FLOATINGFEET:
		if (!P_GivePower (player, pw_floatingfeet))
			return;
		PickupMessage (toucher, GOTANTI);
		break;

	  case MT_TIMEFREEZER:
		if (!P_GivePower (player, pw_freezer))
			return;
		PickupMessage (toucher, GOTTIME);
		break;

	  case MT_INFRATRACKER:
		if (!P_GivePower (player, pw_infratracker))
			return;
		PickupMessage (toucher, GOTTRACKER);
		break;

	  case MT_INVISIBILITY:
		if (!P_GivePower (player, pw_translucency))
			return;
		PickupMessage (toucher, GOTTRANSLUCENCY);
		break;

	  case MT_DOOMSPHERE:
		if (!P_GivePower (player, pw_doomsphere))
			return;
		PickupMessage (toucher, GOTDOOMSPHERE);
		break;

	  case MT_TERMINATOR:
		if (!P_GivePower (player, pw_terminator))
			return;
		if (!update)
		{
			player->health = deh.MegasphereHealth;
			// [BC] Do it this way for network games
			if (player->mo)
				player->mo->health = player->health;
			P_GiveArmor (player,deh.BlueAC);
			PickupMessage (toucher, GOTARNOLD);
		}
		break;

		// [BC] Runes
	  case MT_STRENGTH:
		if (!P_GiveRune (player, ru_strength))
			return;
		PickupMessage (toucher, GOTSTRENGTH);
		break;

	  case MT_DRAIN:
		if (!P_GiveRune (player, ru_drain))
			return;
		PickupMessage (toucher, GOTDRAIN);
		break;

	  case MT_RAGE:
		if (!P_GiveRune (player, ru_rage))
			return;
		PickupMessage (toucher, GOTRAGE);
		break;

	  case MT_RESISTANCE:
		if (!P_GiveRune (player, ru_resistance))
			return;
		PickupMessage (toucher, GOTRESISTANCE);
		break;

	  case MT_REGENERATION:
		if (!P_GiveRune (player, ru_regeneration))
			return;
		PickupMessage (toucher, GOTREGENERATION);
		break;

	  case MT_PROSPERITY:
		if (!P_GiveRune (player, ru_prosperity))
			return;
		PickupMessage (toucher, GOTPROSPERITY);
		break;

	  case MT_HIJUMP:
		if (!P_GiveRune (player, ru_highjump))
			return;
		PickupMessage (toucher, GOTHIJUMP);
		break;

	  case MT_HASTE:
		if (!P_GiveRune (player, ru_haste))
			return;
		PickupMessage (toucher, GOTHISPEED);
		break;

	  case MT_SPREAD:
		if (!P_GiveRune (player, ru_spread))
			return;
		PickupMessage (toucher, GOTSPREAD);
		break;

	  case MT_REFLECTION:
		if (!P_GiveRune (player, ru_reflection))
			return;
		PickupMessage (toucher, GOTREFLECTION);
		break;

	// ammo
	  case MT_CLIP:
		if (!P_GiveAmmo (player,am_clip,1))
			return;
		PickupMessage (toucher, GOTCLIP);
		break;
		
	  case MT_AMMOBOX:
		if (!P_GiveAmmo (player, am_clip,5))
			return;
		PickupMessage (toucher, GOTCLIPBOX);
		break;
		
	  case MT_MISSILE:
		if (!P_GiveAmmo (player, am_misl,1))
			return;
		PickupMessage (toucher, GOTROCKET);
		break;
		
	  case MT_MISSILEBOX:
		if (!P_GiveAmmo (player, am_misl,5))
			return;
		PickupMessage (toucher, GOTROCKBOX);
		break;
		
	  case MT_CELL:
		if (!P_GiveAmmo (player, am_cell,1))
			return;
		PickupMessage (toucher, GOTCELL);
		break;
		
	  case MT_CELLPACK:
		if (!P_GiveAmmo (player, am_cell,5))
			return;
		PickupMessage (toucher, GOTCELLBOX);
		break;
		
	  case MT_SHELLS:
		if (!P_GiveAmmo (player, am_shell,1))
			return;
		PickupMessage (toucher, GOTSHELLS);
		break;
		
	  case MT_SHELLBOX:
		if (!P_GiveAmmo (player, am_shell,5))
			return;
		PickupMessage (toucher, GOTSHELLBOX);
		break;
		
	  case MT_BACKPACK:
		if (!player->backpack)
		{
			for (i=0 ; i<NUMAMMO ; i++)
				player->maxammo[i] *= 2;
			player->backpack = true;
		}
		for (i=0 ; i<NUMAMMO ; i++)
			P_GiveAmmo (player, (ammotype_t)i, 1);
		PickupMessage (toucher, GOTBACKPACK);
		break;
		
		// weapons
	  case MT_PISTOL:	// [BC] Pistol can be an item now
		if (!P_GiveWeapon (player, wp_pistol, 1))
			return;
		PickupMessage (toucher, GOTPISTOL);
		break;

	  case MT_BFG9000:
		if (!P_GiveWeapon (player, wp_bfg, 1))
			return;
		PickupMessage (toucher, GOTBFG9000);
		break;
		
	  case MT_CHAINGUN:
		if (!P_GiveWeapon (player, wp_chaingun, 1))
			return;
		PickupMessage (toucher, GOTCHAINGUN);
		break;
		
	  case MT_CHAINSAW:
		if (!P_GiveWeapon (player, wp_chainsaw, 1))
			return;
		PickupMessage (toucher, GOTCHAINSAW);
		break;
		
	  case MT_ROCKETLAUNCHER:
		if (!P_GiveWeapon (player, wp_missile, 1))
			return;
		PickupMessage (toucher, GOTLAUNCHER);
		break;
		
	  case MT_PLASMAGUN:
		if (!P_GiveWeapon (player, wp_plasma, 1))
			return;
		PickupMessage (toucher, GOTPLASMA);
		break;
		
	  case MT_SHOTGUN:
		if (!P_GiveWeapon (player, wp_shotgun, 1))
			return;
		PickupMessage (toucher, GOTSHOTGUN);
		break;
				
	  case MT_SUPERSHOTGUN:
		if (!P_GiveWeapon (player, wp_supershotgun, 1))
			return;
		PickupMessage (toucher, GOTSHOTGUN2);
		break;

		// [BC] New weapons
	  case MT_GRENADELAUNCHER:
		if (!P_GiveWeapon (player, wp_grenlauncher, 1))
			return;
		PickupMessage (toucher, GOTGRENLAUN);
		break;

	  case MT_RAILGUN:
		if (!P_GiveWeapon (player, wp_railgun, 1))
			return;
		PickupMessage (toucher, GOTRAILGUN);
		break;

	  case MT_BFG10000:
		if (!P_GiveWeapon (player, wp_bfg10k, 1))
			return;
		PickupMessage (toucher, GOTBFG10000);
		break;

	  default:
		I_Error ("P_GiveSpecialThing: Unknown gettable thing: %d", (int)type);
	}	
}

//
// [BC] P_LostSpecialThing
//
// Take a special item away from a player
void P_LostSpecialThing (mobjtype_t type, player_t *player, bool message)
{
	if (!player)
		return;

	AActor *it = player->mo;

	// Identify by mobjtype
	switch (type)
	{
		case MT_BSKU:
			player->cards[it_blueskull] = false;
			if (message)
				PickupMessage (it, LOSTBLUESKULL);
			break;
		case MT_YSKU:
			player->cards[it_yellowskull] = false;
			if (message)
				PickupMessage (it, LOSTYELWSKULL);
			break;
		case MT_RSKU:
			player->cards[it_redskull] = false;
			if (message)
				PickupMessage (it, LOSTREDSKULL);
			break;
		case MT_BKEY:
			it->player->cards[it_bluecard] = false;
			if (message)
				PickupMessage (it, LOSTBLUECARD);
			break;
		case MT_YKEY:
			if (it->player->cards[it_yellowcard]) {
				it->player->cards[it_yellowcard] = false;
				if (message)
					PickupMessage (it, LOSTYELWCARD);
			}
			break;
		case MT_RKEY:
			if (it->player->cards[it_redcard]) {
				it->player->cards[it_redcard] = false;
				if (message)
					PickupMessage (it, LOSTREDCARD);
			}
			break;
		case MT_LSKU:
			if (it->player->cards[it_blackskull]) {
				it->player->cards[it_blackskull] = false;
				if (message)
					PickupMessage (it, LOSTBLACKSKULL);
			}
			break;
		case MT_SSKU:
			if (it->player->cards[it_silverskull]) {
				it->player->cards[it_silverskull] = false;
				if (message)
					PickupMessage (it, LOSTSILVERSKULL);
			}
			break;
		case MT_GSKU:
			if (it->player->cards[it_goldskull]) {
				it->player->cards[it_goldskull] = false;
				if (message)
					PickupMessage (it, LOSTGOLDSKULL);
			}
			break;
		case MT_LKEY:
			if (it->player->cards[it_blackcard])
			{
				it->player->cards[it_blackcard] = false;
				if (message)
					PickupMessage (it, LOSTBLACKCARD);
			}
			break;
		case MT_SKEY:
			if (it->player->cards[it_silvercard]) {
				it->player->cards[it_silvercard] = false;
				if (message)
					PickupMessage (it, LOSTSILVERCARD);
			}
			break;
		case MT_GKEY:
			if (it->player->cards[it_goldcard]) {
				it->player->cards[it_goldcard] = false;
				if (message)
					PickupMessage (it, LOSTGOLDCARD);
			}
			break;
		case MT_BACKPACK:
			if (it->player->backpack)
			{
				it->player->backpack = false;
				if (message)
					PickupMessage (it, LOSTBACKPACK);
			}
			break;
		case MT_PISTOL:
			if (it->player->weaponowned[wp_pistol])
			{
				it->player->weaponowned[wp_pistol] = false;
				if (message)
					PickupMessage (it, LOSTPISTOL);
			}
			break;		
		case MT_CHAINSAW:
			if (it->player->weaponowned[wp_chainsaw])
			{
				it->player->weaponowned[wp_chainsaw] = false;
				if (message)
					PickupMessage (it, LOSTCHAINSAW);
			}
			break;
		case MT_SHOTGUN:
			if (it->player->weaponowned[wp_shotgun]) {
				it->player->weaponowned[wp_shotgun] = false;
				if (message)
					PickupMessage (it, LOSTSHOTGUN);
			}
			break;
		case MT_SUPERSHOTGUN:
			if (it->player->weaponowned[wp_supershotgun]) {
				it->player->weaponowned[wp_supershotgun] = false;
				if (message)
					PickupMessage (it, LOSTSHOTGUN2);
			}
			break;
		case MT_CHAINGUN:
			if (it->player->weaponowned[wp_chaingun]) {
				it->player->weaponowned[wp_chaingun] = false;
				if (message)
					PickupMessage (it, LOSTCHAINGUN);
			}
			break;
		case MT_ROCKETLAUNCHER:
			if (it->player->weaponowned[wp_missile]) {
				it->player->weaponowned[wp_missile] = false;
				if (message)
					PickupMessage (it, LOSTLAUNCHER);
			}
			break;
		case MT_GRENADELAUNCHER:
			if (it->player->weaponowned[wp_grenlauncher]) {
				it->player->weaponowned[wp_grenlauncher] = false;
				if (message)
					PickupMessage (it, LOSTGRENLAUN);
			}
			break;
		case MT_PLASMAGUN:
			if (it->player->weaponowned[wp_plasma]) {
				it->player->weaponowned[wp_plasma] = false;
			if (message)
					PickupMessage (it, LOSTPLASMA);
			}
			break;
		case MT_RAILGUN:
			if (it->player->weaponowned[wp_railgun]) {
				it->player->weaponowned[wp_railgun] = false;
			if (message)
					PickupMessage (it, LOSTRAILGUN);
			}
			break;
		case MT_BFG9000:
			if (it->player->weaponowned[wp_bfg]) {
				it->player->weaponowned[wp_bfg] = false;
			if (message)
					PickupMessage (it, LOSTBFG9000);
			}
			break;

		case MT_INV:
			if (it->player->powers[pw_invulnerability]) {
				it->player->powers[pw_invulnerability] = false;
				if (message)
					PickupMessage (it, LOSTINVUL);
			}
			break;
		case MT_INS:
			if (it->player->powers[pw_invisibility]) {
				it->player->powers[pw_invisibility] = false;
				if (message)
					PickupMessage (it, LOSTINVIS);
			}
			break;
		case MT_TURBOSPHERE:
			if (it->player->powers[pw_turbosphere])
			{
				it->player->powers[pw_turbosphere] = false;
				if (message)
					PickupMessage (it, LOSTTURBO);
			}
			break;
		case MT_LIGHTAMP:
			if (it->player->powers[pw_infrared]) {
				it->player->powers[pw_infrared] = false;
				if (message)
					PickupMessage (it, LOSTVISOR);
			}
			break;
		case MT_RADSUIT:
			if (it->player->powers[pw_ironfeet]) {
				it->player->powers[pw_ironfeet] = false;
				if (message)
					PickupMessage (it, LOSTSUIT);
			}
			break;
		case MT_COMPMAP:
			if (it->player->powers[pw_allmap]) {
				it->player->powers[pw_allmap] = false;
				if (message)
					PickupMessage (it, LOSTMAP);
			}
			break;
		case MT_BERSERK:
			if (it->player->powers[pw_strength]) {
				it->player->powers[pw_strength] = false;
				if (message)
					PickupMessage (it, LOSTBERSERK);
			}
			break;
		case MT_FLOATINGFEET:
			if (it->player->powers[pw_floatingfeet]) {
				it->player->powers[pw_floatingfeet] = false;
				if (message)
					PickupMessage (it, LOSTANTI);
			}
			break;
		case MT_ARMOR:
			if (it->player->armortype == 1) {
				it->player->armortype = 0;
				it->player->armorpoints = 0;
				if (message)
					PickupMessage (it, LOSTARMOR);
			}
			break;
		case MT_MEGAARMOR:
			if (it->player->armortype == 2) {
				it->player->armortype = 0;
				it->player->armorpoints = 0;
				if (message)
					PickupMessage (it, LOSTMEGA);
			}
			break;
		case MT_REDARMOR:
			if (it->player->armortype == 3) {
				it->player->armortype = 0;
				it->player->armorpoints = 0;
				if (message)
					PickupMessage (it, LOSTFIRE);
			}
			break;
		default:
			I_Error("Player_RemoveItem: Unknown item %d.", (int)type);
	}

	if (netstate == NS_SERVER)
	{
	    for (int i=0; i<MAXPLAYERS; i++)
		{
			client_t *cl = &clients[i];

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte (&cl->reliablebuf, svc_lostitem);
			MSG_WriteByte (&cl->reliablebuf, player - players);
			MSG_WriteByte (&cl->reliablebuf, (byte)type);
		}
	}
}


// [RH]
// SexMessage: Replace parts of strings with gender-specific pronouns
//
// The following expansions are performed:
//		%g -> he/she/it
//		%h -> him/her/it
//		%p -> his/her/its
//
void SexMessage (const char *from, char *to, int gender)
{
	static const char *genderstuff[3][3] = {
		{ "he",  "him", "his" },
		{ "she", "her", "her" },
		{ "it",  "it",  "its" }
	};
	static const int gendershift[3][3] = {
		{ 2, 3, 3 },
		{ 3, 3, 3 },
		{ 2, 2, 3 }
	};
	int gendermsg;

	do {
		if (*from != '%') {
			*to++ = *from;
		} else {
			switch (from[1]) {
				case 'g':	gendermsg = 0;	break;
				case 'h':	gendermsg = 1;	break;
				case 'p':	gendermsg = 2;	break;
				default:	gendermsg = -1;	break;
			}
			if (gendermsg < 0) {
				*to++ = '%';
			} else {
				strcpy (to, genderstuff[gender][gendermsg]);
				to += gendershift[gender][gendermsg];
				from++;
			}
		}
	} while (*from++);
}

// [RH]
// ClientObituary: Show a message when a player dies
//
void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker)
{
	int	 mod;
	char *message;
	char gendermessage[1024];
	BOOL friendly;
	BOOL gibbed;	// [BC] Thing got blown up
	state_t *gibframe;
	int  gender;

	if (!self->player)
		return;

	gibframe = &states[mobjinfo[self->type].xdeathstate];

	// [BC] Check if the thing got gibbed or not
	if (self->state == gibframe)
		gibbed = true;
	else
		gibbed = false;

	gender = self->player->userinfo.gender;

	// Treat voodoo dolls as unknown deaths
	if (inflictor && inflictor->player == self->player)
		MeansOfDeath = MOD_UNKNOWN;

	friendly = MeansOfDeath & MOD_FRIENDLY_FIRE;
	mod = MeansOfDeath & ~MOD_FRIENDLY_FIRE;
	message = NULL;

	// [BC] Mix things up a bit
	int rnum = P_Random (pr_obituary)%128;

	switch (mod) {
		case MOD_SUICIDE:
			message = OB_SUICIDE;
			break;
		case MOD_FALLING:
			if (rnum < 64)	// [BC]
				message = OB_FALLING;
			else
				message = OB_FALLING2;
			break;
		case MOD_CRUSH:
			message = OB_CRUSH;
			break;
		case MOD_EXIT:
			if (rnum < 64)	// [BC]
				message = OB_EXIT;
			else
				message = OB_EXIT2;
			break;
		case MOD_WATER:
			message = OB_WATER;
			break;
		case MOD_SLIME:
			message = OB_SLIME;
			break;
		case MOD_LAVA:
			message = OB_LAVA;
			break;
		case MOD_BARREL:
			message = OB_BARREL;
			break;
		case MOD_SPLASH:
			message = OB_SPLASH;
			break;
		case MOD_IMPALE:		// [BC] New MOD's
			message = OB_IMPSTICK;
			break;
		case MOD_SPACE:
			message = OB_SPACE;
			break;
		case MOD_SWITCH:
			message = OB_SWITCH;
			break;
		case MOD_REFLECTION:
			message = OB_REFLECTION;
			break;
	}
	
	if (attacker && !message) {
		if (attacker == self) {
			switch (mod) {
				case MOD_R_SPLASH:
					if (rnum == 100)	// [BC]
						message = OB_R_SPLASHSEC;
					else if (rnum < 42)
						message = OB_R_SPLASH;
					else if (rnum < 84)
						message = OB_R_SPLASH2;
					else
						message = OB_R_SPLASH3;
					break;
				case MOD_G_SPLASH:
					if (rnum == 100)	// [BC]
						message = OB_G_SPLASHSEC;
					else if (rnum < 42)
						message = OB_G_SPLASH;
					else if (rnum < 84)
						message = OB_G_SPLASH2;
					else
						message = OB_G_SPLASH3;
					break;
				case MOD_ROCKET:
					if (rnum == 100)	// [BC]
						message = OB_ROCKETSEC;
					else if (rnum < 42)
						message = OB_ROCKET;
					else if (rnum < 84)
						message = OB_ROCKET2;
					else
						message = OB_ROCKET3;
					break;
				case MOD_GRENADE:			// [BC]
					message = OB_GRENADE;
					break;
				case MOD_RAILGUN:			// [BC]
					message = OB_RAILGUN;
					break;
				default:
					if (rnum < 64)	// [BC]
						message = OB_KILLEDSELF;
					else
						message = OB_KILLEDSELF2;
					break;
			}
		} else if (!attacker->player) {
			switch (attacker->type) {
				case MT_STEALTHBABY:
					message = OB_STEALTHBABY;
					break;
				case MT_STEALTHVILE:
					message = OB_STEALTHVILE;
					break;
				case MT_STEALTHBRUISER:
					message = OB_STEALTHBARON;
					break;
				case MT_STEALTHHEAD:
					message = OB_STEALTHCACO;
					break;
				case MT_STEALTHCHAINGUY:
					message = OB_STEALTHCHAINGUY;
					break;
				case MT_STEALTHSERGEANT:
					message = OB_STEALTHDEMON;
					break;
				case MT_STEALTHKNIGHT:
					message = OB_STEALTHKNIGHT;
					break;
				case MT_STEALTHIMP:
					message = OB_STEALTHIMP;
					break;
				case MT_STEALTHFATSO:
					message = OB_STEALTHFATSO;
					break;
				case MT_STEALTHUNDEAD:
					message = OB_STEALTHUNDEAD;
					break;
				case MT_STEALTHSHOTGUY:
					message = OB_STEALTHSHOTGUY;
					break;
				case MT_STEALTHZOMBIE:
					message = OB_STEALTHZOMBIE;
					break;
				default:
					if (mod == MOD_HIT) {
						switch (attacker->type) {
							case MT_UNDEAD:
								message = OB_UNDEADHIT;
								break;
							case MT_TROOP:
								message = OB_IMPHIT;
								break;
							case MT_DARKIMP:	// [BC]
								message = OB_DARKIMPHIT;
								break;
							case MT_HEAD:
								if (rnum == 100)	// [BC]
									message = OB_CACOHITSEC;
								else
									message = OB_CACOHIT;
								break;
							case MT_CACOLANTERN:	// [BC]
									message = OB_CACOLANTERNHIT;
								break;
							case MT_SERGEANT:
								message = OB_DEMONHIT;
								break;
							case MT_BLOODDEMON:	// [BC]
								message = OB_BLOODDEMONHIT;
								break;
							case MT_SHADOWS:
								message = OB_SPECTREHIT;
								break;
							case MT_BRUISER:
								if (rnum == 100)	// [BC]
									message = OB_BARONHITSEC;
								else
									message = OB_BARONHIT;
								break;
							case MT_KNIGHT:
								message = OB_KNIGHTHIT;
								break;
							default:
								break;
						}
					} else {
						switch (attacker->type) {
							case MT_POSSESSED:
								message = OB_ZOMBIE;
								break;
							case MT_SHOTGUY:
								message = OB_SHOTGUY;
								break;
							case MT_VILE:
								message = OB_VILE;
								break;
							case MT_UNDEAD:
								message = OB_UNDEAD;
								break;
							case MT_FATSO:
								message = OB_FATSO;
								break;
							case MT_HECTEBUS:	// [BC]
								message = OB_HECTEBUS;
								break;
							case MT_CHAINGUY:
								message = OB_CHAINGUY;
								break;
							case MT_SSGGUY:	// [BC]
								message = OB_SSGGUY;
								break;
							case MT_SKULL:
								message = OB_SKULL;
								break;
							case MT_TROOP:
								message = OB_IMP;
								break;
							case MT_DARKIMP:	// [BC]
								message = OB_DARKIMP;
								break;
							case MT_HEAD:
								message = OB_CACO;
								break;
							case MT_CACOLANTERN:	// [BC]
								message = OB_CACOLANTERN;
								break;
							case MT_BRUISER:
								message = OB_BARON;
								break;
							case MT_KNIGHT:
								message = OB_KNIGHT;
								break;
							case MT_SPIDER:
								message = OB_SPIDER;
								break;
							case MT_BABY:
								message = OB_BABY;
								break;
							case MT_CYBER:	// [BC]
								message = OB_CYBER;
								break;
							case MT_WOLFSS:
								message = OB_WOLFSS;
								break;
							default:
								break;
						}
					}
					break;
			}
		}
	}

	if (message) {
		SexMessage (message, gendermessage, gender);
		Printf (PRINT_MEDIUM, "%s %s.\n", self->player->userinfo.netname, gendermessage);
		return;
	}

	if (attacker && attacker->player) {
		if (friendly) {
			// [BC] Put random obituary stuff at top
			attacker->player->fragcount -= 2;
//			if (level.flags & LEVEL_FRAGPOINTSENABLED || fragpoints.value)	// [BC]
//				attacker->player->fragpoints -= 2;
//			attacker->player->frags[attacker->player-players]++;
			self = attacker;
			gender = self->player->userinfo.gender;

			SV_UpdateFrags (attacker->player);

			if (rnum < 32)
				message = OB_FRIENDLY1;
			else if (rnum < 64)
				message = OB_FRIENDLY2;
			else if (rnum < 96)
				message = OB_FRIENDLY3;
			else
				message = OB_FRIENDLY4;
		} else {
			switch (mod) {
				case MOD_FIST:
					if (rnum == 100)	// [BC]
						message = OB_MPFISTSEC;
					else
						message = OB_MPFIST;
					break;
				case MOD_CHAINSAW:
					if (rnum == 100)	// [BC]
						message = OB_MPCHAINSAWSEC;
					else
						message = OB_MPCHAINSAW;
					break;
				case MOD_PISTOL:
					if (rnum == 100)	// [BC]
						message = OB_MPPISTOLSEC;
					else
						message = OB_MPPISTOL;
					break;
				case MOD_SHOTGUN:
					message = OB_MPSHOTGUN;
					break;
				case MOD_SSHOTGUN:
					if (gibbed)
						message = OB_MPSSHOTGUNGIB;
					else
						message = OB_MPSSHOTGUN;
					break;
				case MOD_CHAINGUN:
					message = OB_MPCHAINGUN;
					break;
				case MOD_ROCKET:
					// [BC]
					if (gibbed)
					{
						if (rnum == 100)
							message = OB_MPROCKETGIBSEC;
						else if (rnum < 32)
							message = OB_MPROCKETGIB;
						else if (rnum < 64)
							message = OB_MPROCKETGIB2;
						else if (rnum < 96)
							message = OB_MPROCKETGIB3;
						else
							message = OB_MPROCKETGIB4;
					}
					else
					{
						if (rnum == 100)
							message = OB_MPROCKETSEC;
						else
							message = OB_MPROCKET;
					}
					break;
				case MOD_R_SPLASH:
					message = OB_MPR_SPLASH;
					break;
				case MOD_GRENADE:		// [BC]
					message = OB_MPGRENADE;
					break;
				case MOD_G_SPLASH:		// [BC]
					message = OB_MPG_SPLASH;
					break;
				case MOD_PLASMARIFLE:
					// [BC]
					if (rnum == 100)
						message = OB_MPPLASMARIFLESEC;
					else
						message = OB_MPPLASMARIFLE;
					break;
				case MOD_BFG_BOOM:
					// [BC]
					if (rnum == 100 && gibbed)
						message = OB_MPBFG_BOOMGIBSEC;
					else
						message = OB_MPBFG_BOOM;
					break;
				case MOD_BFG_SPLASH:
					// [BC]
					if (rnum == 100)
						message = OB_MPBFG_SPLASHSEC;
					else
						message = OB_MPBFG_SPLASH;
					break;
				case MOD_BFG10K:
					if (rnum == 100)
						message = OB_BFG10KSEC;
					else
						message = OB_BFG10K;
					break;
				case MOD_TELEFRAG:
					message = OB_MPTELEFRAG;
					break;
				case MOD_RAILGUN:
					message = OB_RAILGUN;
					break;
			}
		}
	}

	if (message)
	{
		char work[256];

		SexMessage (message, gendermessage, gender);
		sprintf (work, "%%s %s\n", gendermessage);
		Printf (PRINT_MEDIUM, work, self->player->userinfo.netname,
				attacker->player->userinfo.netname);
		return;
	}

	SexMessage (OB_DEFAULT, gendermessage, gender);
	Printf (PRINT_MEDIUM, "%s %s.\n", self->player->userinfo.netname, gendermessage);
}

// [BC] P_RemoveCorpses
//
// Removes some dead bodies

EXTERN_CVAR (cl_maxcorpses)

void P_RemoveCorpses (void)
{
	if (!cl_maxcorpses.value)
		return;

	AActor *mo;
	int     corpses = 0;

	TThinkerIterator<AActor> iterator;
	while ( (mo = iterator.Next() ) )
	{
		if (mo->type == MT_PLAYER && (!mo->player || mo->health <=0) )
			corpses++;
	}

	if (corpses > cl_maxcorpses.value)
	{
		TThinkerIterator<AActor> iterator;
		while (corpses > cl_maxcorpses.value && (mo = iterator.Next() ) )
		{
			if (mo->type == MT_PLAYER && !mo->player)
			{
				mo->Destroy();
				corpses--;
			}
		}
	}
}

//
// KillMobj
//
EXTERN_CVAR (fraglimit)

void P_KillMobj (AActor *source, AActor *target, AActor *inflictor, int mod)	// [BC]
{
	AActor *mo;
		
	target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);

	if (target->type != MT_SKULL)
		target->flags &= ~MF_NOGRAVITY;

	target->flags |= MF_CORPSE|MF_DROPOFF;
	target->flags2 &= ~MF2_PASSMOBJ;
	target->flags2 &= ~MF2_REVERSEGRAV;	// [BC]
	target->height >>= 2;

	// [RH] If the thing has a special, execute and remove it
	//		Note that the thing that killed it is considered
	//		the activator of the script.
	if ((target->flags & MF_COUNTKILL) && target->special)
	{
		LineSpecials[target->special] (NULL, source, target->args[0],
									   target->args[1], target->args[2],
									   target->args[3], target->args[4],
									   target->args[5]);	// [BC]
		target->special = 0;
	}
	// [RH] Also set the thing's tid to 0. [why?]
	target->tid = 0;

	if (source && source->player && netstate != NS_CLIENT)
	{
		// count for intermission
		if (target->flags & MF_COUNTKILL)
		{
			source->player->killcount++;
			level.killed_monsters++;
		}

		// Don't count any frags at level start, because they're just telefrags
		// resulting from insufficient deathmatch starts, and it wouldn't be
		// fair to count them toward a player's score.
		if (target->player && level.time)
		{
			// [BC] Bot taunting
			if (source->player->skullb)
			{
				int x = P_Random (pr_botchat)%100;
				skullb_s *bot = source->player->skullb;
	
				if (x < bot->info.chatty)
				{
					x = P_Random (pr_botchat)%15;	// 15 death strings possible
					bot->chatline = &chatinfo[bot->info.chatinfo].frag[x];
					bot->t_chat = bot->info.chattime;
				}
			
				// [BC] Let some anger out
				bot->angerlevel -= 5;
				bot->enemy = NULL;

				// [BC] Don't need to worry about following him anymore
				if (bot->lastpos)
				{
					delete bot->lastpos;
					bot->lastpos = NULL;
				}
			}

			// [BC] Bot stuff
			if (target->player->skullb)
			{
				int x = P_Random (pr_botchat)%100;
				skullb_s *bot = target->player->skullb;

				bot->angerlevel += 10;
				if (bot->angerlevel >= bot->info.threshold)
				{
					if (x < bot->info.pisschance)
					{
						x = P_Random (pr_botchat)%5;	// 5 pissed/frustration strings possible
						bot->chatline = &chatinfo[bot->info.chatinfo].pissed[x];
						bot->t_pissed = 120*TICRATE;
					}
					else
					{
						x = P_Random (pr_botchat)%5;	// 5 pissed/frustration strings possible
						bot->chatline = &chatinfo[bot->info.chatinfo].frustrated[x];
						bot->t_frust = 120*TICRATE;
					}
					bot->t_chat = bot->info.chattime;
					bot->angerlevel = 0;
				}
				else if (x < bot->info.chatty)
				{
					x = P_Random (pr_botchat)%10;	// 10 death strings possible
					bot->chatline = &chatinfo[bot->info.chatinfo].died[x];
					bot->t_chat = bot->info.chattime;
				}

				// If we killed Linguica, see if the caged hissy's around and make him smile!
				if (bot->bottype == BOT_LINGUICA)
				{
					AActor *actor;

					TThinkerIterator<AActor> iterator;

					while (actor = iterator.Next ())
					{
						if (actor->type == MT_CAGEDHISSY)
						{
							if (actor->health)
								P_SetMobjState (actor, S_HISSYHAPPY);
							break;
						}
					}
				}
			}
			
			// [BC] Handle domination medal
			target->player->unanskills = 0;

			source->player->unanskills++;
			if (!(source->player->unanskills%10))
				P_GiveMedal (source->player, md_totaldomination);
			else if (!(source->player->unanskills%5))
				P_GiveMedal (source->player, md_domination);

			// Don't count any frags at level start, because they're just telefrags
			// resulting from insufficient deathmatch starts, and it wouldn't be
			// fair to count them toward a player's score.
//			source->player->frags[target->player - players]++;
			if (target->player == source->player)	// [RH] Cumulative frag count
			{
				source->player->fragcount--;
				if (dmflags2 & DF2_YES_LOSEFRAG || survival.value)	// [BC] Lose two frags
					source->player->fragcount--;
				if (scoremode.value)	// [BC]
					source->player->score -= 5;
			}
			else
			{
				// [BC] 5 points for killing the terminator!
				if (terminator.value && target->player->powers[pw_terminator])
					source->player->fragcount += 5;
				else
					source->player->fragcount++;
				if (scoremode.value)	// [BC]
				{
					int score;

					switch (mod)
					{
					case MOD_FIST:
					case MOD_CHAINSAW:
					case MOD_PISTOL:
						score = 25;
						break;
					case MOD_ROCKET:
					case MOD_RAILGUN:
					case MOD_GRENADE:
					case MOD_SHOTGUN:
					case MOD_CHAINGUN:
					case MOD_BFG_BOOM:
						score = 10;
						break;
					case MOD_SSHOTGUN:
					case MOD_R_SPLASH:
					case MOD_G_SPLASH:
					case MOD_BFG10K:
					case MOD_PLASMARIFLE:
						score = 5;
						break;
					case MOD_BFG_SPLASH:
					default:
						score = 1;
						break;
					}

					source->player->score += score;
					if (source->player->mo == players[consoleplayer].mo)
						Printf(PRINT_HIGH, "You fragged %s! (%i pts.)\n", target->player->userinfo.netname, score);
					if (target->player->mo == players[consoleplayer].mo)
						Printf(PRINT_HIGH, "You were fragged by %s.\n", source->player->userinfo.netname);
					if (scoremode.value)
						target->player->score--;
				}

				if (dmflags2 & DF2_YES_LOSEFRAG || survival.value)	// [BC]
					target->player->fragcount--;
				if (level.flags & LEVEL_FRAGPOINTSENABLED || fragpoints.value)	// [BC]
					if (source->player->team != TM_NONE)
						teams[source->player->team].fragpoints++;

				// [BC] Handle excellent
				if (level.time < source->player->lastkilltime + 2*TICRATE)
				{
					P_GiveMedal (source->player, md_excellent);

					// [BC] Handle incredible
					if (level.time < source->player->lastexcellenttime + 2*TICRATE)
						P_GiveMedal (source->player, md_incredible);

					source->player->lastkilltime = 0;
					source->player->lastexcellenttime = level.time;
				}
				else
					source->player->lastkilltime = level.time;

				// [BC] Handle llama
				if (target->player->chatting || (target->player->icon && target->player->icon->state == &states[S_CHAT]))
					P_GiveMedal (source->player, md_llama);
			}
		}		

		SV_UpdateFrags (source->player);
	}
	else if (netstate == NS_SINGLEPLAYER && (target->flags & MF_COUNTKILL) )
	{
		// count all monster deaths,
		// even those caused by other monsters
		players[0].killcount++;
		level.killed_monsters++;
	}
	
	if (target->player)
	{
		P_RemoveCorpses ();

		// [BC] Increment deaths
		target->player->deaths++;

		// [RH] Force a delay between death and respawn
		target->player->respawn_time = level.time + TICRATE;
		// [BC] Don't respawn quite so fast on
		// forced respawn. It sounds weird when your
		// scream isn't completed.
		if (dmflags & DF_FORCE_RESPAWN)
			target->player->respawn_time += TICRATE/2;

		// count environment kills against you
		if (!source && netstate != NS_CLIENT)
			target->player->fragcount--;	// [RH] Cumulative frag count
				
		target->flags &= ~MF_SOLID;
		target->player->playerstate = PST_DEAD;
		P_DropWeapon (target->player);

		if (target == players[consoleplayer].camera && automapactive)
		{
			// don't die in auto map, switch view prior to dying
			AM_Stop ();
		}

		SV_UpdateFrags (target->player);
	}

	if (target->health <= -target->info->spawnhealth 
		&& target->info->xdeathstate)
	{
		P_SetMobjState (target, target->info->xdeathstate);
	}
	else
		P_SetMobjState (target, target->info->deathstate);

	target->tics -= P_Random (pr_killmobj) & 3;

	if (target->tics < 1)
		target->tics = 1;
				
	// [BC] Kill messages
	if (netstate != NS_SERVER && level.time && !oldschool.value)
	{
		if (target->player && source && source->player && source->player != target->player)
		{
			if (source->player - players == consoleplayer)
				Printf(PRINT_HIGH, "You fragged %s!\n", target->player->userinfo.netname);
			if (target->player - players == consoleplayer)
				Printf(PRINT_HIGH, "You were fragged by %s.\n", source->player->userinfo.netname);
		}
	}

	// [RH] Death messages
	if (target->player && level.time && !oldschool.value)
	{
		P_StartDeathScripts (target);
		ClientObituary (target, inflictor, source);
	}

	if (source && source->player)
	{
		if (netstate != NS_CLIENT)
		{
			// [RH] Implement fraglimit
				if ((teamgame.value || deathmatch.value) && fraglimit.value && !oldschool.value &&	// [BC]
					(int)fraglimit.value <= source->player->fragcount)
				{
					if (netstate == NS_SERVER)
						SV_BroadcastPrintf (PRINT_HIGH, "%s wins!\n", source->player->userinfo.netname);
					else
						Printf (PRINT_HIGH, "%s wins!\n", source->player->userinfo.netname);
					levelTics = 1*TICRATE;
				}
				// [BC] Implement scorelimit
				if (deathmatch.value && scorelimit.value &&
					(int)scorelimit.value <= source->player->score)
				{
					if (netstate == NS_SERVER)
						SV_BroadcastPrintf (PRINT_HIGH, "%s wins!\n", source->player->userinfo.netname);
					else
						Printf (PRINT_HIGH, "%s wins!\n", source->player->userinfo.netname);
					levelTics = 1*TICRATE;
				}
			}
	}

	// Drop stuff.
	// This determines the kind of object spawned
	// during the death frame of a thing.
	mobjtype_t item = (mobjtype_t)0;

	// [BC] Don't drop stuff in client mode
	if (netstate == NS_CLIENT)
	{
		// [BC] ... but still handle terminator icon fixes
		if (terminator.value && target->player->powers[pw_terminator])
		{
			// [BC] Take the terminator away (so that the icon doesn't draw)
			target->player->powers[pw_terminator] = false;
			if (target->player->icon)
			{
				// [BC] It will delete itself
				target->player->icon->Destroy();
				target->player->icon = NULL;
			}
		}
		return;
	}

	switch (target->type)
	{
	case MT_WOLFSS:
	case MT_POSSESSED:
		item = MT_CLIP;
		break;
		
	case MT_SHOTGUY:
		item = MT_SHOTGUN;
		break;
		
	case MT_CHAINGUY:
		item = MT_CHAINGUN;
		break;
		
	//Added by MC: Optional weapon dropping.
    case MT_PLAYER:
		if (dmflags2 & DF2_YES_WEAPONDROP && target->player)	// [BC]
		{
			item = P_WeaponDrop (target->player);
			if (item)
			{
				mo = new AActor (target->x, target->y, ONFLOORZ, item);
				mo->flags |= MF_DROPPED;
			}
		}

		if (terminator.value && target->player->powers[pw_terminator])
		{
			// [BC] Take the terminator away (so that the icon doesn't draw)
			target->player->powers[pw_terminator] = false;
			if (target->player->icon)
			{
				// [BC] It will delete itself
				target->player->icon->Destroy();
				target->player->icon = NULL;
			}
			mo = new AActor (target->x, target->y, ONFLOORZ, MT_TERMINATOR);
			mo->flags |= MF_DROPPED;
		}

		// [BC] Fix me: This can be a lot more efficent
		if (teamgame.value && target->player && target->player->team != TM_NONE)
		{
			teaminfo_s *other;

			if (target->player->team == TM_BLUE)
				other = &teams[TM_RED];
			else
				other = &teams[TM_BLUE];

			if (target->player->cards[other->ourkey])
			{
				target->player->cards[other->ourkey] = false;
				mo = new AActor (target->x, target->y, ONFLOORZ, other->ours);
				mo->flags |= MF_DROPPED;
				if (other->ourkey == it_blueskull)
					mo->tid = 666;
				else
					mo->tid = 667;
				mo->AddToHash ();
				if (dmflags2 & DF2_YES_IRETURN)
					other->returnroutine;
				else if (!(dmflags2 & DF2_YES_RETURN))
					other->returnTics = SKULLTICS;
			}
		}

		if ((dmflags2 & DF2_YES_RUNEDROP  || level.flags & LEVEL_FORCERUNEDROPPING) 
			&& target->player)	// [BC]
		{
			if (target->player->rune == ru_strength) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_STRENGTH);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_rage) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_RAGE);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_drain) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_DRAIN);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_resistance) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_RESISTANCE);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_regeneration) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_REGENERATION);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_prosperity) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_PROSPERITY);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_highjump) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_HIJUMP);
				mo->flags |= MF_DROPPED;
			}
			if (target->player->rune == ru_haste) {
				mo = new AActor (target->x, target->y, ONFLOORZ, MT_HASTE);
				mo->flags |= MF_DROPPED;
			}
		}
		break;

	default:
		return;
	}

	if (!item)
		return; //Happens if bot or player killed when using fists.
	mo = new AActor (target->x, target->y, ONFLOORZ, item);
	mo->flags |= MF_DROPPED;	// special versions of items
}




//
// 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 slime, barrel explosions
// and other environmental stuff.
//
int MeansOfDeath;

void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int mod, int flags)
{
	if (netstate == NS_CLIENT)
		return;

	if (levelTics)
		return;

	unsigned	ang;
	int 		saved;
	player_t*	player;
	fixed_t 	thrust;
	int 		temp;
		
	player = target->player;

	if ( !(target->flags & MF_SHOOTABLE) )
		return; // shouldn't happen...
				
	if (target->health <= 0)
		return;

	// [BC] Handle exceptions up here
	if ((target->flags2 & MF2_INVULNERABLE || target->flags2 & MF2_DORMANT) && !(flags & DMG_NO_INVUL))
		return;
	
	if ((player && (player->cheats & CF_GODMODE || player->powers[pw_invulnerability])) && !(flags & DMG_NO_INVUL))
		return;

	MeansOfDeath = mod;

	// [RH] Andy Baker's Stealth monsters
	if (target->flags & MF_STEALTH)
	{
		target->translucency = FRACUNIT;
		target->visdir = -1;
	}

	if ( target->flags & MF_SKULLFLY )
	{
		target->momx = target->momy = target->momz = 0;
	}
		
	if (player && gameskill.value == sk_baby)
		damage >>= 1;	// take half damage in trainer mode

	// [BC] Handle runes
	if (source && source->player && source->player->rune == ru_strength)
		damage *= 2;

	if (source && source->player && (source->player->rune == ru_drain) && (!player || (player && source != player->mo)))
	{
		damage /= 2;
		P_GiveBody (source->player, damage);
	}

	if (source && source->player &&
		(source->player->powers[pw_doomsphere] || source->player->cheats & CF_HISSY
		|| source->player->powers[pw_terminator]))
		damage *= 4;

	// Some close combat weapons should not
	// inflict thrust and push the victim out of reach,
	// thus kick away unless using the chainsaw.
	if (inflictor
		&& !(target->flags & MF_NOCLIP)
		&& (!source
			|| !source->player
			|| source->player->readyweapon != wp_chainsaw))
	{
		ang = R_PointToAngle2 ( inflictor->x,
								inflictor->y,
								target->x,
								target->y);
				
		thrust = damage*(FRACUNIT>>3)*100/target->info->mass;

		// make fall forwards sometimes
		if ( damage < 40
			 && damage > target->health
			 && target->z - inflictor->z > 64*FRACUNIT
			 && (P_Random (pr_damagemobj)&1) )
		{
			ang += ANG180;
			thrust *= 4;
		}

		ang >>= ANGLETOFINESHIFT;
		target->momx += FixedMul (thrust, finecosine[ang]);
		target->momy += FixedMul (thrust, finesine[ang]);
	}

	// player specific
	if (player)
	{
		// [BC] Handle runes
		if (player->rune == ru_resistance)
			damage /= 2;

		if (player->rune == ru_reflection)
		{
			if (inflictor && inflictor != player->mo)
			{
				damage /= 2;
				P_DamageMobj(source, target, target, damage, MOD_REFLECTION);
			}
		}	

        if (player->skullb)
		{
            player->skullb->allround = true;

			// [BC] 
			if (inflictor && inflictor != player->mo && !player->mo->IsTeammate (inflictor))
			{
				if (!player->skullb->enemy)
				{
					player->skullb->enemy = inflictor;
					player->skullb->t_react = player->skullb->info.reaction * 2;
				}
			}
		}

		// end of game hell hack
		if ((target->subsector->sector->special & 255) == dDamage_End
			&& damage >= target->health)
		{
			damage = target->health - 1;
		}

		// [RH] Avoid friendly fire if enabled
		// [BC] Damage yourself even if friendly fire is off
		if (target->IsTeammate (source) && (target != source))
		{
			if (dmflags & DF_NO_FRIENDLY_FIRE)
				return;
			else
				MeansOfDeath |= MOD_FRIENDLY_FIRE;
		}

		if (player->armortype && !(flags & DMG_NO_ARMOR))
		{
			if (player->armortype == deh.GreenAC)
				saved = damage/3;
			else
				saved = damage/2;
			
			if (player->armorpoints <= saved)
			{
				// armor is used up
				saved = player->armorpoints;
				player->armortype = 0;
			}
			player->armorpoints -= saved;
			damage -= saved;
		}

		if (inflictor)	// [BC] Red armor reduces loss in health only, not armor points
		{
			if (player->armortype == deh.RedAC && inflictor->flags2 & MF2_FIREDAMAGE)
				damage /= 10;
		}
		else 	// [BC] For non-object things that do fire damage
			if ((flags & DMG_FIREDAMAGE || mod == MOD_LAVA) && player->armortype == deh.RedAC)
				damage /= 10;

		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])
			I_Tactile (40,10,40+temp*2);

		if (netstate == NS_SERVER)
		{
	        for (int i=0; i<MAXPLAYERS; i++)
			{
	            client_t *cl = &clients[i];

				if (cl->cls < cs_spawned)
				    continue;
         
				MSG_WriteByte (&cl->reliablebuf, svc_damageplayer);
				MSG_WriteByte (&cl->reliablebuf, player - players);
				MSG_WriteByte (&cl->reliablebuf, player->armorpoints);
	            MSG_WriteShort (&cl->reliablebuf, target->health - damage);
		    }
		}
	}
	
	// do the damage
	target->health -= damage;	
	if (target->health <= 0)
	{
		P_KillMobj (source, target, inflictor, mod);
		if (netstate == NS_SERVER)
		{
			for (int i=0; i<MAXPLAYERS; i++)
	        {
				client_t *cl = &clients[i];
	
				if (cl->cls < cs_spawned)
			        continue;

                MSG_WriteByte (&cl->reliablebuf, svc_killmobj);

	            if (source)
		            MSG_WriteShort (&cl->reliablebuf, source->netid);
			    else
				    MSG_WriteShort (&cl->reliablebuf, 0);

                MSG_WriteShort (&cl->reliablebuf, target->netid);

	            if (inflictor)
		            MSG_WriteShort (&cl->reliablebuf, inflictor->netid);
			    else
				    MSG_WriteShort (&cl->reliablebuf, 0);

				MSG_WriteLong (&cl->reliablebuf, MeansOfDeath);
			}
		}
		return;
	}

	if (!(target->flags2 & MF2_DORMANT))
	{
		// [RH] Only react if not dormant
		if ( (P_Random (pr_damagemobj) < target->info->painchance)
			 && !(target->flags&MF_SKULLFLY) )
		{
			target->flags |= MF_JUSTHIT;	// fight back!
			
			P_SetMobjState (target, target->info->painstate);
		}
							
		target->reactiontime = 0;			// we're awake now...	

		if ( (!target->threshold || target->type == MT_VILE)
			 && source && source != target
			 && source->type != MT_VILE)
		{
			// if not intent on another player, chase after this one

			// killough 2/15/98: remember last enemy, to prevent
			// sleeping early; 2/21/98: Place priority on players

			if (!target->lastenemy || !target->lastenemy->player ||
				 target->lastenemy->health <= 0)
				target->lastenemy = target->target; // remember last enemy - killough

			target->target = source;
			target->threshold = BASETHRESHOLD;
			if (target->state == &states[target->info->spawnstate]
				&& target->info->seestate != S_NULL)
				P_SetMobjState (target, target->info->seestate);
		}
	}
}

BOOL CheckCheatmode (void);

// [BC] Changes and additions
// Kill all the monsters
BEGIN_COMMAND (massacre)
{
	if (CheckCheatmode ())
		return;

	cht_DoCheat (&players[consoleplayer], CHT_MASSACRE);
}
END_COMMAND (massacre)

BEGIN_COMMAND (kill)
{
	if (netstate == NS_SERVER)
		return;

	if (netstate == NS_CLIENT)
	{
		MSG_WriteByte (&net_buffer, clc_suicide);
		MSG_WriteByte (&net_buffer, consoleplayer);
	}
	else
	{
		// Kill the player
		P_Suicide (&players[consoleplayer]);
	}
	C_HideConsole ();
}
END_COMMAND (kill)

BEGIN_COMMAND (fyrestorm)
{
	AddCommandString ("kill");
}
END_COMMAND (fyrestorm)

BEGIN_COMMAND (suicide)
{
	AddCommandString ("kill");
}
END_COMMAND (suicide)
