summaryrefslogtreecommitdiff
path: root/src/p_mobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/p_mobj.c')
-rw-r--r--src/p_mobj.c991
1 files changed, 991 insertions, 0 deletions
diff --git a/src/p_mobj.c b/src/p_mobj.c
new file mode 100644
index 00000000..2d261321
--- /dev/null
+++ b/src/p_mobj.c
@@ -0,0 +1,991 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// $Id: p_mobj.c 4 2005-07-23 16:19:41Z fraggle $
+//
+// 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$
+// Revision 1.1 2005/07/23 16:19:41 fraggle
+// Initial revision
+//
+//
+// DESCRIPTION:
+// Moving object handling. Spawn functions.
+//
+//-----------------------------------------------------------------------------
+
+static const char
+rcsid[] = "$Id: p_mobj.c 4 2005-07-23 16:19:41Z fraggle $";
+
+#include "i_system.h"
+#include "z_zone.h"
+#include "m_random.h"
+
+#include "doomdef.h"
+#include "p_local.h"
+#include "sounds.h"
+
+#include "st_stuff.h"
+#include "hu_stuff.h"
+
+#include "s_sound.h"
+
+#include "doomstat.h"
+
+
+void G_PlayerReborn (int player);
+void P_SpawnMapThing (mapthing_t* mthing);
+
+
+//
+// P_SetMobjState
+// Returns true if the mobj is still present.
+//
+int test;
+
+boolean
+P_SetMobjState
+( mobj_t* mobj,
+ statenum_t state )
+{
+ state_t* st;
+
+ do
+ {
+ if (state == S_NULL)
+ {
+ mobj->state = (state_t *) 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;
+
+ // Modified handling.
+ // Call action functions when the state is set
+ if (st->action.acp1)
+ st->action.acp1(mobj);
+
+ state = st->nextstate;
+ } while (!mobj->tics);
+
+ 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()&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;
+
+ 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;
+
+ 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))
+ {
+ // 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)
+ {
+ // 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
+ {
+ mo->momx = FixedMul (mo->momx, FRICTION);
+ mo->momy = FixedMul (mo->momy, FRICTION);
+ }
+}
+
+//
+// P_ZMovement
+//
+void P_ZMovement (mobj_t* 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;
+ }
+
+ // 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
+ && 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;
+
+ // 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;
+
+ P_AddThinker (&mobj->thinker);
+
+ return mobj;
+}
+
+
+//
+// P_RemoveMobj
+//
+mapthing_t itemrespawnque[ITEMQUESIZE];
+int itemrespawntime[ITEMQUESIZE];
+int iquehead;
+int iquetail;
+
+
+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);
+
+ // 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
+ 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->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;
+
+ // count deathmatch start positions
+ if (mthing->type == 11)
+ {
+ if (deathmatch_p < &deathmatchstarts[10])
+ {
+ memcpy (deathmatch_p, mthing, sizeof(*mthing));
+ deathmatch_p++;
+ }
+ return;
+ }
+
+ // check for players specially
+ if (mthing->type <= 4)
+ {
+ // save spots for respawning in network games
+ playerstarts[mthing->type-1] = *mthing;
+ 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
+ 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 (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);
+ mobj->spawnpoint = *mthing;
+
+ 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
+//
+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()&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;
+
+ z += ((P_Random()-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))
+ 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)
+ an += (P_Random()-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;
+}
+
+
+//
+// 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);
+
+ 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);
+}
+