Files
Doom2DosOriginal/P_MOBJ.C
T

1 line
17 KiB
C++

// P_mobj.c
#include "doomdef.h"
#include "p_local.h"
#include "sounds.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;
if (st->action) // call action functions when the state is set
st->action (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, 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;
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
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)
{ // don't 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, 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
if (mo->momz < 0)
{
if (mo->player && mo->momz < -GRAVITY*8) // squat down
{
mo->player->deltaviewheight = mo->momz>>3;
S_StartSound (mo, sfx_oof);
}
mo->momz = 0;
}
mo->z = mo->floorz;
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 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,y,z;
subsector_t *ss;
mobj_t *mo;
mapthing_t *mthing;
x = mobj->spawnpoint.x << FRACBITS;
y = mobj->spawnpoint.y << FRACBITS;
if (!P_CheckPosition (mobj, x, y) )
return; // somthing is occupying it's position
// spawn a teleport fog at old spot
mo = P_SpawnMobj (mobj->x, mobj->y, mobj->subsector->sector->floorheight , MT_TFOG);
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;
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);
if (mobj->thinker.function == (think_t)-1)
return; // mobj was removed
}
if ( (mobj->z != mobj->floorz) || mobj->momz)
{
P_ZMovement (mobj);
if (mobj->thinker.function == (think_t)-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'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;
mobj->thinker.function = P_MobjThinker;
P_AddThinker (&mobj->thinker);
return mobj;
}
/*
===============
=
= P_RemoveMobj
=
===============
*/
mapthing_t itemrespawnque[ITEMQUESIZE];
int itemrespawntime[ITEMQUESIZE];
int iquehead, 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);
if (iquehead == iquetail) // lose one off the end?
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,y,z;
subsector_t *ss;
mobj_t *mo;
mapthing_t *mthing;
int i;
if (deathmatch != 2)
return; // only respawn items in deathmatch
if (iquehead == iquetail)
return; // nothing left to respawn
if (leveltime - itemrespawntime[iquetail] < 30*35)
return; // wait at least 30 seconds
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,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;
z = ONFLOORZ;
mobj = P_SpawnMobj (x,y,z, MT_PLAYER);
if (mthing->type > 1) // set color translations for player sprites
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;
P_SetupPsprites (p); // setup gun psprite
if (deathmatch)
for (i=0 ; i<NUMCARDS ; i++)
p->cards[i] = true; // give all cards in death match mode
if (mthing->type-1 == consoleplayer)
{
ST_Start (); // wake up the status bar
HU_Start (); // wake up the heads up text
}
}
/*
=================
=
= 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])
{
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;
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
=
================
*/
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);
if (dest->flags & MF_SHADOW)
an += (P_Random()-P_Random())<<20; // fuzzy player
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,y,z, 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);
}