// 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:
//		Moving object handling. Spawn functions.
//
//-----------------------------------------------------------------------------

// HEADER FILES ------------------------------------------------------------

#include "m_alloc.h"
#include "i_system.h"
#include "z_zone.h"
#include "m_random.h"
#include "doomdef.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "p_effect.h"
#include "st_stuff.h"
#include "hu_stuff.h"
#include "s_sound.h"
#include "doomstat.h"
#include "v_video.h"
#include "c_cvars.h"
#include "b_bot.h"	//Added by MC:
#include "stats.h"
#include "sv_main.h"

// MACROS ------------------------------------------------------------------

#define WATER_SINK_FACTOR		3
#define WATER_SINK_SMALL_FACTOR	4
#define WATER_SINK_SPEED		(FRACUNIT/2)
#define WATER_JUMP_SPEED		(FRACUNIT*7/2)

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

void G_PlayerReborn (int player);

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

extern cycle_t BotSupportCycles;

// PUBLIC DATA DEFINITIONS -------------------------------------------------

CVAR (sv_gravity, "800", CVAR_SERVERINFO)
CVAR (sv_friction, "0.90625", CVAR_SERVERINFO)

fixed_t FloatBobOffsets[64] =
{
	0, 51389, 102283, 152192,
	200636, 247147, 291278, 332604,
	370727, 405280, 435929, 462380,
	484378, 501712, 514213, 521763,
	524287, 521763, 514213, 501712,
	484378, 462380, 435929, 405280,
	370727, 332604, 291278, 247147,
	200636, 152192, 102283, 51389,
	-1, -51390, -102284, -152193,
	-200637, -247148, -291279, -332605,
	-370728, -405281, -435930, -462381,
	-484380, -501713, -514215, -521764,
	-524288, -521764, -514214, -501713,
	-484379, -462381, -435930, -405280,
	-370728, -332605, -291279, -247148,
	-200637, -152193, -102284, -51389
};

// CODE --------------------------------------------------------------------

IMPLEMENT_POINTY_SERIAL (AActor, DThinker)
 DECLARE_POINTER (target)
 DECLARE_POINTER (lastenemy)
 DECLARE_POINTER (tracer)
 DECLARE_POINTER (goal)
END_POINTERS

AActor::~AActor ()
{
	// Please avoid calling the destructor directly (or through delete)!
	// Use Destroy() instead.
}

void AActor::Serialize (FArchive &arc)
{
	Super::Serialize (arc);
	if (arc.IsStoring ())
	{
		arc << x
			<< y
			<< z
			<< pitch
			<< angle
			<< roll
			<< sprite
			<< frame
			<< effects
			<< floorz
			<< ceilingz
			<< radius
			<< height
			<< momx
			<< momy
			<< momz
			<< type
			<< tics
			<< state
			<< damage
			<< flags
			<< flags2
			<< flags3	// [BC]
			<< special1
			<< special2
			<< health
			<< movedir
			<< visdir
			<< movecount
			<< target
			<< lastenemy
			<< reactiontime
			<< threshold
			<< player
			<< lastlook
			<< tracer
			<< tid
			<< special
			<< args[0] << args[1] << args[2] << args[3] << args[4]
			<< goal
			<< (unsigned)0
			<< translucency
			<< waterlevel;

		if (translation)
			arc << (DWORD)(translation - translationtables);
		else
			arc << (DWORD)0xffffffff;
		spawnpoint.Serialize (arc);
	}
	else
	{
		unsigned dummy;
		arc >> x
			>> y
			>> z
			>> pitch
			>> angle
			>> roll
			>> sprite
			>> frame
			>> effects
			>> floorz
			>> ceilingz
			>> radius
			>> height
			>> momx
			>> momy
			>> momz
			>> type
			>> tics
			>> state
			>> damage
			>> flags
			>> flags2
			>> flags3	// [BC]
			>> special1
			>> special2
			>> health
			>> movedir
			>> visdir
			>> movecount
			>> target
			>> lastenemy
			>> reactiontime
			>> threshold
			>> player
			>> lastlook
			>> tracer
			>> tid
			>> special
			>> args[0]
			>> args[1]
			>> args[2]
			>> args[3]
			>> args[4]
			>> goal
			>> dummy
			>> translucency
			>> waterlevel;

		DWORD trans;
		arc >> trans;
		if (trans == (DWORD)0xffffffff)
			translation = NULL;
		else
			translation = translationtables + trans;
		spawnpoint.Serialize (arc);
		info = &mobjinfo[type];
		touching_sectorlist = NULL;
		LinkToWorld ();
		AddToHash ();
	}
}

void MapThing::Serialize (FArchive &arc)
{
	if (arc.IsStoring ())
	{
		arc << thingid << x << y << z << angle << type << flags << special
			<< args[0] << args[1] << args[2] << args[3] << args[4];
	}
	else
	{
		arc >> thingid >> x >> y >> z >> angle >> type >> flags >> special
			>> args[0] >> args[1] >> args[2] >> args[3] >> args[4];
	}
}

AActor::AActor ()
{
	memset (&x, 0, (byte *)&this[1] - (byte *)&x);
}

AActor::AActor (const AActor &other)
{
	memcpy (&x, &other.x, (byte *)&this[1] - (byte *)&x);
}

AActor &AActor::operator= (const AActor &other)
{
	memcpy (&x, &other.x, (byte *)&this[1] - (byte *)&x);
	return *this;
}

//==========================================================================
//
// P_SetMobjState
//
// Returns true if the mobj is still present.
//
//==========================================================================

BOOL P_SetMobjState (AActor *mobj, statenum_t state)
{
	state_t *st;

	// killough 4/9/98: remember states seen, to detect cycles:

	static statenum_t seenstate_tab[NUMSTATES];	// fast transition table
	statenum_t *seenstate = seenstate_tab;		// pointer to table
	static int recursion;						// detects recursion
	statenum_t i = state;						// initial state
	BOOL ret = true;							// return value
	statenum_t tempstate[NUMSTATES];			// for use with recursion

	if (recursion++)							// if recursion detected,
		memset(seenstate=tempstate,0,sizeof tempstate);	// clear state table

	do
	{
		if (state == S_NULL)
		{
			mobj->state = (state_t *) S_NULL;
			mobj->Destroy ();
			ret = false;
			break;					// killough 4/9/98
		}

		st = &states[state];
		mobj->state = st;
		mobj->tics = st->tics;
		if (st->sprite != SPR_PLAY)
			mobj->sprite = st->sprite;	// [RH] Only change sprite if not a player
		mobj->frame = st->frame;

		// Modified handling.
		// Call action functions when the state is set

		if (st->action.acp1)
			st->action.acp1(mobj);

		seenstate[state] = (statenum_t)(1 + st->nextstate);		// killough 4/9/98

		state = st->nextstate;
	} while (!mobj->tics && !seenstate[state]);		// killough 4/9/98

	if (ret && !mobj->tics)  // killough 4/9/98: detect state cycles
		Printf (PRINT_HIGH, "Warning: State Cycle Detected\n");

	if (!--recursion)
		for (;(state=seenstate[i]);i=(statenum_t)(state-1))
			seenstate[i] = S_NULL;  // killough 4/9/98: erase memory of states

	return ret;
}

//----------------------------------------------------------------------------
//
// PROC P_ExplodeMissile
//
//----------------------------------------------------------------------------

void P_ExplodeMissile (AActor* mo)
{
	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_movemobj);
		    MSG_WriteShort (&cl->reliablebuf, mo->netid);
			MSG_WriteLong (&cl->reliablebuf, mo->x);
	        MSG_WriteLong (&cl->reliablebuf, mo->y);
		    MSG_WriteLong (&cl->reliablebuf, mo->z);

			MSG_WriteByte(&cl->reliablebuf, svc_explodemissile);
		    MSG_WriteShort(&cl->reliablebuf, mo->netid);
		}
	}

	mo->momx = mo->momy = mo->momz = 0;

	// [BC] Avoid two explosions for grenades
	if (mo->type == MT_GRENADE)
		mo->special1 = 0;

	P_SetMobjState (mo, mobjinfo[mo->type].deathstate);
	if (mobjinfo[mo->type].deathstate != S_NULL)
	{
		// [RH] If the object is already translucent, don't change it.
		// Otherwise, make it 66% translucent.
		if (mo->translucency == FRACUNIT)
			mo->translucency = TRANSLUC66;

		mo->tics -= P_Random (pr_explodemissile) & 3;

		if (mo->tics < 1)
			mo->tics = 1;

		mo->flags &= ~MF_MISSILE;

		if (mo->info->deathsound)
			S_Sound (mo, CHAN_VOICE, mo->info->deathsound, 1, ATTN_NORM);

		mo->effects = 0;		// [RH]
	}
}

//----------------------------------------------------------------------------
//
// PROC P_FloorBounceMissile
//
//----------------------------------------------------------------------------

void P_FloorBounceMissile (AActor *mo)
{
/*
	if(P_HitFloor(mo) >= FLOOR_LIQUID)
	{
		switch(mo->type)
		{
			case MT_SORCFX1:
			case MT_SORCBALL1:
			case MT_SORCBALL2:
			case MT_SORCBALL3:
				break;
			default:
				mo->Destroy ();
				return;
		}
	}
*/
	switch(mo->type)
	{
		// [BC] Specific for glass shards
		case MT_YSHARD1:
		case MT_YSHARD2:
		case MT_YSHARD3:
		case MT_YSHARD4:
		case MT_BSHARD1:
		case MT_BSHARD2:
		case MT_PSHARD1:
		case MT_PSHARD2:
		case MT_GSHARD1:
		case MT_GSHARD2:
			mo->momz = FixedMul(mo->momz, (fixed_t)(-0.3*FRACUNIT));
			if(abs(mo->momz) < (FRACUNIT/2))
			{
				P_SetMobjState(mo, S_NULL);
				return;
			}
			break;

		// [BC] Grenades are a specific case. They slow down more
		// when they bounce off the ground, since they've been designed
		// around grenade jumping. If they slowed down only as much as
		// the shards do, they just wouldn't work as well. I guess an
		// explanation for why they'd realisticly slow down is cause
		// they're longer than they are wide, so they experience more
		// friction when they hit the ground than they would a wall.
		// Oh well, works for me :)

		case MT_GRENADE:
			mo->momz = FixedMul(mo->momz, (fixed_t)(-0.6*FRACUNIT));
			mo->momx = mo->momx/4;
			mo->momy = mo->momy/4;
			break;		
		default:
			mo->momz = FixedMul(mo->momz, (fixed_t)(-0.3*FRACUNIT));
			if(abs(mo->momz) < (FRACUNIT/2))
			{
				P_SetMobjState(mo, S_NULL);
				return;
			}
	}
	// [BC] Don't use the seesound for grenades. Use a bounce sound.
	if (mo->type == MT_GRENADE)
	{
		S_Sound (mo, CHAN_AUTO, "weapons/grbnce", 1, ATTN_NORM);
	}
	else if(mo->info->seesound)
	{
		S_Sound (mo, CHAN_VOICE, mo->info->seesound, 1, ATTN_IDLE);
	}
	if (!(mo->flags & MF_NOGRAVITY) && (mo->momz < 3*FRACUNIT))
		mo->flags2 &= ~MF2_FLOORBOUNCE;
//	P_SetMobjState(mo, mobjinfo[mo->type].deathstate);
}


//
// P_XYMovement  
//
extern int numspechit;
extern line_t **spechit;

#define STOPSPEED			0x1000
#define FRICTION			0xe800

void P_XYMovement (AActor *mo) 
{
	angle_t angle;
	fixed_t ptryx, ptryy;
	player_t *player;
	fixed_t xmove, ymove;

	if (!mo->momx && !mo->momy)
	{
		if (mo->flags & MF_SKULLFLY)
		{
			// the skull slammed into something
			mo->flags &= ~MF_SKULLFLY;
			mo->momx = mo->momy = mo->momz = 0;

			P_SetMobjState (mo, mo->info->spawnstate);
		}
		return;
	}

	player = mo->player;

	int maxmove = (mo->waterlevel < 2) || (mo->flags & MF_MISSILE) ? MAXMOVE : MAXMOVE/4;

	if (mo->momx > maxmove)
		mo->momx = maxmove;
	else if (mo->momx < -maxmove)
		mo->momx = -maxmove;

	if (mo->momy > maxmove)
		mo->momy = maxmove;
	else if (mo->momy < -maxmove)
		mo->momy = -maxmove;
	
	xmove = mo->momx;
	ymove = mo->momy;

	maxmove /= 2;

	do
	{
		if (xmove > maxmove || ymove > maxmove ||
			xmove < -maxmove || ymove < -maxmove)
		{
			ptryx = mo->x + xmove/2;
			ptryy = mo->y + ymove/2;
			xmove >>= 1;
			ymove >>= 1;
		}
		else
		{
			ptryx = mo->x + xmove;
			ptryy = mo->y + ymove;
			xmove = ymove = 0;
		}

		// killough 3/15/98: Allow objects to drop off
		if (!P_TryMove (mo, ptryx, ptryy, true))
		{
			// blocked move
			// [BC] Instead of sliding if the object has 
			// MF2_SLIDE, just slide it if the object is a player.
			if (mo->player)	// [BC]
			{
				// try to slide along it
				if (BlockingMobj == NULL)
				{ // slide against wall
					if (mo->player && mo->waterlevel && mo->waterlevel < 3
						&& (mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove))
					{
						mo->momz = WATER_JUMP_SPEED;
					}
					P_SlideMove (mo);
				}
				else
				{ // slide against mobj
					if (P_TryMove (mo, mo->x, ptryy, true))
					{
						mo->momx = 0;
					}
					else if (P_TryMove (mo, ptryx, mo->y, true))
					{
						mo->momy = 0;
					}
					else
					{
						mo->momx = mo->momy = 0;
					}
					if (player && player->mo == mo)
					{
						if (mo->momx == 0)
							player->momx = 0;
						if (mo->momy == 0)
							player->momy = 0;
					}
				}
			}
			else if (mo->flags & MF_MISSILE)
			{
//				if (netstate != NS_CLIENT)
//				{
					if (mo->flags2 & MF2_FLOORBOUNCE)
					{
						if (BlockingMobj)
						{
							if (BlockingMobj->flags2 & MF2_REFLECTIVE ||
								!(BlockingMobj->player || BlockingMobj->flags3 & MF3_ENEMY || BlockingMobj->type == MT_BARREL))
							{
								fixed_t speed;

								angle = R_PointToAngle2 (BlockingMobj->x,
									BlockingMobj->y, mo->x, mo->y)
									+ANGLE_1*((P_Random(pr_bounce)%16)-8);
								speed = P_AproxDistance (mo->momx, mo->momy);
								speed = FixedMul (speed, (fixed_t)(0.75*FRACUNIT));
								mo->angle = angle;
								angle >>= ANGLETOFINESHIFT;
								mo->momx = FixedMul (speed, finecosine[angle]);
								mo->momy = FixedMul (speed, finesine[angle]);
								if (mo->type == MT_GRENADE)
									S_Sound (mo, CHAN_AUTO, "weapons/grbnce", 1, ATTN_NORM);
								else if (mo->info->seesound)
									S_Sound (mo, CHAN_VOICE, mo->info->seesound, 1, ATTN_IDLE);
								return;
							}
							else
								P_ExplodeMissile (mo);
						}
						else
						{
							// Struck a wall
							// [BC] Delete if "wall" is a "sky"
							if (ceilingline &&
								ceilingline->backsector &&
								ceilingline->backsector->ceilingpic == skyflatnum &&
								mo->z >= ceilingline->backsector->ceilingheight)
							{
								mo->Destroy ();
								return;
							}
							P_BounceWall (mo);
							if (mo->type == MT_GRENADE)
								S_Sound (mo, CHAN_AUTO, "weapons/grbnce", 1, ATTN_NORM);
							else if (mo->info->seesound)
								S_Sound (mo, CHAN_VOICE, mo->info->seesound, 1, ATTN_IDLE);
							return;
						}
					}
					if(BlockingMobj &&
						(BlockingMobj->flags2 & MF2_REFLECTIVE))
					{
						angle = R_PointToAngle2(BlockingMobj->x,
													BlockingMobj->y,
													mo->x, mo->y);

						// Change angle for delflection/reflection
						angle += ANGLE_1 * ((P_Random(pr_bounce)%16)-8);

						// Reflect the missile along angle
						mo->angle = angle;
						angle >>= ANGLETOFINESHIFT;
						mo->momx = FixedMul(mo->info->speed>>1, finecosine[angle]);
						mo->momy = FixedMul(mo->info->speed>>1, finesine[angle]);
//						mo->momz = -mo->momz;
						mo->target = BlockingMobj;
						return;
					}
					// explode a missile
					if (ceilingline &&
						ceilingline->backsector &&
						ceilingline->backsector->ceilingpic == skyflatnum &&
						mo->z >= ceilingline->backsector->ceilingheight) //killough
					{
						// Hack to prevent missiles exploding against the sky.
						// Does not handle sky floors.
						mo->Destroy ();
						return;
					}
					P_ExplodeMissile (mo);
				//}
			}
			else
			{
				mo->momx = mo->momy = 0;
			}
		}
	} while (xmove || ymove);
	
	// Friction

	if (player && player->mo == mo && player->cheats & CF_NOMOMENTUM)
	{
		// debug option for no sliding at all
		mo->momx = mo->momy = 0;
		player->momx = player->momy = 0;
		return;
	}

	if (mo->flags & (MF_MISSILE | MF_SKULLFLY))
		return; 		// no friction for missiles

	if ((mo->z > mo->floorz) && (mo->z + mo->height < mo->ceilingz) // [BC] Apply friction on ceiling
		&& !(mo->flags2 & MF2_ONMOBJ) && !(mo->flags2 & MF2_FLY)
		&& !mo->waterlevel)
		return;			// no friction when falling

	if (mo->flags & MF_CORPSE)
	{
		// do not stop sliding
		//	if halfway off a step with some momentum
		if (mo->momx > FRACUNIT/4
			|| mo->momx < -FRACUNIT/4
			|| mo->momy > FRACUNIT/4
			|| mo->momy < -FRACUNIT/4)
		{
			if (mo->floorz != mo->subsector->sector->floorheight)
				return;
		}
	}

	// killough 11/98:
	// Stop voodoo dolls that have come to rest, despite any
	// moving corresponding player:
	if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED
		&& mo->momy > -STOPSPEED && mo->momy < STOPSPEED
		&& (!player || (player->mo != mo)
			|| !(player->cmd.ucmd.forwardmove | player->cmd.ucmd.sidemove)))
	{
		// if in a walking frame, stop moving
		// killough 10/98:
		// Don't affect main player when voodoo dolls stop:
		if (player && (unsigned)((player->mo->state - states) - S_PLAY_RUN1) < 4 
			&& (player->mo == mo))
		{
			P_SetMobjState (player->mo, S_PLAY);
		}

		mo->momx = mo->momy = 0;

		// killough 10/98: kill any bobbing momentum too (except in voodoo dolls)
		if (player && player->mo == mo)
			player->momx = player->momy = 0; 
	}
	else
	{
		// phares 3/17/98
		// Friction will have been adjusted by friction thinkers for icy
		// or muddy floors. Otherwise it was never touched and
		// remained set at ORIG_FRICTION
		//
		// killough 8/28/98: removed inefficient thinker algorithm,
		// instead using touching_sectorlist in P_GetFriction() to
		// determine friction (and thus only when it is needed).
		//
		// killough 10/98: changed to work with new bobbing method.
		// Reducing player momentum is no longer needed to reduce
		// bobbing, so ice works much better now.

		fixed_t friction = P_GetFriction (mo, NULL);

		mo->momx = FixedMul (mo->momx, friction);
		mo->momy = FixedMul (mo->momy, friction);

		// killough 10/98: Always decrease player bobbing by ORIG_FRICTION.
		// This prevents problems with bobbing on ice, where it was not being
		// reduced fast enough, leading to all sorts of kludges being developed.

		if (player && player->mo == mo)		//  Not voodoo dolls
		{
			player->momx = FixedMul (player->momx, ORIG_FRICTION);
			player->momy = FixedMul (player->momy, ORIG_FRICTION);
		}
	}
}

//
// P_ZMovement
//
void P_ZMovement (AActor *mo)
{
	fixed_t 	dist;
	fixed_t 	delta;
	
	// check for smooth step up
	if (mo->player && mo->z < mo->floorz)
	{
		mo->player->viewheight -= mo->floorz - mo->z;
		mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3;
	}
	// [BC] check for smooth step down
//	if (mo->player && mo->z+mo->height > mo->ceilingz)
//	{
//		mo->player->viewheight += mo->ceilingz - mo->z+mo->height;
//		mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3;
//	}

	// adjust height
	mo->z += mo->momz;

	if ((mo->flags & MF_FLOAT) && mo->target)
	{
		// float down towards target if too close
		if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
		{
			dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y);
			delta =(mo->target->z + (mo->height>>1)) - mo->z;

			if (delta<0 && dist < -(delta*3) )
				mo->z -= FLOATSPEED;
			else if (delta>0 && dist < (delta*3) )
				mo->z += FLOATSPEED;
		}
	}
	if (mo->player && (mo->flags2 & MF2_FLY) && !(mo->z <= mo->floorz))
	{
		mo->z += finesine[(FINEANGLES/80*level.time)&FINEMASK]/8;
		mo->momz = FixedMul (mo->momz, FRICTION_FLY);
	}
	if (mo->waterlevel && !(mo->flags & MF_NOGRAVITY))
	{
		mo->momz = FixedMul (mo->momz, mo->subsector->sector->friction);
	}

	// clip movement
	if (mo->z <= mo->floorz)
	{
		// hit the floor
		if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
		{
			mo->z = mo->floorz;
			// [BC] If floor is a sky, delete it before checking if it has floorbounce or not
			if (mo->subsector->sector->floorpic == skyflatnum)
			{
				// [RH] Just remove the missile without exploding it
				//		if this is a sky floor.
				mo->Destroy ();
				return;
			}
			if (mo->flags2 & MF2_FLOORBOUNCE)
			{
				P_FloorBounceMissile (mo);
				return;
			}
			else
			{
				P_HitFloor (mo);
				P_ExplodeMissile (mo);
				return;
			}
		}
		// [BC] Handle spring pads
		if (mo->subsector->sector->springpadzone)
		{
			fixed_t momz;

			momz = mo->momz;
			mo->momz = -mo->momz;
//			momz -= (mo->z-mo->floorz);
			mo->z = mo->floorz;
			return;
		}
		/*
		if (mo->flags & MF_COUNTKILL)		// Blasted mobj falling
		{
			if (mo->momz < -(23*FRACUNIT))
			{
				P_MonsterFallingDamage (mo);
			}
		}
		*/
		if (mo->z-mo->momz > mo->floorz)
		{
			// Spawn splashes, etc.
			P_HitFloor (mo);
		}
		mo->z = mo->floorz;
		if (mo->momz < 0)
		{
			if (mo->player)
			{
				// [RH] avoid integer roundoff by doing comparisons with floats
				float minmom = sv_gravity.value * mo->subsector->sector->gravity * -655.36f;
				float mom = (float)mo->momz;

				mo->player->jumpTics = 7;	// delay any jumping for a short while
				if (mom < minmom && !(mo->flags2 & MF2_FLY))
				{
					// Squat down.
					// Decrease viewheight for a moment after hitting the ground (hard),
					// and utter appropriate sound.
					mo->player->deltaviewheight = mo->momz >> 3;
					// [BC] Only grunt once per gametic
					if (netstate == NS_CLIENT)
					{
						if (oldgametic == gametic)
							S_Sound (mo, CHAN_AUTO, "*land1", 1, ATTN_NORM);
					}
					else
						S_Sound (mo, CHAN_AUTO, "*land1", 1, ATTN_NORM);
					
				}
				mo->momz = 0;
			}
		}
		if (mo->flags & MF_SKULLFLY)
		{
			// The skull slammed into something
			mo->momz = -mo->momz;
		}
	}
	else if (mo->z + mo->height > mo->ceilingz)
	{
		// hit the ceiling
		mo->z = mo->ceilingz - mo->height;
		if (mo->subsector->sector->ceilingpic == skyflatnum && mo->flags & MF_MISSILE)	// [BC]
		{
			mo->Destroy ();
			return;
		}
		if (mo->momz)
		{
			if (mo->player && mo->flags2 & MF2_REVERSEGRAV)	// [BC]
			{
				if (mo->subsector->sector->ceilingpic == skyflatnum)
				{
					P_DamageMobj(mo, NULL, NULL, mo->player->health+200, MOD_SPACE, DMG_NO_ARMOR);
					return;
				}
				// [RH] avoid integer roundoff by doing comparisons with floats
				float minmom = sv_gravity.value * mo->subsector->sector->gravity * 655.36f;
				float mom = (float)mo->momz;

				mo->player->jumpTics = 7;	// delay any jumping for a short while
				if (mom < minmom && !(mo->flags2 & MF2_FLY))
				{
					// Squat down.
					// Decrease viewheight for a moment after hitting the ground (hard),
					// and utter appropriate sound.
					mo->player->deltaviewheight = mo->momz >> 3;
					S_Sound (mo, CHAN_AUTO, "*land1", 1, ATTN_NORM);
				}
			}
			mo->momz = 0;
		}
		if (mo->flags2 & MF2_FLOORBOUNCE)
		{
			// reverse momentum here for ceiling bounce
			mo->momz = FixedMul (mo->momz, (fixed_t)(-0.75*FRACUNIT));
			if (mo->type == MT_GRENADE)
				S_Sound (mo, CHAN_VOICE, "weapons/grbnce", 1, ATTN_NORM);
			else if (mo->info->seesound)
				S_Sound (mo, CHAN_BODY, mo->info->seesound, 1, ATTN_IDLE);
			return;
		}
		if (mo->momz > 0)
			mo->momz = 0;
		if (mo->flags & MF_SKULLFLY)
		{
			// the skull slammed into something
			mo->momz = -mo->momz;
		}
		if (mo->flags & MF_MISSILE && !(mo->flags & MF_NOCLIP))
		{
			P_ExplodeMissile (mo);
			return;
		}
	}

	else
	{
		fixed_t startmomz = mo->momz;	// [BC]
		fixed_t newmomz = mo->momz;
// [BC] Rewrote gravity code
		if (!mo->waterlevel || (mo->player &&
			!(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove)))
		{
			if (!(mo->flags2 & MF2_ALTGRAV) && !(mo->flags & MF_NOGRAVITY))
			{
				// [BC] Base gravity
				newmomz = (fixed_t)(sv_gravity.value * mo->subsector->sector->gravity * 81.92);
				if (mo->flags2 & MF2_LOGRAV)
					newmomz /= 4;
				if (mo->momz == 0)
					newmomz *= 2;
				if (mo->flags2 & MF2_REVERSEGRAV)
					mo->momz += newmomz;
				else
					mo->momz -= newmomz;
			}
			else if (mo->flags2 & MF2_ALTGRAV)
			{
				newmomz = (fixed_t)(mo->gravity);
				if (mo->momz == 0)
					newmomz *= 2;
				if (mo->flags2 & MF2_REVERSEGRAV)
					mo->momz += newmomz;
				else
					mo->momz -= newmomz;
			}
		}
		if (mo->waterlevel > 1)
		{
			if (mo->momz < -WATER_SINK_SPEED)
			{
				mo->momz = (startmomz < -WATER_SINK_SPEED) ? startmomz : -WATER_SINK_SPEED;
			}
			else
			{
				mo->momz = startmomz + ((mo->momz - startmomz) >>
					(mo->waterlevel == 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR));
			}
		}
	}
}

//===========================================================================
//
// PlayerLandedOnThing
//
//===========================================================================

static void PlayerLandedOnThing(AActor *mo, AActor *onmobj)
{
// [BC] Impale player if what he lands on has the MF2_IMPALE flag
	if (mo->player)
	{
		if (onmobj->flags2&MF2_IMPALE && !mo->player->powers[pw_invulnerability]
			&& dmflags2 & DF2_YES_IMPALING && (!(mo->player->cheats & CF_GODMODE)))
		{
			mo->flags2 |= MF2_ALTGRAV;
			mo->momx = mo->player->momx = mo->momy = mo->player->momy = mo->momz = 0;
			mo->gravity = 300;
			mo->x = onmobj->x;
			mo->y = onmobj->y;
			mo->player->bob = 0;
			P_DamageMobj(mo, NULL, NULL, mo->player->health+100, MOD_IMPALE, DMG_NO_ARMOR);	// [BC] Cut through armor
		}
		else
		{
			mo->player->deltaviewheight = mo->momz>>3;
			S_Sound (mo, CHAN_AUTO, "*land1", 1, ATTN_IDLE);
			//	mo->player->centering = true;
		}
	}
}




//
// P_NightmareRespawn
//
void P_NightmareRespawn (AActor *mobj)
{
	fixed_t x, y, z;
	AActor *mo;
	mapthing2_t *mthing;

	x = mobj->spawnpoint.x << FRACBITS;
	y = mobj->spawnpoint.y << FRACBITS;
	// something is occupying it's position?
	if (!P_CheckPosition (mobj, x, y)) 
		return;		// no respawn

	// spawn a teleport fog at old spot
	// because of removal of the body?
	mo = new AActor (mobj->x, mobj->y, ONFLOORZ, MT_TFOG);
	// initiate teleport sound
	S_Sound (mo, CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);

	// spawn a teleport fog at the new spot
	mo = new AActor (x, y, ONFLOORZ, MT_TFOG);

	S_Sound (mo, CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);

	// spawn the new monster
	mthing = &mobj->spawnpoint;

	if (mobj->info->flags & MF_SPAWNCEILING)
		z = ONCEILINGZ;
	else if (mobj->info->flags2 & MF2_SPAWNFLOAT)
		z = FLOATRANDZ;
	else if (mobj->info->flags2 & MF2_FLOATBOB)
		z = mthing->z << FRACBITS;
	else
		z = ONFLOORZ;

	// spawn it
	// inherit attributes from deceased one
	mo = new AActor (x, y, ONFLOORZ, mobj->type);
	mo->spawnpoint = mobj->spawnpoint;
	mo->angle = ANG45 * (mthing->angle/45);

	if (z == ONFLOORZ)
		mo->z += mthing->z << FRACBITS;
	else if (z == ONCEILINGZ)
		mo->z -= mthing->z << FRACBITS;

	if (mo->flags2 & MF2_FLOATBOB)
	{ // Seed random starting index for bobbing motion
		mo->health = M_Random();
		mo->special1 = mthing->z << FRACBITS;
	}

	if (mthing->flags & MTF_AMBUSH)
		mo->flags |= MF_AMBUSH;

	mo->reactiontime = 18;

	// remove the old monster,
	mobj->Destroy ();
}


//
// [RH] Some new functions to work with Thing IDs. ------->
//
AActor *AActor::TIDHash[128];

//
// P_ClearTidHashes
//
// Clears the tid hashtable.
//

void AActor::ClearTIDHashes ()
{
	int i;

	for (i = 0; i < 128; i++)
		TIDHash[i] = NULL;
}

//
// P_AddMobjToHash
//
// Inserts an mobj into the correct chain based on its tid.
// If its tid is 0, this function does nothing.
//
void AActor::AddToHash ()
{
	if (tid == 0)
	{
		inext = iprev = NULL;
		return;
	}
	else
	{
		int hash = TIDHASH (tid);

		inext = TIDHash[hash];
		iprev = NULL;
		TIDHash[hash] = this;
	}
}

//
// P_RemoveMobjFromHash
//
// Removes an mobj from its hash chain.
//
void AActor::RemoveFromHash ()
{
	if (tid == 0)
		return;
	else
	{
		if (iprev == NULL)
		{
			// First mobj in the chain (probably)
			int hash = TIDHASH(tid);

			if (TIDHash[hash] == this)
				TIDHash[hash] = inext;
			if (inext)
			{
				inext->iprev = NULL;
				inext = NULL;
			}
		}
		else
		{
			// Not the first mobj in the chain
			iprev->inext = inext;
			if (inext)
			{
				inext->iprev = iprev;
				inext = NULL;
			}
			iprev = NULL;
		}
	}
}

//
// P_FindMobjByTid
//
// Returns the next mobj with the tid after the one given,
// or the first with that tid if no mobj is passed. Returns
// NULL if there are no more.
//
AActor *AActor::FindByTID (int tid) const
{
	return FindByTID (this, tid);
}

AActor *AActor::FindByTID (const AActor *actor, int tid)
{
	// Mobjs without tid are never stored.
	if (tid == 0)
		return NULL;

	if (!actor)
		actor = TIDHash[TIDHASH(tid)];
	else
		actor = actor->inext;

	while (actor && actor->tid != tid)
		actor = actor->inext;

	return const_cast<AActor *>(actor);
}

//
// P_FindGoal
//
// Like FindByTID except it also matches on type.
//
AActor *AActor::FindGoal (int tid, int kind) const
{
	return FindGoal (this, tid, kind);
}

AActor *AActor::FindGoal (const AActor *actor, int tid, int kind)
{
	do
	{
		actor = FindByTID (actor, tid);
	} while (actor && actor->type != kind);

	return const_cast<AActor *>(actor);
}

// <------- [RH] End new functions

//
// P_MobjThinker
//
void AActor::RunThink ()
{
	AActor *onmo;

	// [BC] Freeze mode
	if (freeze && (!player || player->skullb || !(player->cheats & CF_FREEZE || player->powers[pw_freezer])))
		return;

	// [BC]
	if (special1 && type == MT_GRENADE)
	{
		if (! --special1)
			P_ExplodeMissile (this);
	}

	// [BC]
	if (special1 && type == MT_RANDOMPOWERUP)
	{
		if (! --special1)
		{
			statenum_t state;
			int i = P_Random (pr_botchat);
			
			// Include time freezer
			if (!teamgame.value && !deathmatch.value)
				i = i % 7;
			else
				i = i % 6;

			switch (i)
			{
			// Megasphere
			case 0:
				state = S_MEGA;
				break;
			// Invulnerability
			case 1:
				state = S_PINV;
				break;
			// Partial invisibility
			case 2:
				state = S_PINS;
				break;
			// Doomsphere
			case 3:
				state = S_DOOM;
				break;
			// Turbosphere
			case 4:
				state = S_TURB;
				break;
			// Soulsphere
			case 5:
				state = S_SOUL;
				break;
			// Time freezer
			case 6:
				state = S_FREEZE;
				break;
			// Invisibility
			case 7:
				state = S_INVS;
				break;
			}

			P_SetMobjState (this, state);
			special1 = TICRATE/6;
		}
	}

	// [BC]
    if (type == MT_FLOATYICON)
	{
		if (!tracer || !tracer->player
			|| tracer->player->icon != this)
		{
			Destroy();
			return;
		}
		if (special1 && !--special1)
		{
			tracer->player->icon = NULL;
			Destroy ();
			return;
		}
		// We're okay, so move it so that it's above the thing it's tracking.
		SetOrigin (tracer->x, tracer->y, tracer->z + tracer->height+4*FRACUNIT);
	}

	// [RH] Pulse in and out of visibility
	if (effects & FX_VISIBILITYPULSE)
	{
		if (special2 > 0)
		{
			translucency += 0x800;
			if (translucency >= FRACUNIT)
			{
				translucency = FRACUNIT;
				special2 = -1;
			}
		}
		else
		{
			translucency -= 0x800;
			if (translucency <= TRANSLUC25)
			{
				translucency = TRANSLUC25;
				special2 = 1;
			}
		}
	}

	// [RH] Fade a stealth monster in and out of visibility
	if (visdir > 0)
	{
		translucency += 2*FRACUNIT/TICRATE;
		if (translucency > FRACUNIT)
		{
			translucency = FRACUNIT;
			visdir = 0;
		}
	}
	else if (visdir < 0)
	{
		translucency -= 3*FRACUNIT/TICRATE/2;
		if (translucency < 0)
		{
			translucency = 0;
			visdir = 0;
		}
	}

	// Handle X and Y momemtums
	BlockingMobj = NULL;
	if (momx || momy || (flags & MF_SKULLFLY))
	{
		P_XYMovement (this);

		if (ObjectFlags & OF_MassDestruction)
			return;		// actor was destroyed
	}

	if (flags2&MF2_FLOATBOB)
	{ // Floating item bobbing motion (special1 is height)
		// [BC] Smoother movement when standing on bobbing things
		if (onmo = P_CheckOnmobj (this))
		{
			z = floorz + special1 + FloatBobOffsets[(health++)&63];
			if (flags & MF_SOLID)
				onmo->z = z + height;
		}
		else
			z = floorz + special1 + FloatBobOffsets[(health++)&63];
	}
	else if ((z != floorz) || momz || BlockingMobj || ((z+height != ceilingz) && (flags2 & MF2_REVERSEGRAV)))	// [BC]
	{
		// Handle Z momentum and gravity
		if (flags2 & MF2_PASSMOBJ)
		{
			if (!(onmo = P_CheckOnmobj (this)))
			{
				P_ZMovement (this);
				if (player && flags2 & MF2_ONMOBJ)
				{
					flags2 &= ~MF2_ONMOBJ;
				}
			}
			else
			{
				if (player)
				{
					if (momz < (fixed_t)(sv_gravity.value * subsector->sector->gravity * -655.36f)
						&& !(flags2&MF2_FLY))
					{
						PlayerLandedOnThing (this, onmo);
					}
					if (onmo->z + onmo->height - z <= 24 * FRACUNIT)
					{
						// [BC] Why the hell was that there in the first place...
//						player->viewheight -= z + onmo->height - z;
//						player->deltaviewheight =
//							(VIEWHEIGHT - player->viewheight)>>3;
						z = onmo->z + onmo->height;
						flags2 |= MF2_ONMOBJ;
						momz = 0;
					}
					else
					{
						// hit the bottom of the blocking mobj
						momz = 0;
					}
				}
			}
		}
		else
		{
			P_ZMovement (this);
		}

		if (ObjectFlags & OF_MassDestruction)
			return;		// actor was destroyed
	}

	//byte lastwaterlevel = waterlevel;

	waterlevel = 0;
	if (subsector->sector->waterzone)
		waterlevel = 3;
	sector_t *hsec;
	if ( (hsec = subsector->sector->heightsec) )
	{
		if (hsec->waterzone && !subsector->sector->waterzone)
		{
			if (z < hsec->floorheight)
			{
				waterlevel = 1;
				if (z + height/2 < hsec->floorheight)
				{
					waterlevel = 2;
					if (z + height <= hsec->floorheight)
						waterlevel = 3;
				}
			}
			else if (z + height > hsec->ceilingheight)
			{
				waterlevel = 3;
			}
		}
	}

	// [BC] Handle anti-gravity and zero-gravity zones
	if (subsector->sector->antigravityzone)
		flags2 |= MF2_REVERSEGRAV;
	else
	{
		if (!(mobjinfo[type].flags2 & MF2_REVERSEGRAV))
			flags2 &= ~MF2_REVERSEGRAV;
	}

	if (subsector->sector->zerogravityzone)
	{
		flags |= MF_NOGRAVITY;
		flags2 |= MF2_FLY;
	}	
	else
	{
		// [BC] Revert it properly
		if (!(mobjinfo[type].flags & MF_NOGRAVITY))
			flags &= ~MF_NOGRAVITY;
		if (!(mobjinfo[type].flags2 & MF2_FLY))
			flags2 &= ~MF2_FLY;
	}

	if (subsector->sector->returnzone)
	{
		for (int i=0; i<NUMTEAMS; i++)
		{
			if (type == teams[i].ours)
			{
				if (i == TM_BLUE)
					P_StartTeam1ReturnScripts ();
				else
					P_StartTeam2ReturnScripts ();
//				teams[i].returnroutine;
				teams[i].returnTics = 0;
			}
		}
	}

	if (flags2 & MF2_DORMANT)
		return;

	if (player)
	{
		if (player->invulTics)
		{
			player->mo->flags2 |= MF2_INVULNERABLE;
			int x = M_Random() % 3;
			if (x == 2)
			{
				player->mo->translucency = TRANSLUC25;
			}
			else if (x == 1)
			{
				player->mo->translucency = TRANSLUC50;
			}
			else if (x == 0)
			{
				player->mo->translucency = TRANSLUC75;
			}

			if (netstate == NS_CLIENT && player == &players[consoleplayer])
			{
				if (oldgametic == gametic)
					player->invulTics--;
			}
			else
				player->invulTics--;

			if (!player->invulTics)
			{
				player->mo->flags2 &= ~MF2_INVULNERABLE;
				player->mo->translucency = FRACUNIT;
			}
		}
	}

	// cycle through states, calling action functions at transitions
	if (tics != -1)
	{
		// [BC] Only tic down once per gametic
		if (netstate == NS_CLIENT && player && player == &players[consoleplayer])
		{
			if (oldgametic == gametic)
				tics--;
		}
		else
			tics--;

		// [BC] Handle haste rune
		if (player && player->rune == ru_haste)
		{
			if (tics)
				tics--;
		}
				
		// you can cycle through multiple states in a tic
		if (tics <= 0)
			if (!P_SetMobjState (this, state->nextstate) )
				return; 		// freed itself
	}
	else
	{
		// check for nightmare respawn
		if (!(flags & MF_COUNTKILL) || !respawnmonsters)
			return;

		movecount++;

		if (movecount < 12*TICRATE)
			return;

		if (level.time & 31)
			return;

		if (P_Random (pr_mobjthinker) > 4)
			return;

		P_NightmareRespawn (this);
	}
}

//==========================================================================
//
// P_SpawnMobj
//
//==========================================================================

//
// SV_FreeNetid
// [csDoom] Every mobj has its own ID.
//
int SV_FreeNetid(void)
{  
    AActor  *mo;
	int      id = 0;

nextid:
	
	id++;

	if (id > 32767)
		I_Error ("SV_FreeNetid: tried to spawn thing with netid > 32767\n");

	TThinkerIterator<AActor> iterator;
    while ( (mo = iterator.Next() ) )
	{
       if (mo->netid == id)
	       goto nextid;
	}

    return id;
}

AActor::AActor (fixed_t ix, fixed_t iy, fixed_t iz, mobjtype_t itype)
{
	state_t *st;
	fixed_t space;

	if ((unsigned int)itype >= NUMMOBJTYPES)
	{
		I_Error ("Tried to spawn actor type %d\n", itype);
	}

	memset (&x, 0, (byte *)&this[1] - (byte *)&x);
	info = &mobjinfo[itype];
	type = itype;
	x = ix;
	y = iy;
	radius = info->radius;
	height = info->height;
	flags = info->flags;
	flags2 = info->flags2;
	flags3 = info->flags3;	// [BC]
	damage = info->damage;
	health = info->spawnhealth;
	translucency = info->translucency;

	// [RH] Ugly hack:
	switch (type)
	{
	case MT_INS:
		effects = FX_VISIBILITYPULSE;
		special2 = -1;
		break;
	case MT_INVISIBILITY:	// [BC]
		effects = FX_VISIBILITYPULSE;
		special2 = -1;	// [BC] Whatever the hell that does
		break;
	case MT_ROCKET:
		effects = FX_ROCKET;
		break;
	case MT_GRENADE:	// [BC]
		effects = FX_GRENADE;
		break;
	}

	// [BC] Get random powerup started
	if (type == MT_RANDOMPOWERUP)
		special1 = TICRATE/6;

	if (gameskill.value != sk_nightmare)
		reactiontime = info->reactiontime;

	if (netstate == NS_CLIENT)
		lastlook = P_Random (pr_spawnmobj) % MAXPLAYERS;

	// Set the state, but do not use P_SetMobjState, because action
	// routines can't be called yet.  If the spawnstate has an action
	// routine, it will not be called.
	st = &states[info->spawnstate];

	// [BC] Hack to display flags in CTF
	if (teamgame.value)
	{
		if (type == MT_BSKU)
			st = &states[S_BFLAG];
		if (type == MT_RSKU)
			st = &states[S_RFLAG];
	}

	state = st;
	tics = st->tics;
	sprite = st->sprite;
	frame = st->frame;
	touching_sectorlist = NULL;	// NULL head of sector list // phares 3/13/98

	// set subsector and/or block links
	LinkToWorld ();
	floorz = subsector->sector->floorheight;
	ceilingz = subsector->sector->ceilingheight;

	if (iz == ONFLOORZ)
	{
		z = floorz;
	}
	else if (iz == ONCEILINGZ)
	{
		z = ceilingz - height;
	}
	else if (iz == FLOATRANDZ)
	{
		space = ceilingz - info->height - floorz;
		if (space > 48*FRACUNIT)
		{
			space -= 40*FRACUNIT;
			z = ((space * P_Random(pr_botspawn))>>8) + floorz + 40*FRACUNIT;
		}
		else
		{
			z = floorz;
		}
	}
	else if (flags2 & MF2_FLOATBOB)
	{
		z = floorz + iz;		// artifact z passed in as height
	}
	else
	{
		z = iz;
	}

	if (netstate == NS_SERVER)
	{
		int        i;
		client_t  *cl;

		netid = SV_FreeNetid();

		if (itype != MT_PLAYER)
		{
			for (i=0; i<MAXPLAYERS; i++)
			{
				cl = &clients[i];

				if (cl->cls < cs_spawned)
					continue;

				MSG_WriteByte(&cl->reliablebuf, svc_spawnmobj);
				MSG_WriteLong(&cl->reliablebuf, ix);
				MSG_WriteLong(&cl->reliablebuf, iy);
				MSG_WriteLong(&cl->reliablebuf, iz);
				MSG_WriteShort(&cl->reliablebuf, itype);
				MSG_WriteShort(&cl->reliablebuf, netid);
			}
		}
	}
}


//
// P_RemoveMobj
//
mapthing2_t		itemrespawnque[ITEMQUESIZE];
int 			itemrespawntime[ITEMQUESIZE];
int 			iquehead;
int 			iquetail;

void AActor::Destroy ()
{
	if (netstate == NS_SERVER &&
		type != MT_PUFF &&
		(type != MT_PLAYER || health > 0))	// don't tell clients if it removes a dead player
	{
		int i;

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

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte(&cl->reliablebuf, svc_removemobj);
			MSG_WriteShort(&cl->reliablebuf, netid);
		}
	}

	// [BC] Barrel and skull stuff
	if (netstate != NS_CLIENT && !(flags & MF_DROPPED) && !(flags3 & MF3_KEY) && type != MT_TERMINATOR)
	{
		if (flags & MF_SPECIAL || (type == MT_BARREL && dmflags2 & DF2_BARRELS_RESPAWN))
		{
			if ((type != MT_INV && type != MT_INS) || (dmflags & DF_RESPAWN_SUPER))
			{
				itemrespawnque[iquehead] = spawnpoint;
				itemrespawntime[iquehead] = level.time;
				iquehead = (iquehead+1)&(ITEMQUESIZE-1);
			}

			// lose one off the end?
			if (iquehead == iquetail)
				iquetail = (iquetail+1)&(ITEMQUESIZE-1);
		}
	}
		
	// [RH] Unlink from tid chain
	RemoveFromHash ();

	// unlink from sector and block lists
	UnlinkFromWorld ();

	// Delete all nodes on the current sector_list			phares 3/16/98
	if (sector_list)
	{
		P_DelSeclist (sector_list);
		sector_list = NULL;
	}

	// stop any playing sound
	S_RelinkSound (this, NULL);

	Super::Destroy ();
}

//
// P_RespawnSpecials
//
void P_RespawnSpecials (void)
{
	fixed_t 			x;
	fixed_t 			y;
	fixed_t 			z;
	
	AActor* 			mo;
	AActor*				mofog;	// [BC] Fix fog z coordinate problem
	mapthing2_t* 		mthing;
	
	int 				i;

	// only respawn items in deathmatch
	if ((!deathmatch.value && !teamgame.value &&
		!alwaysapplydmflags.value) || !((dmflags & DF_ITEMS_RESPAWN) || oldschool.value))	// [BC]
		return;

	// nothing left to respawn?
	if (iquehead == iquetail)
		return; 		

	// wait at least 30 seconds
	if (level.time - itemrespawntime[iquetail] < 30*TICRATE)
		return; 				

	mthing = &itemrespawnque[iquetail];
		
	x = mthing->x << FRACBITS;
	y = mthing->y << FRACBITS;

	// find which type to spawn
	for (i=0 ; i< NUMMOBJTYPES ; i++)
	{
		if (mthing->type == mobjinfo[i].doomednum)
			break;
	}
	if (mobjinfo[i].flags & MF_SPAWNCEILING)
		z = ONCEILINGZ;
	else if (mobjinfo[i].flags2 & MF2_SPAWNFLOAT)
		z = FLOATRANDZ;
	else if (mobjinfo[i].flags2 & MF2_FLOATBOB)
		z = mthing->z << FRACBITS;
	else
		z = ONFLOORZ;
	
	// spawn a teleport fog at the new spot
	mofog = new AActor (x, y, z, MT_IFOG);
	S_Sound (mofog, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);

	// spawn it
	mo = new AActor (x, y, z, (mobjtype_t)i);
	mo->spawnpoint = *mthing;	
	mo->angle = ANG45 * (mthing->angle/45);

	if (z == ONFLOORZ)
		mo->z += mthing->z << FRACBITS;
	else if (z == ONCEILINGZ)
		mo->z -= mthing->z << FRACBITS;

	mofog->z = mo->z;	// [BC]

	if (netstate == NS_SERVER)
	{
		client_t  *cl;

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

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte(&cl->reliablebuf, svc_updatemobjz);
			MSG_WriteShort(&cl->reliablebuf, mo->netid);
			MSG_WriteLong(&cl->reliablebuf, mo->z);
			MSG_WriteByte(&cl->reliablebuf, svc_updatemobjz);
			MSG_WriteShort(&cl->reliablebuf, mofog->netid);
			MSG_WriteLong(&cl->reliablebuf, mofog->z);
		}
	}

	if (mo->flags2 & MF2_FLOATBOB)
	{ // Seed random starting index for bobbing motion
		mo->health = M_Random();
		mo->special1 = mthing->z << FRACBITS;
	}

	// [RH] Set the thing's special
	// On second thought, don't.
	// [BC] Keep it in team games. Why, I dunno
	if (!teamgame.value)
		mo->special = 0;
	else
	{
		mo->special = mthing->special;
		memcpy (mo->args, mthing->args, sizeof(mo->args));
	}

	// pull it from the que
	iquetail = (iquetail+1)&(ITEMQUESIZE-1);
}




//
// P_SpawnPlayer
// Called when a player is spawned on the level.
// Most of the player structure stays unchanged
//	between levels.
//
EXTERN_CVAR (chasedemo)
extern BOOL demonew;

void P_SpawnPlayer (mapthing2_t *mthing)
{
	int		  playernum;
	player_t *p;
	AActor	 *mobj;
	int 	  i;
	BOOL      entered, reborn;	// [BC] For new scripts
	byte		playerstate;

	// [RH] Things 4001-? are also multiplayer starts. Just like 1-4.
	//		To make things simpler, figure out which player is being
	//		spawned here.
	playernum = (mthing->type > 4000) ? mthing->type - 4001 + 4 : mthing->type - 1;

	// not playing?
	if (playernum >= MAXPLAYERS || !playeringame[playernum])
		return;

	// [BC] Doom only had 4 players...
	if (playernum > 4 && oldschool.value)
		return;

	p = &players[playernum];
// [BC]
	playerstate = p->playerstate;
	entered = reborn = false;

	// [BC] Run "enter" scripts, but don't clear player stats
	if (p->playerstate == PST_ENTER2)
		entered = true;
	if (p->playerstate == PST_ENTER)
	{
		entered = true;
		G_PlayerReborn (playernum);
	}
	if (p->playerstate == PST_REBORN)
	{
		reborn = true;
		G_PlayerReborn (playernum);
	}

	mobj = new AActor (mthing->x << FRACBITS, mthing->y << FRACBITS, ONFLOORZ, MT_PLAYER);

	// set color translations for player sprites
	// [RH] Different now: MF_TRANSLATION is not used.
	mobj->translation = translationtables + 256*playernum;

	mobj->angle = ANG45 * (mthing->angle/45);
	mobj->pitch = mobj->roll = 0;
	mobj->player = p;
	mobj->health = p->health;

	//Added by MC: Identification (number in the players[MAXPLAYERS] array)
    mobj->id = playernum;

	if (oldschool.value)
		p->userinfo.skin = 0;

	// [RH] Set player sprite based on skin
	mobj->sprite = skins[p->userinfo.skin].sprite;

	// [BC] Hack for Doomcrate
	if (p->skullb && (p->skullb->bottype == (bottype_t)BOT_DOOMCRATE))
		mobj->radius = 32*FRACUNIT;

	p->fov = 90.0f;
	p->mo = p->camera = mobj;
	p->playerstate = PST_LIVE;
	p->refire = 0;
	p->damagecount = 0;
	p->bonuscount = 0;
	p->extralight = 0;
	p->mo->fixedcolormap = 0;
	p->viewheight = VIEWHEIGHT;
	p->inconsistant = 0;
	p->attacker = NULL;

	p->momx = p->momy = 0;		// killough 10/98: initialize bobbing to 0.

	players[consoleplayer].camera = players[displayplayer].mo;

	// [RH] Allow chasecam for demo watching
	if ((demoplayback || demonew) && chasedemo.value)
		p->cheats = CF_CHASECAM;

	// setup gun psprite
	P_SetupPsprites (p);

	// give all cards in death match mode
	if (deathmatch.value)
		for (i = 0; i < NUMCARDS; i++)
			p->cards[i] = true;

	if (players[consoleplayer].camera == p->mo)
	{
		// wake up the status bar
		ST_Start ();
	}

	// [RH] If someone is in the way, kill them
	P_TeleportMove (mobj, mobj->x, mobj->y, mobj->z, true);

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

            if (cl->cls < cs_spawned)
                continue;
		
			MSG_WriteByte (&cl->reliablebuf, svc_spawnplayer);
            MSG_WriteByte (&cl->reliablebuf, playernum);
			MSG_WriteByte (&cl->reliablebuf, playerstate);
            MSG_WriteShort (&cl->reliablebuf, mobj->netid);
            MSG_WriteLong (&cl->reliablebuf, mobj->angle);
            MSG_WriteLong (&cl->reliablebuf, mobj->x);
            MSG_WriteLong (&cl->reliablebuf, mobj->y);
            MSG_WriteLong (&cl->reliablebuf, mobj->z);
			MSG_WriteByte (&cl->reliablebuf, (byte)players[playernum].isbot);
        }
	}

	// [BC] Do script stuff
	if (entered)
		P_StartEnterScripts(p->mo);
	if (reborn)
		P_StartRespawnScripts(p->mo);

	// [BC] Build player translation here
	P_BuildColor (p - players);
}


//
// P_SpawnMapThing
// The fields of the mapthing should
// already be in host byte order.
//
// [RH] position is used to weed out unwanted start spots
// [BC] Players are no longer spawned in this function

void P_SpawnMapThing (mapthing2_t *mthing, int position)
{
	int i;
	int bit;
	AActor *mobj;
	fixed_t x, y, z;

	if (mthing->type == 0 || mthing->type == -1)
		return;

	// count deathmatch start positions
	if (mthing->type == 11)
	{
		if (deathmatch_p == &deathmatchstarts[MaxDeathmatchStarts])
		{
			// [RH] Get more deathmatchstarts
			int offset = MaxDeathmatchStarts;
			MaxDeathmatchStarts *= 2;
			deathmatchstarts = (mapthing2_t *)Realloc (deathmatchstarts, MaxDeathmatchStarts * sizeof(mapthing2_t));
			deathmatch_p = &deathmatchstarts[offset];
		}
		memcpy (deathmatch_p, mthing, sizeof(*mthing));
		deathmatch_p++;
		return;
	}
	
	// [BC] Count team starts
	if (mthing->type == 5080)
	{
		if (team1_p == &team1starts[MaxTeam1Starts])
		{
			int offset = MaxTeam1Starts;
			MaxTeam1Starts *= 2;
			team1starts = (mapthing2_t *)Realloc (team1starts, MaxTeam1Starts * sizeof(mapthing2_t));
			team1_p = &team1starts[offset];
		}
		memcpy (team1_p, mthing, sizeof(*mthing));
		team1_p++;
		return;
	}

	if (mthing->type == 5081)
	{
		if (team2_p == &team2starts[MaxTeam2Starts])
		{
			int offset = MaxTeam2Starts;
			MaxTeam2Starts *= 2;
			team2starts = (mapthing2_t *)Realloc (team2starts, MaxTeam2Starts * sizeof(mapthing2_t));
			team2_p = &team2starts[offset];
		}
		memcpy (team2_p, mthing, sizeof(*mthing));
		team2_p++;
		return;
	}

	if (mthing->type == 5082)
	{
		if (temp_p == &tempstarts[MaxTempStarts])
		{
			int offset = MaxTempStarts;
			MaxTempStarts *= 2;
			tempstarts = (mapthing2_t *)Realloc (tempstarts, MaxTempStarts * sizeof(mapthing2_t));
			temp_p = &tempstarts[offset];
		}
		memcpy (temp_p, mthing, sizeof(*mthing));
		temp_p++;
		return;
	}
	if ((mthing->type > 0 && mthing->type < 5) || (mthing->type > 4000 && mthing->type < 4005))
	{
		if (sp_p == &spstarts[16])
		{
			int offset = 16;
			tempstarts = (mapthing2_t *)Realloc (tempstarts, 32 * sizeof(mapthing2_t));
			sp_p = &spstarts[offset];
		}
		memcpy (sp_p, mthing, sizeof(*mthing));
		sp_p++;
//		return;
	}

	// [RH] Record polyobject-related things
	if (HexenHack)
	{
		switch (mthing->type)
		{
		case PO_HEX_ANCHOR_TYPE:
			mthing->type = PO_ANCHOR_TYPE;
			break;
		case PO_HEX_SPAWN_TYPE:
			mthing->type = PO_SPAWN_TYPE;
			break;
		case PO_HEX_SPAWNCRUSH_TYPE:
			mthing->type = PO_SPAWNCRUSH_TYPE;
			break;
		}
	}

	if (mthing->type == PO_ANCHOR_TYPE ||
		mthing->type == PO_SPAWN_TYPE ||
		mthing->type == PO_SPAWNCRUSH_TYPE)
	{
		polyspawns_t *polyspawn = new polyspawns_t;
		polyspawn->next = polyspawns;
		polyspawn->x = mthing->x << FRACBITS;
		polyspawn->y = mthing->y << FRACBITS;
		polyspawn->angle = mthing->angle;
		polyspawn->type = mthing->type;
		polyspawns = polyspawn;
		if (mthing->type != PO_ANCHOR_TYPE)
			po_NumPolyobjs++;
		return;
	}

	// check for players specially
	if (mthing->type <= 4 && mthing->type > 0)
	{
		// [RH] Only spawn spots that match position.
		if (mthing->args[0] != position)
			return;

		// save spots for respawning in network games
		playerstarts[mthing->type-1] = *mthing;
		return;
	}
	else if (mthing->type >= 4001 && mthing->type <= 4001 + MAXPLAYERS - 4)
	{
		// [BC] Heh
		if (oldschool.value)
			return;

		// [RH] Multiplayer starts for players 5-?

		// [RH] Only spawn spots that match position.
		if (mthing->args[0] != position)
			return;

		playerstarts[mthing->type - 4001 + 4] = *mthing;
		return;
	}

	// [RH] sound sequence overrides
	if (mthing->type >= 1400 && mthing->type < 1410)
	{
		R_PointInSubsector (mthing->x<<FRACBITS,
			mthing->y<<FRACBITS)->sector->seqType = mthing->type - 1400;
		return;
	}
	else if (mthing->type == 1411)
	{
		int type;

		if (mthing->args[0] == 255)
			type = -1;
		else
			type = mthing->args[0];

		if (type > 63)
		{
			Printf (PRINT_HIGH, "Sound sequence %d out of range\n", type);
		}
		else
		{
			R_PointInSubsector (mthing->x << FRACBITS,
				mthing->y << FRACBITS)->sector->seqType = type;
		}
		return;
	}

	if (deathmatch.value) 
	{
		if (!(mthing->flags & MTF_DEATHMATCH) && mthing->type != 9001)
		{
			return;
		}
	}
	else if (teamgame.value)
	{
	}
	else if (netstate != NS_SINGLEPLAYER)
	{
		if (!(mthing->flags & MTF_COOPERATIVE) && mthing->type != 9001)
		{
			return;
		}
	}
	else
	{
		if (!(mthing->flags & MTF_SINGLE) && mthing->type != 9001)
		{
			return;
		}
	}

	// check for apropriate skill level
	if (gameskill.value == sk_baby)
		bit = 1;
	else if (gameskill.value == sk_nightmare)
		bit = 4;
	else
		bit = 1 << ((int)gameskill.value - 1);

	if (!(mthing->flags & bit))
		return;
	
	// [RH] Determine if it is an old ambient thing, and if so,
	//		map it to MT_AMBIENT with the proper parameter.
	if (mthing->type >= 14001 && mthing->type <= 14064)
	{
		mthing->args[0] = mthing->type - 14000;
		mthing->type = 14065;
		i = MT_AMBIENT;
	}
	// [RH] Check if it's a particle fountain
	else if (mthing->type >= 9027 && mthing->type <= 9033)
	{
		mthing->args[0] = mthing->type - 9026;
		i = MT_FOUNTAIN;
	}
	else
	{
		// find which type to spawn
		for (i = 0; i < NUMMOBJTYPES; i++)
			if (mthing->type == mobjinfo[i].doomednum)
				break;
	}

	if (i == NUMMOBJTYPES)
	{
		// [RH] Don't die if the map tries to spawn an unknown thing
		Printf (PRINT_HIGH, "Unknown type %i at (%i, %i)\n",
				 mthing->type,
				 mthing->x, mthing->y);
		i = MT_UNKNOWNTHING;
	}
	// [RH] If the thing's corresponding sprite has no frames, also map
	//		it to the unknown thing.
	else if (sprites[states[mobjinfo[i].spawnstate].sprite].numframes == 0)
	{
		Printf (PRINT_HIGH, "Type %i at (%i, %i) has no frames\n",
				mthing->type, mthing->x, mthing->y);
		i = MT_UNKNOWNTHING;
	}

	if (netstate == NS_CLIENT)
	{
		// [BC/csDoom] Don't spawn special items
	    // for client...
		// Type 14 is a teleport exit. We must spawn it here otherwise
		// teleporters won't work well.
		if (mobjinfo[i].flags & MF_SPECIAL && mthing->type != 14)
			return;
	}

	// [BC] Instagib! Whoo!
	if (instagib.value)
	{
		if (mobjinfo[i].flags & MF_SPECIAL && i != MT_RAILGUN)
			return;
	}

	// [RH] don't spawn extra weapons in coop
	if (netstate != NS_SINGLEPLAYER && !deathmatch.value && !teamgame.value)	// [BC]
	{
		switch (i)
		{
		case MT_CHAINGUN:
		case MT_SHOTGUN:
		case MT_SUPERSHOTGUN:
		case MT_BFG9000:
		case MT_CHAINSAW:
		case MT_ROCKETLAUNCHER:
		case MT_PLASMAGUN:
		case MT_GRENADELAUNCHER:	// [BC] New
		case MT_RAILGUN:
		case MT_PISTOL:
			if ((mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH)
				return;
			break;
		default:
			break;
		}
	}
					
	// spawn it
	x = mthing->x << FRACBITS;
	y = mthing->y << FRACBITS;

	if (i == MT_WATERZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->waterzone = 1;
		return;
	}
	else if (i == MT_SECRETTRIGGER)
	{
		level.total_secrets++;
	}
	// [BC] New stuff!
	if (i == MT_ANTIGRAVITYZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->antigravityzone = true;
		return;
	}
	if (i == MT_ZEROGRAVITYZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->zerogravityzone = true;
		return;
	}
	if (i == MT_RETURNZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->returnzone = true;
		return;
	}
	if (i == MT_SPRINGPADZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->springpadzone = true;
		return;
	}
	if (i == MT_SPECIALZONE)
	{
		sector_t *sec = R_PointInSubsector (x, y)->sector;
		sec->SSpecial = mthing->special;
		sec->args[0] = mthing->args[0];
		sec->args[1] = mthing->args[1];
		sec->args[2] = mthing->args[2];
		sec->args[3] = mthing->args[3];
		sec->args[4] = mthing->args[4];
		return;
	}

	if (mobjinfo[i].flags & MF_SPAWNCEILING)
		z = ONCEILINGZ;
	else if (mobjinfo[i].flags2 & MF2_SPAWNFLOAT)
		z = FLOATRANDZ;
	else if (mobjinfo[i].flags2 & MF2_FLOATBOB)
		z = mthing->z << FRACBITS;
	else
		z = ONFLOORZ;
	
	mobj = new AActor (x, y, z, (mobjtype_t)i);

	if (z == ONFLOORZ)
		mobj->z += mthing->z << FRACBITS;
	else if (z == ONCEILINGZ)
		mobj->z -= mthing->z << FRACBITS;
	mobj->spawnpoint = *mthing;

	if (mobj->flags2 & MF2_FLOATBOB)
	{ // Seed random starting index for bobbing motion
		mobj->health = M_Random();
		mobj->special1 = mthing->z << FRACBITS;
	}

	// [RH] Set the thing's special
	mobj->special = mthing->special;
	memcpy (mobj->args, mthing->args, sizeof(mobj->args));

	// [RH] If it's an ambient sound, activate it
	if (i == MT_AMBIENT)
		S_ActivateAmbient (mobj, mobj->args[0]);

	// [RH] If a fountain and not dormant, start it
	if (i == MT_FOUNTAIN && !(mthing->flags & MTF_DORMANT))
		mobj->effects = mobj->args[0] << FX_FOUNTAINSHIFT;

	if (mobj->tics > 0)
		mobj->tics = 1 + (P_Random (pr_spawnmapthing) % mobj->tics);
	if (mobj->flags & MF_COUNTKILL)
		level.total_monsters++;
	if (mobj->flags & MF_COUNTITEM)
		level.total_items++;

	if (i != MT_SPARK)
		mobj->angle = ANG45 * (mthing->angle/45);

	if (mthing->flags & MTF_AMBUSH)
		mobj->flags |= MF_AMBUSH;

	// [RH] Add ThingID to mobj and link it in with the others
	mobj->tid = mthing->thingid;
	mobj->AddToHash ();

	// [RH] Go dormant as needed
	if (mthing->flags & MTF_DORMANT)
		P_DeactivateMobj (mobj);
}



//
// GAME SPAWN FUNCTIONS
//


//
// P_SpawnPuff
//
extern fixed_t attackrange;
CVAR (cl_pufftype, "0", CVAR_ARCHIVE)
CVAR (cl_bloodtype, "0", CVAR_ARCHIVE)

void P_SpawnPuff (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown)
{
	AActor *puff;
	int t;

	t = P_Random (pr_spawnpuff);
	z += (t - P_Random (pr_spawnpuff)) << 10;

	puff = new AActor (x, y, z, MT_PUFF);
	puff->momz = FRACUNIT;
	puff->tics -= P_Random (pr_spawnpuff) & 3;

	if (puff->tics < 1)
		puff->tics = 1;
		
	// don't make punches spark on the wall
	if (attackrange == MELEERANGE)
		P_SetMobjState (puff, S_PUFF3);
	else if (cl_pufftype.value && updown != 3)
	{
		P_DrawSplash2 (32, x, y, z, dir, updown, 1);
		puff->flags2 |= MF2_DONTDRAW;
	}
}

// [BC]
void P_SpawnBFG10KShot (fixed_t x, fixed_t y, fixed_t z, AActor *source)
{
	AActor *mo;

	mo = new AActor (x, y, z, MT_BFG10KSHOT);

	mo->momx = mo->momy = mo->momz = 0;

	if (mo->translucency == FRACUNIT)
		mo->translucency = TRANSLUC66;

	mo->tics -= P_Random (pr_explodemissile) & 3;

	if (mo->tics < 1)
		mo->tics = 1;

	mo->flags &= ~MF_MISSILE;

	if (mo->info->seesound)
		S_Sound (mo, CHAN_VOICE, mo->info->seesound, 1, ATTN_NORM);

	mo->effects = 0;		// [RH]
}

//
// P_SpawnBlood
// 
void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage)
{
	AActor *th;
	int t;

	if (cl_bloodtype.value <= 1) {
		t = P_Random (pr_spawnblood);
		z += (t - P_Random (pr_spawnblood)) << 10;
		th = new AActor (x, y, z, MT_BLOOD);
		th->momz = FRACUNIT*2;
		th->tics -= P_Random (pr_spawnblood) & 3;

		if (th->tics < 1)
			th->tics = 1;
					
		if (damage <= 12 && damage >= 9)
			P_SetMobjState (th, S_BLOOD2);
		else if (damage < 9)
			P_SetMobjState (th, S_BLOOD3);
	}

	if (cl_bloodtype.value >= 1)
		P_DrawSplash2 (32, x, y, z, dir, 2, 0);
}


//---------------------------------------------------------------------------
//
// FUNC P_HitFloor
//
//---------------------------------------------------------------------------
#define SMALLSPLASHCLIP 12<<FRACBITS;

int P_HitFloor(AActor *thing)
{
/*
	AActor *mo;
	int smallsplash=false;

	if(thing->floorz != thing->subsector->sector->floorheight)
	{ // don't splash if landing on the edge above water/lava/etc....
		return(FLOOR_SOLID);
	}

	// Things that don't splash go here
	switch(thing->type)
	{
		case MT_LEAF1:
		case MT_LEAF2:
//		case MT_BLOOD:			// I set these to low mass -- pm
//		case MT_BLOODSPLATTER:
		case MT_SPLASH:
		case MT_SLUDGECHUNK:
			return(FLOOR_SOLID);
		default:
			break;
	}

	// Small splash for small masses
	if (thing->info->mass < 10) smallsplash = true;

	switch(P_GetThingFloorType(thing))
	{
		case FLOOR_WATER:
			if (smallsplash)
			{
				mo=new AActor(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE);
				if (mo) mo->floorclip += SMALLSPLASHCLIP;
				S_StartSound(mo, SFX_AMBIENT10);	// small drip
			}
			else
			{
				mo = new AActor(thing->x, thing->y, ONFLOORZ, MT_SPLASH);
				mo->target = thing;
				mo->momx = (P_Random()-P_Random())<<8;
				mo->momy = (P_Random()-P_Random())<<8;
				mo->momz = 2*FRACUNIT+(P_Random()<<8);
				mo = new AActor(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE);
				if (thing->player) P_NoiseAlert(thing, thing);
				S_StartSound(mo, SFX_WATER_SPLASH);
			}
			return(FLOOR_WATER);
		case FLOOR_LAVA:
			if (smallsplash)
			{
				mo=new AActor(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH);
				if (mo) mo->floorclip += SMALLSPLASHCLIP;
			}
			else
			{
				mo = new AActor(thing->x, thing->y, ONFLOORZ, MT_LAVASMOKE);
				mo->momz = FRACUNIT+(P_Random()<<7);
				mo = new AActor(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH);
				if (thing->player) P_NoiseAlert(thing, thing);
			}
			S_StartSound(mo, SFX_LAVA_SIZZLE);
			if(thing->player && leveltime&31)
			{
				P_DamageMobj(thing, &LavaInflictor, NULL, 5);
			}
			return(FLOOR_LAVA);
		case FLOOR_SLUDGE:
			if (smallsplash)
			{
				mo = new AActor(thing->x, thing->y, ONFLOORZ,
					MT_SLUDGESPLASH);
				if (mo) mo->floorclip += SMALLSPLASHCLIP;
			}
			else
			{
				mo = new AActor(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK);
				mo->target = thing;
				mo->momx = (P_Random()-P_Random())<<8;
				mo->momy = (P_Random()-P_Random())<<8;
				mo->momz = FRACUNIT+(P_Random()<<8);
				mo = new AActor(thing->x, thing->y, ONFLOORZ, 
					MT_SLUDGESPLASH);
				if (thing->player) P_NoiseAlert(thing, thing);
			}
			S_StartSound(mo, SFX_SLUDGE_GLOOP);
			return(FLOOR_SLUDGE);
	}
	return(FLOOR_SOLID);
*/
	return 0;
}

//
// P_CheckMissileSpawn
// Moves the missile forward a bit
//	and possibly explodes it right there.
//
BOOL P_CheckMissileSpawn (AActor* th)
{
	th->tics -= P_Random (pr_checkmissilespawn) & 3;
	if (th->tics < 1)
		th->tics = 1;

	// move a little forward so an angle can
	// be computed if it immediately explodes
	th->x += th->momx>>1;
	th->y += th->momy>>1;
	th->z += th->momz>>1;

	// killough 3/15/98: no dropoff (really = don't care for missiles)

	if (!P_TryMove (th, th->x, th->y, false))
	{
		if (netstate == NS_CLIENT)
			Printf (PRINT_HIGH, "Checkspawn: Blew up\n");
		P_ExplodeMissile (th);
		return false;
	}
	return true;
}


//
// P_SpawnMissile
//
AActor *P_SpawnMissile (AActor *source, AActor *dest, mobjtype_t type)
{
	AActor *th = new AActor (source->x, source->y, source->z + 4*8*FRACUNIT, type);
	
	if (th->info->seesound)
		S_Sound (th, CHAN_VOICE, th->info->seesound, 1, ATTN_NORM);

	th->target = source;		// where it came from

	vec3_t velocity;
	float speed = FIXED2FLOAT (th->info->speed);

	velocity[0] = FIXED2FLOAT(dest->x - source->x);
	velocity[1] = FIXED2FLOAT(dest->y - source->y);
	velocity[2] = FIXED2FLOAT(dest->z - source->z);
	VectorNormalize (velocity);
	th->momx = FLOAT2FIXED(velocity[0] * speed);
	th->momy = FLOAT2FIXED(velocity[1] * speed);
	th->momz = FLOAT2FIXED(velocity[2] * speed);

	// fuzzy player: rotate velocity vector in 2D
	if (dest->flags & MF_SHADOW)
	{
		int t = P_Random (pr_spawnmissile);
		angle_t an = (t - P_Random (pr_spawnmissile)) << 20;
		an >>= ANGLETOFINESHIFT;
		
		fixed_t newx = FixedMul (th->momx, finecosine[an]) - FixedMul (th->momy, finesine[an]);
		fixed_t newy = FixedMul (th->momx, finesine[an]) + FixedMul (th->momy, finecosine[an]);
		th->momx = newx;
		th->momy = newy;
	}

	th->angle = R_PointToAngle2 (0, 0, th->momx, th->momy);

	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_missilespeedangle);
            MSG_WriteShort(&cl->reliablebuf, th->netid);
            MSG_WriteLong (&cl->reliablebuf, th->angle);
            MSG_WriteLong (&cl->reliablebuf, th->momx);
            MSG_WriteLong (&cl->reliablebuf, th->momy);
            MSG_WriteLong (&cl->reliablebuf, th->momz);
        }
	}

    P_CheckMissileSpawn (th);

	return th;
}


//
// P_SpawnPlayerMissile
// Tries to aim at a nearby monster
//
void P_SpawnPlayerMissile (AActor *source, mobjtype_t type)
{
	if (netstate == NS_CLIENT)
		return;

	angle_t an;
	fixed_t slope;
	fixed_t pitchslope = finetangent[FINEANGLES/4-(source->pitch>>ANGLETOFINESHIFT)];

	for (int i=0; i<3; i++)
	{
		// [BC] Spawn multiple missiles if player has the spread rune
		if (i && source->player && !(source->player->rune == ru_spread))
			return;

		bool spreaded = source->player->rune == ru_spread;

		// see which target is to be aimed at
		if (i == 0)
			an = source->angle;
		if (i == 1)
			an = source->angle + ANGLE_45/3;
		if (i == 2)
			an = source->angle - ANGLE_45/3;

		if (source->player->userinfo.aimdist == 0 && !(dmflags & DF_NO_FREELOOK) && !oldschool.value)
			slope = pitchslope;
		else
		{
			slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);

			if (!linetarget)
			{
				an += 1<<26;
				slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);
	
				if (!linetarget)
				{
					an -= 2<<26;
					slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);
				}

				if (!linetarget)
				{
					an = source->angle;
					// [RH] Use pitch to calculate slope instead of 0.
					slope = pitchslope;
				}
			}

			if (linetarget && source->player)
			{
				if (!(dmflags & DF_NO_FREELOOK) && !oldschool.value
					&& abs(slope - pitchslope) > source->player->userinfo.aimdist)
				{
					an = source->angle;
					slope = pitchslope;
				}
			}
		}
		// [BC] Had to move this out here
		AActor *th = new AActor (source->x, source->y, source->z + 3*8*FRACUNIT, type);

		// [BC] special1 with grenades is detonation time
		if (th->type == MT_GRENADE)
			th->special1 = GRENADETICS;

		// [BC] Don't play sounds more than once
		if (!i)
			if (th->info->seesound)
				S_Sound (th, CHAN_VOICE, th->info->seesound, 1, ATTN_NORM);

		th->target = source;
		th->angle = an;

		vec3_t velocity;
		float speed = FIXED2FLOAT (th->info->speed);

		velocity[0] = FIXED2FLOAT (finecosine[an>>ANGLETOFINESHIFT]);
		velocity[1] = FIXED2FLOAT (finesine[an>>ANGLETOFINESHIFT]);
		velocity[2] = FIXED2FLOAT (slope);

		VectorNormalize (velocity);

		th->momx = FLOAT2FIXED (velocity[0] * speed);
		th->momy = FLOAT2FIXED (velocity[1] * speed);
		th->momz = FLOAT2FIXED (velocity[2] * speed);

		// [BC] Arc grenades up a bit.
		if (type == MT_GRENADE)
			th->momz += 3*FRACUNIT;

		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_missilespeedangle);
				MSG_WriteShort(&cl->reliablebuf, th->netid);
	            MSG_WriteLong (&cl->reliablebuf, th->angle);
		        MSG_WriteLong (&cl->reliablebuf, th->momx);
			    MSG_WriteLong (&cl->reliablebuf, th->momy);
				MSG_WriteLong (&cl->reliablebuf, th->momz);
	        }
	
			P_CheckMissileSpawn (th);
		}
		else
		{
			static fixed_t x;
			static fixed_t y;
			static fixed_t z;

			if (!i)
			{
				x = th->x;
				y = th->y;
				z = th->z;
			}
			else
			{
				th->x = x;
				th->y = y;
				th->z = z;
			}

			P_CheckMissileSpawn (th);
		}
	}
}

// [RH] Throw gibs around (based on Q2 code)

#if 0	// Not used right now (Note that this code considers vectors to
		// be expressed as fixed_t's.)
void VelocityForDamage (int damage, vec3_t v)
{
	v[0] = 8 * crandom(pr_vel4dmg);
	v[1] = 8 * crandom(pr_vel4dmg);
	v[2] = 8 * FRACUNIT + (fixed_t)(P_Random(pr_vel4dmg) * (6553600/256));

	if (damage < 50)
		VectorScale (v, 0.7f, v);
	else 
		VectorScale (v, 1.2f, v);
}

void ClipGibVelocity (AActor *ent)
{
	if (ent->momx < -15 * FRACUNIT)
		ent->momx = -15 * FRACUNIT;
	else if (ent->momx > 15 * FRACUNIT)
		ent->momx = 15 * FRACUNIT;
	if (ent->momy < -15 * FRACUNIT)
		ent->momy = -15 * FRACUNIT;
	else if (ent->momy > 15 * FRACUNIT)
		ent->momy = 15 * FRACUNIT;
	if (ent->momz < 6 * FRACUNIT)
		ent->momz = 6 * FRACUNIT;	// always some upwards
	else if (ent->momz > 10 * FRACUNIT)
		ent->momz = 10 * FRACUNIT;
}

void ThrowGib (AActor *self, mobjtype_t gibtype, int damage)
{
	AActor *gib;
	vec3_t	vd;
	vec3_t	selfvel;
	vec3_t	gibvel;

	gib = new AActor (self->x + crandom(pr_throwgib) * (self->radius >> (FRACBITS + 1)),
					  self->y + crandom(pr_throwgib) * (self->radius >> (FRACBITS + 1)),
					  self->z + (self->height >> 1) + crandom(pr_throwgib) * (self->height >> (FRACBITS + 1)),
					  gibtype);

	VelocityForDamage (damage, vd);
	selfvel[0] = self->momx;
	selfvel[1] = self->momy;
	selfvel[2] = self->momz;
	VectorMA (selfvel, 1.0, vd, gibvel);
	gib->momx = gibvel[0];
	gib->momy = gibvel[1];
	gib->momz = gibvel[2];
	ClipGibVelocity (gib);
}
#else
void ThrowGib (AActor *self, mobjtype_t gibtype, int damage)
{
}
#endif

bool AActor::IsTeammate (AActor *other)
{
	if (!player || !other || !other->player)
		return false;
	if (teamgame.value)	// [BC]
	{
		if (!player->team || !other->player->team)
			return false;

		if (player->team == TM_NONE || other->player->team == TM_NONE)
			return false;
		
		if (other->player->team != player->team)
			return false;

		return true;
	}
	if (!deathmatch.value)
		return true;
	if (teamplay.value && other->player->userinfo.team[0] &&
		!stricmp (player->userinfo.team, other->player->userinfo.team))
	{
		return true;
	}
	return false;
}