/* 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.*/

/*-----------------------------------------------------------------------------*/

static const char
rcsid[] = "$Id: p_mobj.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";

#include "i_system.h"
#include "z_zone.h"
#include "m_random.h"

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

#include "st_stuff.h"
#include "hu_stuff.h"

#include "s_sound.h"

#include "doomstat.h"

#include "p_setup.h"

#ifdef __riscos__
#include "ROsupport.h"
#endif


#ifdef DIYINLINE
#include "inl_pside.c"
#include "inl_psub.c"
#endif



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

boolean
P_SetMobjState
( mobj_t*	mobj,
  statenum_t	state )
{
    state_t*	st;
    boolean	ret=true;
    static int	seenstate[NUMSTATES];
    static int	stateid=0;
    static int	recursion=0;

    /*
     *  method to eliminate infinite recursion, somewhat improved compared
     *  to the one used by Boom. Every time this function is called non-
     *  recursively, the stateid is incremented which identifies the states
     *  seen by a given object. A recursion is detected if seenstates[state]
     *  == stateid. This eliminates the need to memset the entire seenstate
     *  array every time.
     */

    /* For top-level, increment the mobj state id */
    if (recursion++ == 0)
    {
        if (++stateid == 0)	/* counter wraparound, reset */
        {
            stateid++;
            memset(seenstate, 0, NUMSTATES*sizeof(int));
        }
    }

    do
    {
	if (state == S_NULL)
	{
	    mobj->state = (state_t *) S_NULL;
	    P_RemoveMobj (mobj);
	    ret = false;
	    break;
	}

	st = &states[state];
	mobj->state = st;
	mobj->tics = st->tics;
	mobj->sprite = st->sprite;
	mobj->frame = st->frame;
        seenstate[state] = stateid;	/* state seen in this loop */

	/* Modified handling.*/
	/* Call action functions when the state is set*/
	if (st->action.acp1)
	    st->action.acp1(mobj);

	state = st->nextstate;
    } while ((!mobj->tics) && (seenstate[state] != stateid));

    if (ret && !mobj->tics)
        fprintf(logfile, "Warning: state cycle detected!\n");

    recursion--;

    return ret;
}



/* 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()&3;

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

    mo->flags &= ~MF_MISSILE;

    if (mo->info->deathsound)
	S_StartSound (mo, mo->info->deathsound);
}



/* P_XYMovement  */

#define STOPSPEED		0x1000
#define FRICTION		0xe800

void P_XYMovement (mobj_t* mo)
{
    fixed_t 	ptryx;
    fixed_t	ptryy;
    player_t*	player;
    fixed_t	xmove;
    fixed_t	ymove;
    BOOMSTATEMENT(int oldx;)
    BOOMSTATEMENT(int oldy;)

    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;

    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;

    BOOMSTATEMENT(oldx = mo->momx; oldy = mo->momy;)

    do
    {
	if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2)
	{
	    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;
	}

	if (!P_TryMove (mo, ptryx, ptryy, true))
	{
	    /* blocked move*/
	    if (mo->player)
	    {	/* try to slide along it*/
		P_SlideMove (mo);
	    }
	    else if (mo->flags & MF_MISSILE)
	    {
		/* explode a missile*/
		if (ceilingline &&
		    ceilingline->backsector &&
		    ceilingline->backsector->ceilingpic == skyflatnum)
		  BOOMSTATEMENT(if (demo_compatibility || (mo->z > ceilingline->backsector->ceilingheight)))
		{
		    /* Hack to prevent missiles exploding*/
		    /* against the sky.*/
		    /* Does not handle sky floors.*/
		    P_RemoveMobj (mo);
		    return;
		}
		P_ExplodeMissile (mo);
	    }
	    else
		mo->momx = mo->momy = 0;
	}
    } while (xmove || ymove);

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

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

    if (mo->z > mo->floorz)
	return;		/* no friction when airborne*/

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

    if (mo->momx > -STOPSPEED
	&& mo->momx < STOPSPEED
	&& mo->momy > -STOPSPEED
	&& mo->momy < STOPSPEED
	&& (!player
	    || (player->cmd.forwardmove== 0
		&& player->cmd.sidemove == 0 ) ) )
    {
	/* if in a walking frame, stop moving*/
	if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4)
	    P_SetMobjState (player->mo, S_PLAY);

	mo->momx = 0;
	mo->momy = 0;
    }
    else
    {
#ifdef DIYBOOM
        if ((oldx == mo->x) && (oldy == mo->y))
        {
            mo->momx = FixedMul(mo->momx, ORIG_FRICTION);
            mo->momy = FixedMul(mo->momy, ORIG_FRICTION);
        }
        else
        {
            mo->momx = FixedMul(mo->momx, mo->friction);
            mo->momy = FixedMul(mo->momy, mo->friction);
        }
	mo->friction = ORIG_FRICTION;
#else
	mo->momx = FixedMul (mo->momx, FRICTION);
	mo->momy = FixedMul (mo->momy, FRICTION);
#endif
    }
}


/* P_ZMovement*/

void P_ZMovement (mobj_t* mo)
{
    fixed_t	dist;
    fixed_t	delta;

    /* check for smooth step up*/
    if (mo->player && BOOMSTATEMENT((mo->player->mo == mo) &&) (mo->z < mo->floorz))
    {
	mo->player->viewheight -= mo->floorz-mo->z;

	mo->player->deltaviewheight
	    = (VIEWHEIGHT - mo->player->viewheight)>>3;
    }

    /* Takeoff in jump mode? */
    if (jump_cheat && mo->player && (mo->z == mo->floorz) && (mo->momz > 0))
    {
	mo->player->deltaviewheight = -8*GRAVITY;
	S_StartSound(mo, sfx_oof);
    }

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

    }

    /* clip movement*/
    if (mo->z <= mo->floorz)
    {
	/* hit the floor*/

	/* Note (id):*/
	/*  somebody left this after the setting momz to 0,*/
	/*  kinda useless there.*/
	if (mo->flags & MF_SKULLFLY)
	{
	    /* the skull slammed into something*/
	    mo->momz = -mo->momz;
	}

	if (mo->momz < 0)
	{
	    if (mo->player BOOMSTATEMENT(&& (mo->player->mo == mo))
		&& mo->momz < -GRAVITY*8)
	    {
		/* Squat down.*/
		/* Decrease viewheight for a moment*/
		/* after hitting the ground (hard),*/
		/* and utter appropriate sound.*/
		mo->player->deltaviewheight = mo->momz>>3;
		S_StartSound (mo, sfx_oof);
	    }
	    mo->momz = 0;
	}
	mo->z = mo->floorz;

	if ( (mo->flags & MF_MISSILE)
	     && !(mo->flags & MF_NOCLIP) )
	{
	    P_ExplodeMissile (mo);
	    return;
	}
    }
    else if (! (mo->flags & MF_NOGRAVITY) )
    {
	if (mo->momz == 0)
	    mo->momz = -GRAVITY*2;
	else
	    mo->momz -= GRAVITY;
    }

    if (mo->z + mo->height > mo->ceilingz)
    {
	/* hit the ceiling*/
	if (mo->momz > 0)
	    mo->momz = 0;
	{
	    mo->z = mo->ceilingz - mo->height;
	}

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




/* P_NightmareRespawn*/

void
P_NightmareRespawn (mobj_t* mobj)
{
    fixed_t		x;
    fixed_t		y;
    fixed_t		z;
    subsector_t*	ss;
    mobj_t*		mo;
    mapthing_t*		mthing;

    x = mobj->spawnpoint.x << FRACBITS;
    y = mobj->spawnpoint.y << FRACBITS;

    /* somthing is occupying it's position?*/
    if (!P_CheckPosition (mobj, x, y) )
	return;	/* no respwan*/

    /* spawn a teleport fog at old spot*/
    /* because of removal of the body?*/
    mo = P_SpawnMobj (mobj->x,
		      mobj->y,
		      mobj->subsector->sector->floorheight , MT_TFOG);
    /* initiate teleport sound*/
    S_StartSound (mo, sfx_telept);

    /* spawn a teleport fog at the new spot*/
    ss = R_PointInSubsector (x,y);

    mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG);

    S_StartSound (mo, sfx_telept);

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

    /* spawn it*/
    if (mobj->info->flags & MF_SPAWNCEILING)
	z = ONCEILINGZ;
    else
	z = ONFLOORZ;

    /* inherit attributes from deceased one*/
    mo = P_SpawnMobj (x,y,z, mobj->type);
    mo->spawnpoint = mobj->spawnpoint;
    mo->angle = ANG45 * (mthing->angle/45);

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

    mo->reactiontime = 18;

    /* remove the old monster,*/
    P_RemoveMobj (mobj);
}



/* P_MobjThinker*/

void P_MobjThinker (mobj_t* mobj)
{
    /* momentum movement*/
    if (mobj->momx
	|| mobj->momy
	|| (mobj->flags&MF_SKULLFLY) )
    {
	P_XYMovement (mobj);

	/* FIXME: decent NOP/NULL/Nil function pointer please.*/
	if (mobj->thinker.function.acv == (actionf_v) (-1))
	    return;		/* mobj was removed*/
    }
    if ( (mobj->z != mobj->floorz)
	 || mobj->momz )
    {
	P_ZMovement (mobj);

	/* FIXME: decent NOP/NULL/Nil function pointer please.*/
	if (mobj->thinker.function.acv == (actionf_v) (-1))
	    return;		/* mobj was removed*/
    }


    /* cycle through states,*/
    /* calling action functions at transitions*/
    if (mobj->tics != -1)
    {
	mobj->tics--;

	/* you can cycle through multiple states in a tic*/
	if (!mobj->tics)
	    if (!P_SetMobjState (mobj, mobj->state->nextstate) )
		return;		/* freed itself*/
    }
    else
    {
	/* check for nightmare respawn*/
	if (! (mobj->flags & MF_COUNTKILL) )
	    return;

	if (!respawnmonsters)
	    return;

	mobj->movecount++;

	if (mobj->movecount < 12*35)
	    return;

	if ( leveltime&31 )
	    return;

	if (P_Random () > 4)
	    return;

	P_NightmareRespawn (mobj);
    }

}



/* P_SpawnMobj*/

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

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

    mobj->lastlook = P_Random () % MAXPLAYERS;
    /* do not set the state with P_SetMobjState,*/
    /* because action routines can not be called yet*/
    st = &states[info->spawnstate];

    mobj->state = st;
    mobj->tics = st->tics;
    mobj->sprite = st->sprite;
    mobj->frame = st->frame;
    BOOMSTATEMENT(mobj->touching_sectorlist = NULL;)

    /* 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;

    mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
    BOOMSTATEMENT(mobj->above_thing = 0;)
    BOOMSTATEMENT(mobj->below_thing = 0;)
    BOOMSTATEMENT(mobj->friction = ORIG_FRICTION;)
    BOOMSTATEMENT(mobj->movefactor = ORIG_FRICTION_FACTOR;)

    P_AddThinker (&mobj->thinker);

    return mobj;
}



/* P_RemoveMobj*/

mapthing_t	itemrespawnque[ITEMQUESIZE];
int		itemrespawntime[ITEMQUESIZE];
int		iquehead = 0;
int		iquetail = 0;


void P_RemoveMobj (mobj_t* mobj)
{
    if ((mobj->flags & MF_SPECIAL)
	&& !(mobj->flags & MF_DROPPED)
	&& (mobj->type != MT_INV)
	&& (mobj->type != MT_INS))
    {
	itemrespawnque[iquehead] = mobj->spawnpoint;
	itemrespawntime[iquehead] = leveltime;
	iquehead = (iquehead+1)&(ITEMQUESIZE-1);

	/* lose one off the end?*/
	if (iquehead == iquetail)
	    iquetail = (iquetail+1)&(ITEMQUESIZE-1);
    }

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

#ifdef DIYBOOM
    if (sector_list)
    {
        P_DelSeclist(sector_list);
	sector_list = NULL;
    }
#endif

    /* stop any playing sound*/
    S_StopSound (mobj);

    /* free block*/
    P_RemoveThinker ((thinker_t*)mobj);
}





/* P_RespawnSpecials*/

void P_RespawnSpecials (void)
{
    fixed_t		x;
    fixed_t		y;
    fixed_t		z;

    subsector_t*	ss;
    mobj_t*		mo;
    mapthing_t*		mthing;

    int			i;

    /* only respawn items in deathmatch*/
    if (deathmatch != 2)
	return;

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

    /* wait at least 30 seconds*/
    if (leveltime - itemrespawntime[iquetail] < 30*35)
	return;

    mthing = &itemrespawnque[iquetail];

    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*/
    i = P_LookupDoomednum((int)(mthing->type));

    /* spawn it*/
    if (mobjinfo[i].flags & MF_SPAWNCEILING)
	z = ONCEILINGZ;
    else
	z = ONFLOORZ;

    mo = P_SpawnMobj (x,y,z, i);
    mo->spawnpoint = *mthing;
    mo->angle = ANG45 * (mthing->angle/45);

    /* 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.*/

void P_SpawnPlayer (mapthing_t* mthing)
{
    player_t*		p;
    fixed_t		x;
    fixed_t		y;
    fixed_t		z;

    mobj_t*		mobj;

    int			i;

    /* not playing?*/
    if (!playeringame[mthing->type-1])
	return;

    p = &players[mthing->type-1];

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

    x 		= mthing->x << FRACBITS;
    y 		= mthing->y << FRACBITS;
    z		= ONFLOORZ;
    mobj	= P_SpawnMobj (x,y,z, MT_PLAYER);

    /* set color translations for player sprites*/
    if (mthing->type > 1)
	mobj->flags |= (mthing->type-1)<<MF_TRANSSHIFT;

    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;

    /* setup gun psprite*/
    P_SetupPsprites (p);

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

    if (mthing->type-1 == consoleplayer)
    {
	/* wake up the status bar*/
	ST_Start ();
	/* wake up the heads up text*/
	HU_Start ();
    }

}



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

void P_SpawnMapThing (mapthing_t* mthing)
{
    int			i;
    int			bit;
    mobj_t*		mobj;
    fixed_t		x;
    fixed_t		y;
    fixed_t		z;

    BOOMSTATEMENT(if (!compatibility && !mthing->type) return;)

    /* count deathmatch start positions*/
    if (mthing->type == 11)
    {
	if (deathmatch_p < &deathmatchstarts[MAX_DEATHMATCH_STARTS])
	{
	    memcpy (deathmatch_p, mthing, sizeof(mapthing_t));
	    deathmatch_p++;
	}
	return;
    }

    /* check for players specially*/
    if ((mthing->type <= MAXPLAYERS) && (mthing->type > 0))
    {
	/* save spots for respawning in network games*/
	memcpy(&(playerstarts[mthing->type-1]), mthing, sizeof(mapthing_t));
	if (!deathmatch)
	    P_SpawnPlayer (mthing);

	return;
    }

    /* check for apropriate skill level*/
    if (!netgame && (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;

    /* find which type to spawn*/
    i = P_LookupDoomednum((int)(mthing->type));

    if (i==NUMMOBJTYPES)
    {
	fprintf(logfile, "P_SpawnMapThing: Unknown type %i at (%i, %i)\n",
		 mthing->type,
		 mthing->x, mthing->y);
	return;
    }

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

    /* don't spawn any monsters if -nomonsters*/
    if (nomonsters
	&& ( i == MT_SKULL
	     || (mobjinfo[i].flags & 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);
    memcpy(&(mobj->spawnpoint), mthing, sizeof(mapthing_t));

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




/* GAME SPAWN FUNCTIONS*/




/* P_SpawnPuff*/

void
P_SpawnPuff
( fixed_t	x,
  fixed_t	y,
  fixed_t	z )
{
    mobj_t*	th;
    int		t;	/* remove dependence on order of evaluation */

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

    th = P_SpawnMobj (x,y,z, MT_PUFF);
    th->momz = FRACUNIT;
    th->tics -= P_Random()&3;

    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;
    int		t;	/* remove dependence on order of evaluation */

    t = P_Random();
    z += ((t-P_Random())<<10);
    th = P_SpawnMobj (x,y,z, MT_BLOOD);
    th->momz = FRACUNIT*2;
    th->tics -= P_Random()&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);
}




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

void P_CheckMissileSpawn (mobj_t* th)
{
    th->tics -= P_Random()&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);

    if (!P_TryMove (th, th->x, th->y, false))
	P_ExplodeMissile (th);
}



/* P_SpawnMissile*/

mobj_t*
P_SpawnMissile
( mobj_t*	source,
  mobj_t*	dest,
  mobjtype_t	type )
{
    mobj_t*	th;
    angle_t	an;
    int		dist;

    th = P_SpawnMobj (source->x,
		      source->y,
		      source->z + 4*8*FRACUNIT, type);

    if (th->info->seesound)
	S_StartSound (th, th->info->seesound);

    th->target = source;	/* where it came from*/
    an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y);

    /* fuzzy player*/
    if (dest->flags & MF_SHADOW)
    {
        int	t=P_Random();	/* remove dependence on order of evaluation */
	an += (t-P_Random())<<20;
    }

    th->angle = an;
    an >>= ANGLETOFINESHIFT;
    th->momx = FixedMul (th->info->speed, finecosine[an]);
    th->momy = FixedMul (th->info->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);

    return th;
}



/* Very similar to A_Tracer, but using that didn't work */

void A_HomingMissile (mobj_t* actor)
{
    angle_t	exact;
    fixed_t	dist;
    fixed_t	slope;
    mobj_t*	dest;

    /*
     * Only player missiles are made homing!
     * P_SpawnMobj initialises everything with 0, so by default tracer is NULL.
     * Only for player missiles tracer can be set, thus only players get HMs.
     */
    if (actor->tracer == NULL)
	return;

    if ((gametic-levelstarttic) & 1)
	return;

    /* adjust direction*/
    dest = actor->tracer;

    if (!dest || dest->health <= 0)
	return;

    /* change angle	*/
    exact = R_PointToAngle2 (actor->x,
			     actor->y,
			     dest->x,
			     dest->y);

    if (exact != actor->angle)
    {
	if (exact - actor->angle > 0x80000000)
	{
	    actor->angle -= TRACEANGLE;
	    if (exact - actor->angle < 0x80000000)
		actor->angle = exact;
	}
	else
	{
	    actor->angle += TRACEANGLE;
	    if (exact - actor->angle > 0x80000000)
		actor->angle = exact;
	}
    }

    exact = actor->angle>>ANGLETOFINESHIFT;
    actor->momx = FixedMul (actor->info->speed, finecosine[exact]);
    actor->momy = FixedMul (actor->info->speed, finesine[exact]);

    /* change slope*/
    dist = P_AproxDistance (dest->x - actor->x,
			    dest->y - actor->y);

    dist = dist / actor->info->speed;

    if (dist < 1)
	dist = 1;
    slope = (dest->z+40*FRACUNIT - actor->z) / dist;

    if (slope < actor->momz)
	actor->momz -= FRACUNIT/8;
    else
	actor->momz += FRACUNIT/8;
}


/* 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;
    fixed_t	y;
    fixed_t	z;
    fixed_t	slope;

    /* 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);

    /* Player might get homing missiles */
    th->tracer = linetarget;

    if (th->info->seesound)
	S_StartSound (th, th->info->seesound);

    th->target = source;
    th->angle = an;
    th->momx = FixedMul( th->info->speed,
			 finecosine[an>>ANGLETOFINESHIFT]);
    th->momy = FixedMul( th->info->speed,
			 finesine[an>>ANGLETOFINESHIFT]);
    th->momz = FixedMul( th->info->speed, slope);

    P_CheckMissileSpawn (th);
}



boolean	homing_missiles = false;

/*
 *  Nice little hack that gives the players (not the monsters)
 *  homing rockets, plasma guns and BFG9000s ;-).
 */
int
P_MakeHomingMissiles(int state)
{
    int		playermissiles[] = {MT_ROCKET, MT_PLASMA, MT_BFG, -1};
    /* We don't want infinite loops just because some joker played around with the states */
    byte	touchedstate[(NUMSTATES+7)>>3];
    int		i, frame;

    /* None touched yet */
    memset(touchedstate, 0, (NUMSTATES+7)>>3);

    /* Toggle? */
    if (state == -1)
    {
        if (homing_missiles)
            state = 0;
        else
            state = 1;
    }

    /*
     *  Some missiles have multiple animations in flight (Plasma, BFG).
     *  Make sure all of those are changed and use an approach that always works.
     */
    i=0;
    while (playermissiles[i] != -1)
    {
	frame = mobjinfo[playermissiles[i]].spawnstate;
	while (frame != S_NULL)
	{
	    /* Have we touched this yet? */
	    if ((touchedstate[frame>>3] & (1<<(frame&7))) != 0) break;
	    /*fprintf(logfile, "update frame %d\n", frame);*/
	    /* I don't think we have to bother with rangechecks here? */
	    touchedstate[frame>>3] |= (1<<(frame&7));
	    if (state == 1)
	    {
		states[frame].action.acp1 = (actionf_p1)A_HomingMissile;
	    }
	    else
	    {
		states[frame].action.acv = (actionf_v)NULL;
	    }
	    frame = states[frame].nextstate;
	}
	i++;
    }

    if (state == 1)
    {
	homing_missiles = true; return 1;
    }
    else
    {
	homing_missiles = false; return 0;
    }
}
