gzdoom/code/P_mobj.c

1265 lines
27 KiB
C
Raw Normal View History

1998-04-07 00:00:00 +00:00
// 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.
//
//-----------------------------------------------------------------------------
1998-04-07 00:00:00 +00:00
#include "m_alloc.h"
1998-04-07 00:00:00 +00:00
#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"
1998-04-07 00:00:00 +00:00
#include "v_video.h"
#include "c_cvars.h"
cvar_t *sv_gravity;
cvar_t *sv_friction;
1998-04-07 00:00:00 +00:00
void G_PlayerReborn (int player);
1998-07-14 00:00:00 +00:00
void P_SpawnMapThing (mapthing2_t *mthing);
1998-04-07 00:00:00 +00:00
//
// P_SetMobjState
// Returns true if the mobj is still present.
//
1998-07-14 00:00:00 +00:00
BOOL P_SetMobjState (mobj_t *mobj, statenum_t state)
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
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
1998-04-07 00:00:00 +00:00
do
{
if (state == S_NULL)
{
mobj->state = (state_t *) S_NULL;
P_RemoveMobj (mobj);
1998-07-14 00:00:00 +00:00
ret = false;
break; // killough 4/9/98
1998-04-07 00:00:00 +00:00
}
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
1998-07-14 00:00:00 +00:00
if (st->action.acp1)
st->action.acp1(mobj);
seenstate[state] = 1 + st->nextstate; // killough 4/9/98
1998-04-07 00:00:00 +00:00
state = st->nextstate;
1998-07-14 00:00:00 +00:00
} while (!mobj->tics && !seenstate[state]); // killough 4/9/98
if (ret && !mobj->tics) // killough 4/9/98: detect state cycles
Printf ("Warning: State Cycle Detected\n");
if (!--recursion)
for (;(state=seenstate[i]);i=state-1)
seenstate[i] = 0; // killough 4/9/98: erase memory of states
return ret;
1998-04-07 00:00:00 +00:00
}
//
// P_ExplodeMissile
//
void P_ExplodeMissile (mobj_t* mo)
{
mo->momx = mo->momy = mo->momz = 0;
P_SetMobjState (mo, mobjinfo[mo->type].deathstate);
1998-07-14 00:00:00 +00:00
// [RH] If the object isn't translucent, don't change it.
// Otherwise, make it 50% translucent.
if (TransTable && !(mo->flags & MF_TRANSLUCBITS))
1998-04-07 00:00:00 +00:00
mo->flags |= MF_TRANSLUC50;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
mo->tics -= P_Random (pr_explodemissile) & 3;
1998-04-07 00:00:00 +00:00
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)
1998-07-14 00:00:00 +00:00
if (olddemo ||
mo->z > ceilingline->backsector->ceilingheight) //killough
{
// Hack to prevent missiles exploding against the sky.
// Does not handle sky floors.
P_RemoveMobj (mo);
return;
}
1998-04-07 00:00:00 +00:00
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
1998-07-26 00:00:00 +00:00
if (olddemo) { // no friction when airborne
if (mo->z > mo->floorz)
return;
} else {
if (!P_FindFloor (mo)) // [RH] Z-Check
return;
}
1998-04-07 00:00:00 +00:00
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.ucmd.forwardmove== 0
&& player->cmd.ucmd.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;
1998-07-14 00:00:00 +00:00
mobj_t *other;
1998-04-07 00:00:00 +00:00
// 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;
}
1998-07-14 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
// 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
1998-07-26 00:00:00 +00:00
if (olddemo) {
if (mo->z <= mo->floorz)
other = (mobj_t *)-1;
else
other = NULL;
} else {
other = P_FindFloor (mo); // [RH] Z-Check
if ((mo->flags & MF_MISSILE) && (other == mo->target) && (gametic < (signed)mo->targettic))
other = NULL;
}
1998-07-14 00:00:00 +00:00
if (other) {
// hit the floor ([RH] or something else)
1998-04-07 00:00:00 +00:00
// 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
1998-04-07 00:00:00 +00:00
&& mo->momz < (fixed_t)(sv_gravity->value * -655.36))
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
// [RH] Andy Kempling's fall damage mod.
// (adapted to work with ZDoom)
// calculate the number of broken bones... (joke)
// damage player according to the height fallen.
//
if (dmflags & (DF_YES_FALLING|DF_YES_FALLING_LOTS)) {
fixed_t minvelocity;
fixed_t damage;
float divisor;
if (dmflags & DF_YES_FALLING_LOTS) {
// Original values
minvelocity = -741455*4/3;
divisor = -26214400.0f;
} else {
// Not-quite-so-damaging values
minvelocity = -741455*5/3;
divisor = -39321600.0f;
}
if (mo->momz < minvelocity) {
damage = (fixed_t)((float)(mo->momz) * sv_gravity->value / divisor);
if (other != (mobj_t *)-1) {
// [RH] We landed on another thing. Give half the damage to it.
damage >>= 1;
P_DamageMobj (other, mo, mo, damage, MOD_FALLXFER);
}
if (mo->player) {
// Monsters don't take falling damage
P_DamageMobj(mo, mo, mo, damage, MOD_FALLING);
}
}
}
1998-04-07 00:00:00 +00:00
// 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;
}
1998-07-14 00:00:00 +00:00
if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) )
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
if (other == (mobj_t *)-1) {
if (!olddemo && mo->subsector->sector->floorpic == skyflatnum) {
// [RH] Just remove the missile without exploding it
// if this is a sky floor.
P_RemoveMobj (mo);
return;
}
mo->z = mo->floorz;
}
1998-04-07 00:00:00 +00:00
P_ExplodeMissile (mo);
return;
}
1998-07-14 00:00:00 +00:00
P_StandOnThing (mo, other);
1998-04-07 00:00:00 +00:00
}
else if (! (mo->flags & MF_NOGRAVITY) )
{
if (mo->momz == 0)
1998-04-07 00:00:00 +00:00
mo->momz = (fixed_t)(sv_gravity->value * -163.84);
1998-04-07 00:00:00 +00:00
else
1998-04-07 00:00:00 +00:00
mo->momz -= (fixed_t)(sv_gravity->value * 81.92);
1998-04-07 00:00:00 +00:00
}
1998-07-26 00:00:00 +00:00
if (olddemo) {
if (mo->z + mo->height > mo->ceilingz)
other = (mobj_t *) -1;
else
other = NULL;
} else {
other = P_FindCeiling (mo); // [RH] Z-Check
if ((mo->flags & MF_MISSILE) && (other == mo->target) && (gametic < (signed)mo->targettic))
other = NULL;
}
1998-07-14 00:00:00 +00:00
if (other)
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
// hit the ceiling (or something else)
1998-04-07 00:00:00 +00:00
if (mo->momz > 0)
mo->momz = 0;
1998-07-14 00:00:00 +00:00
if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) )
{
if (other == (mobj_t *)-1) {
if (!olddemo && mo->subsector->sector->ceilingpic == skyflatnum) {
// [RH] Just remove the missile without exploding it
// if this is a sky ceiling.
P_RemoveMobj (mo);
return;
}
mo->z = mo->ceilingz - mo->height;
}
P_ExplodeMissile (mo);
return;
}
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
if (other == (mobj_t *)-1)
mo->z = mo->ceilingz - mo->height;
else
mo->z = other->z - mo->height;
1998-04-07 00:00:00 +00:00
}
if (mo->flags & MF_SKULLFLY)
{ // the skull slammed into something
mo->momz = -mo->momz;
}
}
}
//
// P_NightmareRespawn
//
1998-07-14 00:00:00 +00:00
void P_NightmareRespawn (mobj_t* mobj)
1998-04-07 00:00:00 +00:00
{
fixed_t x;
fixed_t y;
1998-07-14 00:00:00 +00:00
fixed_t z;
subsector_t* ss;
1998-04-07 00:00:00 +00:00
mobj_t* mo;
1998-07-14 00:00:00 +00:00
mapthing2_t* mthing;
int onfloor;
x = mobj->spawnpoint.x << FRACBITS;
y = mobj->spawnpoint.y << FRACBITS;
z = mobj->spawnpoint.z << FRACBITS;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
// something is occupying it's position?
1998-04-07 00:00:00 +00:00
if (!P_CheckPosition (mobj, x, y) )
1998-07-14 00:00:00 +00:00
return; // no respawn
1998-04-07 00:00:00 +00:00
// spawn a teleport fog at old spot
// because of removal of the body?
mo = P_SpawnMobj (mobj->x,
mobj->y,
1998-07-14 00:00:00 +00:00
0, MT_TFOG, ONFLOORZ);
1998-04-07 00:00:00 +00:00
// initiate teleport sound
S_StartSound (mo, sfx_telept);
1998-07-14 00:00:00 +00:00
ss = R_PointInSubsector (x,y);
1998-04-07 00:00:00 +00:00
// spawn a teleport fog at the new spot
1998-07-14 00:00:00 +00:00
if (mobj->info->flags & MF_SPAWNCEILING)
onfloor = ONCEILINGZ;
else
onfloor = ONFLOORZ;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
mo = P_SpawnMobj (x, y, z, MT_TFOG, onfloor);
1998-04-07 00:00:00 +00:00
S_StartSound (mo, sfx_telept);
// spawn the new monster
mthing = &mobj->spawnpoint;
1998-07-14 00:00:00 +00:00
// spawn it
1998-04-07 00:00:00 +00:00
// inherit attributes from deceased one
1998-07-14 00:00:00 +00:00
mo = P_SpawnMobj (x,y,z, mobj->type, onfloor);
mo->spawnpoint = mobj->spawnpoint;
1998-04-07 00:00:00 +00:00
mo->angle = ANG45 * (mthing->angle/45);
1998-07-14 00:00:00 +00:00
if (mthing->flags & MTF_AMBUSH)
1998-04-07 00:00:00 +00:00
mo->flags |= MF_AMBUSH;
mo->reactiontime = 18;
1998-07-14 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
// 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++;
1998-07-14 00:00:00 +00:00
if (mobj->movecount < 12*TICRATE)
1998-04-07 00:00:00 +00:00
return;
1998-04-07 00:00:00 +00:00
if ( level.time&31 )
1998-04-07 00:00:00 +00:00
return;
1998-07-14 00:00:00 +00:00
if (P_Random (pr_mobjthinker) > 4)
1998-04-07 00:00:00 +00:00
return;
P_NightmareRespawn (mobj);
}
}
//
// P_SpawnMobj
1998-07-14 00:00:00 +00:00
// [RH] Since MapThings can now be stored with their own z-position,
// we now use a separate parameter to indicate if it should be
// spawned relative to the floor or ceiling.
1998-04-07 00:00:00 +00:00
//
1998-07-14 00:00:00 +00:00
mobj_t *P_SpawnMobj (fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, int onfloor)
1998-04-07 00:00:00 +00:00
{
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->value != sk_nightmare)
mobj->reactiontime = info->reactiontime;
1998-07-14 00:00:00 +00:00
mobj->lastlook = P_Random (pr_spawnmobj) % MAXPLAYERS;
1998-04-07 00:00:00 +00:00
// 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;
1998-07-26 00:00:00 +00:00
mobj->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98
1998-04-07 00:00:00 +00:00
// set subsector and/or block links
P_SetThingPosition (mobj);
mobj->floorz = mobj->subsector->sector->floorheight;
mobj->ceilingz = mobj->subsector->sector->ceilingheight;
1998-07-14 00:00:00 +00:00
if (onfloor == ONFLOORZ)
mobj->z = mobj->floorz + z;
else if (onfloor == ONCEILINGZ)
mobj->z = mobj->ceilingz - mobj->height - z;
1998-04-07 00:00:00 +00:00
else
mobj->z = z;
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
P_AddThinker (&mobj->thinker);
1998-07-14 00:00:00 +00:00
// [RH] If it's an ambient sound, activate it
if (type >= MT_AMBIENT0 && type <= MT_AMBIENT63)
S_ActivateAmbient (mobj, type - MT_AMBIENT0);
1998-04-07 00:00:00 +00:00
return mobj;
}
//
// P_RemoveMobj
//
1998-07-14 00:00:00 +00:00
mapthing2_t itemrespawnque[ITEMQUESIZE];
1998-04-07 00:00:00 +00:00
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;
1998-04-07 00:00:00 +00:00
itemrespawntime[iquehead] = level.time;
1998-04-07 00:00:00 +00:00
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);
1998-07-26 00:00:00 +00:00
// Delete all nodes on the current sector_list phares 3/16/98
if (sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
1998-04-07 00:00:00 +00:00
// 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;
1998-07-14 00:00:00 +00:00
mapthing2_t* mthing;
int onfloor;
1998-04-07 00:00:00 +00:00
int i;
// only respawn items in deathmatch
1998-07-14 00:00:00 +00:00
if (!deathmatch->value || !(dmflags & DF_ITEMS_RESPAWN))
return;
1998-04-07 00:00:00 +00:00
// nothing left to respawn?
if (iquehead == iquetail)
return;
// wait at least 30 seconds
1998-07-14 00:00:00 +00:00
if (level.time - itemrespawntime[iquetail] < 30*TICRATE)
1998-04-07 00:00:00 +00:00
return;
mthing = &itemrespawnque[iquetail];
1998-07-14 00:00:00 +00:00
x = mthing->x << FRACBITS;
y = mthing->y << FRACBITS;
z = mthing->z << FRACBITS;
1998-04-07 00:00:00 +00:00
// find which type to spawn
for (i=0 ; i< NUMMOBJTYPES ; i++)
{
if (mthing->type == mobjinfo[i].doomednum)
break;
}
if (mobjinfo[i].flags & MF_SPAWNCEILING)
1998-07-14 00:00:00 +00:00
onfloor = ONCEILINGZ;
1998-04-07 00:00:00 +00:00
else
1998-07-14 00:00:00 +00:00
onfloor = ONFLOORZ;
// spawn a teleport fog at the new spot
ss = R_PointInSubsector (x,y);
mo = P_SpawnMobj (x, y, z, MT_IFOG, onfloor);
S_StartSound (mo, sfx_itmbk);
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
// spawn it
mo = P_SpawnMobj (x,y,z, i, onfloor);
1998-04-07 00:00:00 +00:00
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.
//
1998-07-14 00:00:00 +00:00
void P_SpawnPlayer (mapthing2_t *mthing)
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
player_t *p;
fixed_t x;
fixed_t y;
fixed_t z;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
mobj_t *mobj;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
int i;
1998-04-07 00:00:00 +00:00
// not playing?
1998-07-14 00:00:00 +00:00
// [RH] Things 4001-? are also multiplayer starts.
if (mthing->type > 4000) {
if (!playeringame[mthing->type - 4001 + 4])
return;
else {
p = &players[mthing->type - 4001 + 4];
if (p->playerstate == PST_REBORN)
G_PlayerReborn (mthing->type - 4001 + 4);
}
} else {
if (!playeringame[mthing->type-1])
return;
else {
p = &players[mthing->type-1];
if (p->playerstate == PST_REBORN)
G_PlayerReborn (mthing->type - 1);
}
}
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
x = mthing->x << FRACBITS;
y = mthing->y << FRACBITS;
z = mthing->z << FRACBITS;
mobj = P_SpawnMobj (x,y,z, MT_PLAYER, ONFLOORZ);
1998-04-07 00:00:00 +00:00
// set color translations for player sprites
1998-07-14 00:00:00 +00:00
// [RH] Different now: MF_TRANSLATION is not used.
mobj->palette = (struct palette_s *)(translationtables + (mthing->type-1)*256);
1998-04-07 00:00:00 +00:00
mobj->angle = ANG45 * (mthing->angle/45);
1998-04-07 00:00:00 +00:00
mobj->pitch = mobj->roll = 0;
1998-04-07 00:00:00 +00:00
mobj->player = p;
mobj->health = p->health;
p->mo = mobj;
1998-07-14 00:00:00 +00:00
p->playerstate = PST_LIVE;
1998-04-07 00:00:00 +00:00
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);
1998-07-14 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
// give all cards in death match mode
1998-07-14 00:00:00 +00:00
if (deathmatch->value)
1998-04-07 00:00:00 +00:00
for (i=0 ; i<NUMCARDS ; i++)
p->cards[i] = true;
1998-07-14 00:00:00 +00:00
if (consoleplayer ==
(mthing->type > 4000 ? mthing->type - 4001 + 4 : mthing->type - 1))
1998-04-07 00:00:00 +00:00
{
// wake up the status bar
ST_Start ();
// wake up the heads up text
1998-07-14 00:00:00 +00:00
HU_Start ();
1998-04-07 00:00:00 +00:00
}
}
//
// P_SpawnMapThing
// The fields of the mapthing should
// already be in host byte order.
//
1998-07-14 00:00:00 +00:00
void P_SpawnMapThing (mapthing2_t *mthing)
1998-04-07 00:00:00 +00:00
{
int i;
int bit;
mobj_t* mobj;
fixed_t x;
fixed_t y;
fixed_t z;
1998-07-14 00:00:00 +00:00
int onfloor;
1998-04-07 00:00:00 +00:00
// count deathmatch start positions
if (mthing->type == 11)
{
1998-04-07 00:00:00 +00:00
if (deathmatch_p == &deathmatchstarts[MaxDeathmatchStarts]) {
// [RH] Get more deathmatchstarts
MaxDeathmatchStarts += 8;
1998-07-14 00:00:00 +00:00
deathmatchstarts = Realloc (deathmatchstarts, MaxDeathmatchStarts * sizeof(mapthing2_t));
1998-04-07 00:00:00 +00:00
deathmatch_p = &deathmatchstarts[MaxDeathmatchStarts - 8];
1998-04-07 00:00:00 +00:00
}
1998-04-07 00:00:00 +00:00
memcpy (deathmatch_p, mthing, sizeof(*mthing));
deathmatch_p++;
1998-04-07 00:00:00 +00:00
return;
}
// check for players specially
1998-07-14 00:00:00 +00:00
if (mthing->type <= 4 && mthing->type > 0)
1998-04-07 00:00:00 +00:00
{
// save spots for respawning in network games
playerstarts[mthing->type-1] = *mthing;
1998-07-14 00:00:00 +00:00
if (!deathmatch->value)
1998-04-07 00:00:00 +00:00
P_SpawnPlayer (mthing);
return;
1998-07-14 00:00:00 +00:00
} else if (mthing->type >= 4001 && mthing->type <= 4001 + MAXPLAYERS - 4) {
// [RH] Multiplayer starts for players 5-?
playerstarts[mthing->type - 4001 + 4] = *mthing;
if (!deathmatch->value)
P_SpawnPlayer (mthing);
return;
1998-04-07 00:00:00 +00:00
}
1998-07-14 00:00:00 +00:00
if (netgame) {
if (deathmatch->value) {
if (!(mthing->flags & MTF_DEATHMATCH))
return;
} else {
if (!(mthing->flags & MTF_COOPERATIVE))
return;
}
} else if (!(mthing->flags & MTF_SINGLE))
1998-04-07 00:00:00 +00:00
return;
1998-07-14 00:00:00 +00:00
// check for apropriate skill level
1998-04-07 00:00:00 +00:00
if (gameskill->value == sk_baby)
bit = 1;
else if (gameskill->value == sk_nightmare)
bit = 4;
else
bit = 1 << ((int)gameskill->value - 1);
1998-07-14 00:00:00 +00:00
if (!(mthing->flags & bit) )
1998-04-07 00:00:00 +00:00
return;
// find which type to spawn
for (i=0 ; i< NUMMOBJTYPES ; i++)
if (mthing->type == mobjinfo[i].doomednum)
break;
1998-07-14 00:00:00 +00:00
if (i==NUMMOBJTYPES) {
// [RH] Don't die if the map tries to spawn an unknown thing
Printf ("Unknown type %i at (%i, %i)\n",
1998-04-07 00:00:00 +00:00
mthing->type,
mthing->x, mthing->y);
1998-07-14 00:00:00 +00:00
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 ("Type %i at (%i, %i) has no frames\n",
mthing->type, mthing->x, mthing->y);
i = MT_UNKNOWNTHING;
}
1998-04-07 00:00:00 +00:00
// don't spawn keycards and players in deathmatch
1998-07-14 00:00:00 +00:00
if (deathmatch->value && mobjinfo[i].flags & MF_NOTDMATCH)
1998-04-07 00:00:00 +00:00
return;
// don't spawn any monsters if -nomonsters
1998-07-14 00:00:00 +00:00
if (dmflags & DF_NO_MONSTERS
1998-04-07 00:00:00 +00:00
&& ( i == MT_SKULL
|| (mobjinfo[i].flags & MF_COUNTKILL)) )
{
return;
}
1998-07-14 00:00:00 +00:00
// [RH] Other things that shouldn't be spawned depending on dmflags
if (deathmatch->value) {
spritenum_t sprite = states[mobjinfo[i].spawnstate].sprite;
if (dmflags & DF_NO_HEALTH) {
switch (sprite) {
case SPR_STIM:
case SPR_MEDI:
case SPR_PSTR:
case SPR_BON1:
case SPR_SOUL:
case SPR_MEGA:
return;
default:
break;
}
}
if (dmflags & DF_NO_ITEMS) {
switch (sprite) {
case SPR_PINV:
case SPR_PSTR:
case SPR_PINS:
case SPR_SUIT:
case SPR_PMAP:
case SPR_PVIS:
return;
default:
break;
}
}
if (dmflags & DF_NO_ARMOR) {
switch (sprite) {
case SPR_ARM1:
case SPR_ARM2:
case SPR_BON2:
case SPR_MEGA:
return;
default:
break;
}
}
}
1998-04-07 00:00:00 +00:00
// spawn it
x = mthing->x << FRACBITS;
y = mthing->y << FRACBITS;
1998-07-14 00:00:00 +00:00
z = mthing->z << FRACBITS;
1998-04-07 00:00:00 +00:00
if (mobjinfo[i].flags & MF_SPAWNCEILING)
1998-07-14 00:00:00 +00:00
onfloor = ONCEILINGZ;
1998-04-07 00:00:00 +00:00
else
1998-07-14 00:00:00 +00:00
onfloor = ONFLOORZ;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
mobj = P_SpawnMobj (x,y,z, i, onfloor);
1998-04-07 00:00:00 +00:00
mobj->spawnpoint = *mthing;
if (mobj->tics > 0)
1998-07-14 00:00:00 +00:00
mobj->tics = 1 + (P_Random (pr_spawnmapthing) % mobj->tics);
1998-04-07 00:00:00 +00:00
if (mobj->flags & MF_COUNTKILL)
1998-04-07 00:00:00 +00:00
level.total_monsters++;
1998-04-07 00:00:00 +00:00
if (mobj->flags & MF_COUNTITEM)
1998-04-07 00:00:00 +00:00
level.total_items++;
1998-04-07 00:00:00 +00:00
mobj->angle = ANG45 * (mthing->angle/45);
1998-07-14 00:00:00 +00:00
if (mthing->flags & MTF_AMBUSH)
1998-04-07 00:00:00 +00:00
mobj->flags |= MF_AMBUSH;
}
//
// GAME SPAWN FUNCTIONS
//
//
// P_SpawnPuff
//
extern fixed_t attackrange;
1998-07-14 00:00:00 +00:00
void P_SpawnPuff (fixed_t x, fixed_t y, fixed_t z)
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
mobj_t *th;
int t;
t = P_Random (pr_spawnpuff);
z += (t - P_Random (pr_spawnpuff)) << 10;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
th = P_SpawnMobj (x,y,z, MT_PUFF, 0);
1998-04-07 00:00:00 +00:00
th->momz = FRACUNIT;
1998-07-14 00:00:00 +00:00
th->tics -= P_Random (pr_spawnpuff) & 3;
1998-04-07 00:00:00 +00:00
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
1998-07-14 00:00:00 +00:00
P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage)
1998-04-07 00:00:00 +00:00
{
1998-07-14 00:00:00 +00:00
mobj_t *th;
int t;
t = P_Random (pr_spawnblood);
z += (t - P_Random (pr_spawnblood)) << 10;
th = P_SpawnMobj (x,y,z, MT_BLOOD, 0);
1998-04-07 00:00:00 +00:00
th->momz = FRACUNIT*2;
1998-07-14 00:00:00 +00:00
th->tics -= P_Random (pr_spawnblood) & 3;
1998-04-07 00:00:00 +00:00
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)
{
1998-07-14 00:00:00 +00:00
th->tics -= P_Random (pr_checkmissilespawn) & 3;
1998-04-07 00:00:00 +00:00
if (th->tics < 1)
th->tics = 1;
1998-07-14 00:00:00 +00:00
// Give the missile time to get away from the shooter
th->targettic = gametic + 10;
1998-04-07 00:00:00 +00:00
// move a little forward so an angle can
// be computed if it immediately explodes
1998-07-14 00:00:00 +00:00
th->x += th->momx>>1;
th->y += th->momy>>1;
th->z += th->momz>>1;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
if (!P_TryMove (th, th->x, th->y)) {
1998-04-07 00:00:00 +00:00
P_ExplodeMissile (th);
1998-07-14 00:00:00 +00:00
}
1998-04-07 00:00:00 +00:00
}
//
// P_SpawnMissile
//
1998-07-14 00:00:00 +00:00
mobj_t *P_SpawnMissile (mobj_t *source, mobj_t *dest, mobjtype_t type)
1998-04-07 00:00:00 +00:00
{
mobj_t* th;
angle_t an;
int dist;
th = P_SpawnMobj (source->x,
source->y,
1998-07-14 00:00:00 +00:00
source->z + 4*8*FRACUNIT, type, 0);
1998-04-07 00:00:00 +00:00
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
1998-07-14 00:00:00 +00:00
if (dest->flags & MF_SHADOW) {
int t = P_Random (pr_spawnmissile);
an += (t - P_Random (pr_spawnmissile)) << 20;
}
1998-04-07 00:00:00 +00:00
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
//
1998-07-14 00:00:00 +00:00
void P_SpawnPlayerMissile (mobj_t *source, mobjtype_t type)
1998-04-07 00:00:00 +00:00
{
mobj_t* th;
angle_t an;
fixed_t x;
fixed_t y;
fixed_t z;
fixed_t slope;
1998-07-14 00:00:00 +00:00
fixed_t pitchslope;
pitchslope = FixedMul (source->pitch, -40960);
1998-04-07 00:00:00 +00:00
// see which target is to be aimed at
an = source->angle;
1998-04-07 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
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;
1998-04-07 00:00:00 +00:00
// [RH] Use pitch to calculate slope instead of 0.
1998-07-14 00:00:00 +00:00
slope = pitchslope;
1998-04-07 00:00:00 +00:00
}
}
1998-04-07 00:00:00 +00:00
if (linetarget && source->player)
1998-07-14 00:00:00 +00:00
if (!(dmflags & DF_NO_FREELOOK)
&& abs(slope - pitchslope) > source->player->userinfo->aimdist) {
1998-04-07 00:00:00 +00:00
an = source->angle;
1998-07-14 00:00:00 +00:00
slope = pitchslope;
1998-04-07 00:00:00 +00:00
}
1998-04-07 00:00:00 +00:00
x = source->x;
y = source->y;
z = source->z + 4*8*FRACUNIT;
1998-07-14 00:00:00 +00:00
th = P_SpawnMobj (x,y,z, type, 0);
1998-04-07 00:00:00 +00:00
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);
}
1998-07-14 00:00:00 +00:00
// [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 (mobj_t *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 (mobj_t *self, mobjtype_t gibtype, int damage)
{
mobj_t *gib;
vec3_t vd;
vec3_t selfvel;
vec3_t gibvel;
gib = P_SpawnMobj (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,
0);
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 (mobj_t *self, mobjtype_t gibtype, int damage)
{
}
#endif