1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
20 // Moving object handling. Spawn functions.
22 //-----------------------------------------------------------------------------
25 rcsid[] = "$Id: p_mobj.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";
43 void G_PlayerReborn (int player);
44 void P_SpawnMapThing (mapthing_t* mthing);
49 // Returns true if the mobj is still present.
64 mobj->state = (state_t *) S_NULL;
71 mobj->tics = st->tics;
72 mobj->sprite = st->sprite;
73 mobj->frame = st->frame;
76 // Call action functions when the state is set
78 st->action(mobj, NULL);
80 state = st->nextstate;
81 } while (!mobj->tics);
90 void P_ExplodeMissile (mobj_t* mo)
92 mo->momx = mo->momy = mo->momz = 0;
94 P_SetMobjState (mo, mobjinfo[mo->type].deathstate);
96 mo->tics -= P_Random()&3;
101 mo->flags &= ~MF_MISSILE;
103 if (mo->info->deathsound)
104 S_StartSound (mo, mo->info->deathsound);
111 #define STOPSPEED 0x1000
112 #define FRICTION 0xe800
114 void P_XYMovement (mobj_t* mo)
122 if (!mo->momx && !mo->momy)
124 if (mo->flags & MF_SKULLFLY)
126 // the skull slammed into something
127 mo->flags &= ~MF_SKULLFLY;
128 mo->momx = mo->momy = mo->momz = 0;
130 P_SetMobjState (mo, mo->info->spawnstate);
137 if (mo->momx > MAXMOVE)
139 else if (mo->momx < -MAXMOVE)
142 if (mo->momy > MAXMOVE)
144 else if (mo->momy < -MAXMOVE)
152 if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2)
154 ptryx = mo->x + xmove/2;
155 ptryy = mo->y + ymove/2;
161 ptryx = mo->x + xmove;
162 ptryy = mo->y + ymove;
166 if (!P_TryMove (mo, ptryx, ptryy))
170 { // try to slide along it
173 else if (mo->flags & MF_MISSILE)
177 ceilingline->backsector &&
178 ceilingline->backsector->ceilingpic == skyflatnum)
180 // Hack to prevent missiles exploding
182 // Does not handle sky floors.
186 P_ExplodeMissile (mo);
189 mo->momx = mo->momy = 0;
191 } while (xmove || ymove);
194 if (player && player->cheats & CF_NOMOMENTUM)
196 // debug option for no sliding at all
197 mo->momx = mo->momy = 0;
201 if (mo->flags & (MF_MISSILE | MF_SKULLFLY) )
202 return; // no friction for missiles ever
204 if (mo->z > mo->floorz)
205 return; // no friction when airborne
207 if (mo->flags & MF_CORPSE)
209 // do not stop sliding
210 // if halfway off a step with some momentum
211 if (mo->momx > FRACUNIT/4
212 || mo->momx < -FRACUNIT/4
213 || mo->momy > FRACUNIT/4
214 || mo->momy < -FRACUNIT/4)
216 if (mo->floorz != mo->subsector->sector->floorheight)
221 if (mo->momx > -STOPSPEED
222 && mo->momx < STOPSPEED
223 && mo->momy > -STOPSPEED
224 && mo->momy < STOPSPEED
226 || (player->cmd.forwardmove== 0
227 && player->cmd.sidemove == 0 ) ) )
229 // if in a walking frame, stop moving
230 if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4)
231 P_SetMobjState (player->mo, S_PLAY);
238 mo->momx = FixedMul (mo->momx, FRICTION);
239 mo->momy = FixedMul (mo->momy, FRICTION);
246 void P_ZMovement (mobj_t* mo)
251 // check for smooth step up
252 if (mo->player && mo->z < mo->floorz)
254 mo->player->viewheight -= mo->floorz-mo->z;
256 mo->player->deltaviewheight
257 = (VIEWHEIGHT - mo->player->viewheight)>>3;
263 if ( mo->flags & MF_FLOAT
266 // float down towards target if too close
267 if ( !(mo->flags & MF_SKULLFLY)
268 && !(mo->flags & MF_INFLOAT) )
270 dist = P_AproxDistance (mo->x - mo->target->x,
271 mo->y - mo->target->y);
273 delta =(mo->target->z + (mo->height>>1)) - mo->z;
275 if (delta<0 && dist < -(delta*3) )
277 else if (delta>0 && dist < (delta*3) )
284 if (mo->z <= mo->floorz)
289 // somebody left this after the setting momz to 0,
290 // kinda useless there.
291 if (mo->flags & MF_SKULLFLY)
293 // the skull slammed into something
294 mo->momz = -mo->momz;
300 && mo->momz < -GRAVITY*8)
303 // Decrease viewheight for a moment
304 // after hitting the ground (hard),
305 // and utter appropriate sound.
306 mo->player->deltaviewheight = mo->momz>>3;
307 S_StartSound (mo, sfx_oof);
313 if ( (mo->flags & MF_MISSILE)
314 && !(mo->flags & MF_NOCLIP) )
316 P_ExplodeMissile (mo);
320 else if (! (mo->flags & MF_NOGRAVITY) )
323 mo->momz = -GRAVITY*2;
328 if (mo->z + mo->height > mo->ceilingz)
334 mo->z = mo->ceilingz - mo->height;
337 if (mo->flags & MF_SKULLFLY)
338 { // the skull slammed into something
339 mo->momz = -mo->momz;
342 if ( (mo->flags & MF_MISSILE)
343 && !(mo->flags & MF_NOCLIP) )
345 P_ExplodeMissile (mo);
354 // P_NightmareRespawn
357 P_NightmareRespawn (mobj_t* mobj)
366 x = mobj->spawnpoint.x << FRACBITS;
367 y = mobj->spawnpoint.y << FRACBITS;
369 // somthing is occupying it's position?
370 if (!P_CheckPosition (mobj, x, y) )
371 return; // no respwan
373 // spawn a teleport fog at old spot
374 // because of removal of the body?
375 mo = P_SpawnMobj (mobj->x,
377 mobj->subsector->sector->floorheight , MT_TFOG);
378 // initiate teleport sound
379 S_StartSound (mo, sfx_telept);
381 // spawn a teleport fog at the new spot
382 ss = R_PointInSubsector (x,y);
384 mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG);
386 S_StartSound (mo, sfx_telept);
388 // spawn the new monster
389 mthing = &mobj->spawnpoint;
392 if (mobj->info->flags & MF_SPAWNCEILING)
397 // inherit attributes from deceased one
398 mo = P_SpawnMobj (x,y,z, mobj->type);
399 mo->spawnpoint = mobj->spawnpoint;
400 mo->angle = ANG45 * (mthing->angle/45);
402 if (mthing->options & MTF_AMBUSH)
403 mo->flags |= MF_AMBUSH;
405 mo->reactiontime = 18;
407 // remove the old monster,
415 void P_MobjThinker (void *_mobj, void*)
417 mobj_t *mobj = (mobj_t*)_mobj;
422 || (mobj->flags&MF_SKULLFLY) )
426 // FIXME: decent NOP/NULL/Nil function pointer please.
427 if (mobj->thinker.function == (actionf_t)(-1))
428 return; // mobj was removed
430 if ( (mobj->z != mobj->floorz)
435 // FIXME: decent NOP/NULL/Nil function pointer please.
436 if (mobj->thinker.function == (actionf_t)(-1))
437 return; // mobj was removed
441 // cycle through states,
442 // calling action functions at transitions
443 if (mobj->tics != -1)
447 // you can cycle through multiple states in a tic
449 if (!P_SetMobjState (mobj, mobj->state->nextstate) )
450 return; // freed itself
454 // check for nightmare respawn
455 if (! (mobj->flags & MF_COUNTKILL) )
458 if (!respawnmonsters)
463 if (mobj->movecount < 12*35)
472 P_NightmareRespawn (mobj);
492 mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);
493 memset (mobj, 0, sizeof (*mobj));
494 info = &mobjinfo[type];
500 mobj->radius = info->radius;
501 mobj->height = info->height;
502 mobj->flags = info->flags;
503 mobj->health = info->spawnhealth;
505 if (gameskill != sk_nightmare)
506 mobj->reactiontime = info->reactiontime;
508 mobj->lastlook = P_Random () % MAXPLAYERS;
509 // do not set the state with P_SetMobjState,
510 // because action routines can not be called yet
511 st = &states[info->spawnstate];
514 mobj->tics = st->tics;
515 mobj->sprite = st->sprite;
516 mobj->frame = st->frame;
518 // set subsector and/or block links
519 P_SetThingPosition (mobj);
521 mobj->floorz = mobj->subsector->sector->floorheight;
522 mobj->ceilingz = mobj->subsector->sector->ceilingheight;
525 mobj->z = mobj->floorz;
526 else if (z == ONCEILINGZ)
527 mobj->z = mobj->ceilingz - mobj->info->height;
531 mobj->thinker.function = P_MobjThinker;
533 P_AddThinker (&mobj->thinker);
542 mapthing_t itemrespawnque[ITEMQUESIZE];
543 int itemrespawntime[ITEMQUESIZE];
548 void P_RemoveMobj (mobj_t* mobj)
550 if ((mobj->flags & MF_SPECIAL)
551 && !(mobj->flags & MF_DROPPED)
552 && (mobj->type != MT_INV)
553 && (mobj->type != MT_INS))
555 itemrespawnque[iquehead] = mobj->spawnpoint;
556 itemrespawntime[iquehead] = leveltime;
557 iquehead = (iquehead+1)&(ITEMQUESIZE-1);
559 // lose one off the end?
560 if (iquehead == iquetail)
561 iquetail = (iquetail+1)&(ITEMQUESIZE-1);
564 // unlink from sector and block lists
565 P_UnsetThingPosition (mobj);
567 // stop any playing sound
571 P_RemoveThinker ((thinker_t*)mobj);
580 void P_RespawnSpecials (void)
592 // only respawn items in deathmatch
596 // nothing left to respawn?
597 if (iquehead == iquetail)
600 // wait at least 30 seconds
601 if (leveltime - itemrespawntime[iquetail] < 30*35)
604 mthing = &itemrespawnque[iquetail];
606 x = mthing->x << FRACBITS;
607 y = mthing->y << FRACBITS;
609 // spawn a teleport fog at the new spot
610 ss = R_PointInSubsector (x,y);
611 mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG);
612 S_StartSound (mo, sfx_itmbk);
614 // find which type to spawn
615 for (i=0 ; i< NUMMOBJTYPES ; i++)
617 if (mthing->type == mobjinfo[i].doomednum)
622 if (mobjinfo[i].flags & MF_SPAWNCEILING)
627 mo = P_SpawnMobj (x,y,z, i);
628 mo->spawnpoint = *mthing;
629 mo->angle = ANG45 * (mthing->angle/45);
631 // pull it from the que
632 iquetail = (iquetail+1)&(ITEMQUESIZE-1);
640 // Called when a player is spawned on the level.
641 // Most of the player structure stays unchanged
644 void P_SpawnPlayer (mapthing_t* mthing)
656 if (!playeringame[mthing->type-1])
659 p = &players[mthing->type-1];
661 if (p->playerstate == PST_REBORN)
662 G_PlayerReborn (mthing->type-1);
664 x = mthing->x << FRACBITS;
665 y = mthing->y << FRACBITS;
667 mobj = P_SpawnMobj (x,y,z, MT_PLAYER);
669 // set color translations for player sprites
670 if (mthing->type > 1)
671 mobj->flags |= (mthing->type-1)<<MF_TRANSSHIFT;
673 mobj->angle = ANG45 * (mthing->angle/45);
675 mobj->health = p->health;
678 p->playerstate = PST_LIVE;
684 p->fixedcolormap = 0;
685 p->viewheight = VIEWHEIGHT;
690 // give all cards in death match mode
692 for (i=0 ; i<NUMCARDS ; i++)
695 if (mthing->type-1 == consoleplayer)
697 // wake up the status bar
699 // wake up the heads up text
707 // The fields of the mapthing should
708 // already be in host byte order.
710 void P_SpawnMapThing (mapthing_t* mthing)
719 // count deathmatch start positions
720 if (mthing->type == 11)
722 if (deathmatch_p < &deathmatchstarts[10])
724 memcpy (deathmatch_p, mthing, sizeof(*mthing));
730 // check for players specially
731 if (mthing->type <= 4)
733 // save spots for respawning in network games
734 playerstarts[mthing->type-1] = *mthing;
736 P_SpawnPlayer (mthing);
741 // check for apropriate skill level
742 if (!netgame && (mthing->options & 16) )
745 if (gameskill == sk_baby)
747 else if (gameskill == sk_nightmare)
750 bit = 1<<(gameskill-1);
752 if (!(mthing->options & bit) )
755 // find which type to spawn
756 for (i=0 ; i< NUMMOBJTYPES ; i++)
757 if (mthing->type == mobjinfo[i].doomednum)
761 I_Error ("P_SpawnMapThing: Unknown type %i at (%i, %i)",
763 mthing->x, mthing->y);
765 // don't spawn keycards and players in deathmatch
766 if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH)
769 // don't spawn any monsters if -nomonsters
772 || (mobjinfo[i].flags & MF_COUNTKILL)) )
778 x = mthing->x << FRACBITS;
779 y = mthing->y << FRACBITS;
781 if (mobjinfo[i].flags & MF_SPAWNCEILING)
786 mobj = P_SpawnMobj (x,y,z, i);
787 mobj->spawnpoint = *mthing;
790 mobj->tics = 1 + (P_Random () % mobj->tics);
791 if (mobj->flags & MF_COUNTKILL)
793 if (mobj->flags & MF_COUNTITEM)
796 mobj->angle = ANG45 * (mthing->angle/45);
797 if (mthing->options & MTF_AMBUSH)
798 mobj->flags |= MF_AMBUSH;
804 // GAME SPAWN FUNCTIONS
811 extern fixed_t attackrange;
821 z += ((P_Random()-P_Random())<<10);
823 th = P_SpawnMobj (x,y,z, MT_PUFF);
825 th->tics -= P_Random()&3;
830 // don't make punches spark on the wall
831 if (attackrange == MELEERANGE)
832 P_SetMobjState (th, S_PUFF3);
849 z += ((P_Random()-P_Random())<<10);
850 th = P_SpawnMobj (x,y,z, MT_BLOOD);
851 th->momz = FRACUNIT*2;
852 th->tics -= P_Random()&3;
857 if (damage <= 12 && damage >= 9)
858 P_SetMobjState (th,S_BLOOD2);
860 P_SetMobjState (th,S_BLOOD3);
866 // P_CheckMissileSpawn
867 // Moves the missile forward a bit
868 // and possibly explodes it right there.
870 void P_CheckMissileSpawn (mobj_t* th)
872 th->tics -= P_Random()&3;
876 // move a little forward so an angle can
877 // be computed if it immediately explodes
878 th->x += (th->momx>>1);
879 th->y += (th->momy>>1);
880 th->z += (th->momz>>1);
882 if (!P_TryMove (th, th->x, th->y))
883 P_ExplodeMissile (th);
900 th = P_SpawnMobj (source->x,
902 source->z + 4*8*FRACUNIT, type);
904 if (th->info->seesound)
905 S_StartSound (th, th->info->seesound);
907 th->target = source; // where it came from
908 an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y);
911 if (dest->flags & MF_SHADOW)
912 an += (P_Random()-P_Random())<<20;
915 an >>= ANGLETOFINESHIFT;
916 th->momx = FixedMul (th->info->speed, finecosine[an]);
917 th->momy = FixedMul (th->info->speed, finesine[an]);
919 dist = P_AproxDistance (dest->x - source->x, dest->y - source->y);
920 dist = dist / th->info->speed;
925 th->momz = (dest->z - source->z) / dist;
926 P_CheckMissileSpawn (th);
933 // P_SpawnPlayerMissile
934 // Tries to aim at a nearby monster
949 // see which target is to be aimed at
951 slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);
956 slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);
961 slope = P_AimLineAttack (source, an, 16*64*FRACUNIT);
973 z = source->z + 4*8*FRACUNIT;
975 th = P_SpawnMobj (x,y,z, type);
977 if (th->info->seesound)
978 S_StartSound (th, th->info->seesound);
982 th->momx = FixedMul( th->info->speed,
983 finecosine[an>>ANGLETOFINESHIFT]);
984 th->momy = FixedMul( th->info->speed,
985 finesine[an>>ANGLETOFINESHIFT]);
986 th->momz = FixedMul( th->info->speed, slope);
988 P_CheckMissileSpawn (th);