Source to src/p_mobj.c


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

/* P_mobj.c */

#include "doomdef.h"
#include "p_local.h"
#include "sounds.h"

void G_PlayerReborn (int player);

#define		ITEMQUESIZE	32
mapthing_t	itemrespawnque[ITEMQUESIZE];
int			itemrespawntime[ITEMQUESIZE];
int			iquehead, iquetail;

/*
===============
=
= P_RemoveMobj
=
===============
*/

void P_RemoveMobj (mobj_t *mobj)
{
#ifndef MARS
	int		spot;
	
/* add to the respawnque for altdeath mode */

	if ((mobj->flags & MF_SPECIAL) &&
		!(mobj->flags & MF_DROPPED) &&
		(mobj->type != MT_INV) &&
		(mobj->type != MT_INS))
	{
		spot = iquehead&(ITEMQUESIZE-1);

		itemrespawnque[spot].x = mobj->spawnx;
		itemrespawnque[spot].y = mobj->spawny;
		itemrespawnque[spot].type = mobj->spawntype;
		itemrespawnque[spot].angle = mobj->spawnangle;
		itemrespawntime[spot] = ticon;
		iquehead++;
	}

#endif

/* unlink from sector and block lists */
	P_UnsetThingPosition (mobj);

/* unlink from mobj list */
	mobj->next->prev = mobj->prev;
	mobj->prev->next = mobj->next;
	Z_Free (mobj);
}


/*
===============
=
= P_RespawnSpecials
=
===============
*/

void P_RespawnSpecials (void)
{
#ifndef MARS
	fixed_t         x,y,z; 
	subsector_t 	*ss; 
	mobj_t			*mo;
	mapthing_t		*mthing;
	int				i;
	int				spot;
	
	if (netgame != gt_deathmatch)
		return;
		
	if (iquehead == iquetail)
		return;			/* nothing left to respawn */

	if (iquehead - iquetail > ITEMQUESIZE)
		iquetail = iquehead - ITEMQUESIZE;	/* loose some off the que */
		
	spot = iquetail&(ITEMQUESIZE-1);
	
	if (ticon - itemrespawntime[spot] < 30*15)
		return;			/* wait at least 30 seconds */

	mthing = &itemrespawnque[spot];
	
	x = mthing->x << FRACBITS; 
	y = mthing->y << FRACBITS; 
	  
/* spawn a teleport fog at the new spot */
	ss = R_PointInSubsector (x,y); 
	mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); 

	S_StartSound (mo, sfx_itmbk);

/* find which type to spawn */
	for (i=0 ; i< NUMMOBJTYPES ; i++)
		if (mthing->type == mobjinfo[i].doomednum)
			break;

/* spawn it */
	if (mobjinfo[i].flags & MF_SPAWNCEILING)
		z = ONCEILINGZ;
	else
		z = ONFLOORZ;
	mo = P_SpawnMobj (x,y,z, i);
	mo->angle = ANG45 * (mthing->angle/45);
	mo->spawnx = mthing->x;
	mo->spawny = mthing->y;
	mo->spawntype = mthing->type;
	mo->spawnangle = mthing->angle;
	
/* pull it from the que */
	iquetail++;
#endif
}
	

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

boolean P_SetMobjState (mobj_t *mobj, statenum_t state)
{
	state_t	*st;
	
	if (state == S_NULL)
	{
		mobj->state = S_NULL;
		P_RemoveMobj (mobj);
		return false;
	}
	
	st = &states[state];

	mobj->state = st;
	mobj->tics = st->tics;
	mobj->sprite = st->sprite;
	mobj->frame = st->frame;

	if (st->action)		/* call action functions when the state is set */
		st->action (mobj);	

	mobj->latecall = NULL;	/* make sure it doesn't come back to life... */
	
	return true;
}

/* 
=================== 
= 
= P_ExplodeMissile  
=
=================== 
*/ 

void P_ExplodeMissile (mobj_t *mo)
{
	mo->momx = mo->momy = mo->momz = 0;
	P_SetMobjState (mo, mobjinfo[mo->type].deathstate);
	mo->tics -= P_Random()&1;
	if (mo->tics < 1)
		mo->tics = 1;
	mo->flags &= ~MF_MISSILE;
	if (mo->info->deathsound)
		S_StartSound (mo, mo->info->deathsound);
}


/*
===============
=
= P_SpawnMobj
=
===============
*/

int zonetics;

mobj_t *P_SpawnMobj (fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
{
	mobj_t		*mobj;
	state_t		*st;
	mobjinfo_t	*info;
	
	mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);

	D_memset (mobj, 0, sizeof (*mobj));
	info = &mobjinfo[type];
	
	mobj->type = type;
	mobj->info = info;
	mobj->x = x;
	mobj->y = y;
	mobj->radius = info->radius;
	mobj->height = info->height;
	mobj->flags = info->flags;
	mobj->health = info->spawnhealth;
	mobj->reactiontime = info->reactiontime;
	
/* do not set the state with P_SetMobjState, because action routines can't */
/* be called yet */
	st = &states[info->spawnstate];

	mobj->state = st;
	mobj->tics = st->tics;
	mobj->sprite = st->sprite;
	mobj->frame = st->frame;

/* set subsector and/or block links */
	P_SetThingPosition (mobj);
	
	mobj->floorz = mobj->subsector->sector->floorheight;
	mobj->ceilingz = mobj->subsector->sector->ceilingheight;
	if (z == ONFLOORZ)
		mobj->z = mobj->floorz;
	else if (z == ONCEILINGZ)
		mobj->z = mobj->ceilingz - mobj->info->height;
	else 
		mobj->z = z;
	
/* */
/* link into the mobj list */
/* */
	mobjhead.prev->next = mobj;
	mobj->next = &mobjhead;
	mobj->prev = mobjhead.prev;
	mobjhead.prev = mobj;

	return mobj;
}


/*============================================================================= */


/*
============
=
= P_SpawnPlayer
=
= Called when a player is spawned on the level 
= Most of the player structure stays unchanged between levels
============
*/

void P_SpawnPlayer (mapthing_t *mthing)
{
	player_t	*p;
	fixed_t		x,y,z;
	mobj_t		*mobj;
	int	i;

	if (!playeringame[mthing->type-1])
		return;						/* not playing */
		
	p = &players[mthing->type-1];

	if (p->playerstate == PST_REBORN)
		G_PlayerReborn (mthing->type-1);

	x = mthing->x << FRACBITS;
	y = mthing->y << FRACBITS;
#if 0
if (mthing->type==1)
{
x = 0xffb00000;
y = 0xff500000;
}
#endif
	z = ONFLOORZ;
	mobj = P_SpawnMobj (x,y,z, MT_PLAYER);
	
	mobj->angle = ANG45 * (mthing->angle/45);

	mobj->player = p;
	mobj->health = p->health;
	p->mo = mobj;
	p->playerstate = PST_LIVE;	
	p->refire = 0;
	p->message = NULL;
	p->damagecount = 0;
	p->bonuscount = 0;
	p->extralight = 0;
	p->fixedcolormap = 0;
	p->viewheight = VIEWHEIGHT;
	P_SetupPsprites (p);		/* setup gun psprite	 */
	
	if (netgame == gt_deathmatch)
		for (i=0 ; i<NUMCARDS ; i++)
			p->cards[i] = true;		/* give all cards in death match mode			 */
}



/*
=================
=
= P_SpawnMapThing
=
= The fields of the mapthing should already be in host byte order
==================
*/

void P_SpawnMapThing (mapthing_t *mthing)
{
	int			i, bit;
	mobj_t		*mobj;
	fixed_t		x,y,z;
		
/* count deathmatch start positions */
	if (mthing->type == 11)
	{
		if (deathmatch_p < &deathmatchstarts[10])
			D_memcpy (deathmatch_p, mthing, sizeof(*mthing));
		deathmatch_p++;
		return;
	}
	
/* check for players specially */

#if 0
if (mthing->type > 4)
return;	/*DEBUG */
#endif

	if (mthing->type <= 4)
	{
		/* save spots for respawning in network games */
		if (mthing->type <= MAXPLAYERS)
		{
			playerstarts[mthing->type-1] = *mthing;
			if (netgame != gt_deathmatch)
				P_SpawnPlayer (mthing);
		}
		return;
	}

/* check for apropriate skill level */
	if ( (netgame != gt_deathmatch) && (mthing->options & 16) )
		return;
		
	if (gameskill == sk_baby)
		bit = 1;
	else if (gameskill == sk_nightmare)
		bit = 4;
	else
		bit = 1<<(gameskill-1);
	if (!(mthing->options & bit) )
		return;
	
#ifdef MARS
/* hack player corpses into something else, because player graphics */
/* aren't included */
	if (mthing->type == 10 || mthing->type == 12)	/* player corpse */
		mthing->type = 18;		/* possessed human corpse */
#endif


/* find which type to spawn */
	for (i=0 ; i< NUMMOBJTYPES ; i++)
		if (mthing->type == mobjinfo[i].doomednum)
			break;
	
	if (i==NUMMOBJTYPES)
		I_Error ("P_SpawnMapThing: Unknown type %i at (%i, %i)",mthing->type
		, mthing->x, mthing->y);


		
/* don't spawn keycards and players in deathmatch */
	if (netgame == gt_deathmatch && mobjinfo[i].flags & (MF_NOTDMATCH|MF_COUNTKILL) )
		return;
		
	
/* spawn it */

	x = mthing->x << FRACBITS;
	y = mthing->y << FRACBITS;
	if (mobjinfo[i].flags & MF_SPAWNCEILING)
		z = ONCEILINGZ;
	else
		z = ONFLOORZ;
	mobj = P_SpawnMobj (x,y,z, i);
	if (mobj->tics > 0)
		mobj->tics = 1 + (P_Random () % mobj->tics);
	if (mobj->flags & MF_COUNTKILL)
		totalkills++;
	if (mobj->flags & MF_COUNTITEM)
		totalitems++;
		
	mobj->angle = ANG45 * (mthing->angle/45);
	if (mthing->options & MTF_AMBUSH)
		mobj->flags |= MF_AMBUSH;
		
	mobj->spawnx = mthing->x;
	mobj->spawny = mthing->y;
	mobj->spawntype = mthing->type;
	mobj->spawnangle = mthing->angle;
}


/*
===============================================================================

						GAME SPAWN FUNCTIONS

===============================================================================
*/

/*
================
=
= P_SpawnPuff
=
================
*/

extern fixed_t attackrange;

void P_SpawnPuff (fixed_t x, fixed_t y, fixed_t z)
{
	mobj_t	*th;
	
	z += ((P_Random()-P_Random())<<10);
	th = P_SpawnMobj (x,y,z, MT_PUFF);
	th->momz = FRACUNIT;
	th->tics -= P_Random()&1;
	if (th->tics < 1)
		th->tics = 1;
		
/* don't make punches spark on the wall */
	if (attackrange == MELEERANGE)
		P_SetMobjState (th, S_PUFF3);
}


/*
================
=
= P_SpawnBlood
=
================
*/

void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage)
{
	mobj_t	*th;
	
	z += ((P_Random()-P_Random())<<10);
	th = P_SpawnMobj (x,y,z, MT_BLOOD);
	th->momz = FRACUNIT*2;
	th->tics -= P_Random()&1;
	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);
}

/*
================
=
= P_CheckMissileSpawn
=
= Moves the missile forward a bit and possibly explodes it right there
=
================
*/

void P_CheckMissileSpawn (mobj_t *th)
{
	th->x += (th->momx>>1);
	th->y += (th->momy>>1);	/* move a little forward so an angle can */
							/* be computed if it immediately explodes */
	th->z += (th->momz>>1);
	if (!P_TryMove (th, th->x, th->y))
		P_ExplodeMissile (th);
}

/*
================
=
= P_SpawnMissile
=
================
*/

void P_SpawnMissile (mobj_t *source, mobj_t *dest, mobjtype_t type)
{
	mobj_t		*th;
	angle_t		an;
	int			dist;
	int			speed;
	
	th = P_SpawnMobj (source->x,source->y, source->z + 4*8*FRACUNIT, type);
	if (th->info->seesound)
		S_StartSound (source, th->info->seesound);
	th->target = source;		/* where it came from */
	an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y);	
	th->angle = an;
	an >>= ANGLETOFINESHIFT;
	speed = th->info->speed >> 16;
	th->momx = speed * finecosine[an];
	th->momy = speed * finesine[an];
	
	dist = P_AproxDistance (dest->x - source->x, dest->y - source->y);
	dist = dist / th->info->speed;
	if (dist < 1)
		dist = 1;
	th->momz = (dest->z - source->z) / dist;
	P_CheckMissileSpawn (th);
}


/*
================
=
= P_SpawnPlayerMissile
=
= Tries to aim at a nearby monster
================
*/

void P_SpawnPlayerMissile (mobj_t *source, mobjtype_t type)
{
	mobj_t			*th;
	angle_t			an;
	fixed_t			x,y,z, slope;
	int				speed;
			
/* */
/* see which target is to be aimed at */
/* */
	an = source->angle;
	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;
			slope = 0;
		}
	}
	
	x = source->x;
	y = source->y;
	z = source->z + 4*8*FRACUNIT;
	
	th = P_SpawnMobj (x,y,z, type);
	if (th->info->seesound)
		S_StartSound (source, th->info->seesound);
	th->target = source;
	th->angle = an;
	
	speed = th->info->speed >> 16;
	
	th->momx = speed * finecosine[an>>ANGLETOFINESHIFT];
	th->momy = speed * finesine[an>>ANGLETOFINESHIFT];
	th->momz = speed * slope;

	P_CheckMissileSpawn (th);
}