1 /** @file p_mobj.c World map object interactions.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2003-2005 Samuel Villarreal <svkaiser@gmail.com>
6  * @authors Copyright © 1999 by Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman (PrBoom 2.2.6)
7  * @authors Copyright © 1999-2000 by Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze (PrBoom 2.2.6)
8  * @authors Copyright © 1993-1996 by id Software, Inc.
9  *
10  * @par License
11  * GPL: http://www.gnu.org/licenses/gpl.html
12  *
13  * <small>This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by the
15  * Free Software Foundation; either version 2 of the License, or (at your
16  * option) any later version. This program is distributed in the hope that it
17  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
18  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19  * Public License for more details. You should have received a copy of the GNU
20  * General Public License along with this program; if not, write to the Free
21  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA</small>
23  */
24 
25 #ifdef MSVC
26 #  pragma optimize("g", off)
27 #endif
28 
29 #include <math.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "jdoom64.h"
34 
35 #include "dmu_lib.h"
36 #include "hu_stuff.h"
37 #include "g_common.h"
38 #include "p_map.h"
39 #include "p_terraintype.h"
40 #include "player.h"
41 #include "p_tick.h"
42 #include "p_actor.h"
43 #include "p_start.h"
44 
45 #define VANISHTICS              (2*TICSPERSEC)
46 #define SPAWNFADETICS           (1*TICSPERSEC)
47 
48 #define MAX_BOB_OFFSET          (8)
49 
P_ExplodeMissile(mobj_t * mo)50 void P_ExplodeMissile(mobj_t *mo)
51 {
52     mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;
53 
54     P_MobjChangeState(mo, P_GetState(mo->type, SN_DEATH));
55 
56     mo->tics -= P_Random() & 3;
57 
58     if(mo->tics < 1)
59         mo->tics = 1;
60 
61     if(mo->flags & MF_MISSILE)
62     {
63         mo->flags &= ~MF_MISSILE;
64         mo->flags |= MF_VIEWALIGN;
65         // Remove the brightshadow flag.
66         if(mo->flags & MF_BRIGHTSHADOW)
67             mo->flags &= ~MF_BRIGHTSHADOW;
68         if(mo->flags & MF_BRIGHTEXPLODE)
69             mo->flags |= MF_BRIGHTSHADOW;
70     }
71 
72     if(mo->info->deathSound)
73         S_StartSound(mo->info->deathSound, mo);
74 }
75 
P_FloorBounceMissile(mobj_t * mo)76 void P_FloorBounceMissile(mobj_t *mo)
77 {
78     mo->mom[MZ] = -mo->mom[MZ];
79     P_MobjChangeState(mo, P_GetState(mo->type, SN_DEATH));
80 }
81 
P_MobjMoveXY(mobj_t * mo)82 void P_MobjMoveXY(mobj_t *mo)
83 {
84     coord_t pos[3], mom[3];
85     //player_t *player;
86     dd_bool largeNegative;
87 
88     // $democam: cameramen have their own movement code
89     if(P_CameraXYMovement(mo))
90         return;
91 
92     if(INRANGE_OF(mo->mom[MX], 0, NOMOM_THRESHOLD) &&
93        INRANGE_OF(mo->mom[MY], 0, NOMOM_THRESHOLD))
94     {
95         if(mo->flags & MF_SKULLFLY)
96         {
97             // The skull slammed into something.
98             mo->flags &= ~MF_SKULLFLY;
99             mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;
100 
101             P_MobjChangeState(mo, P_GetState(mo->type, SN_SPAWN));
102         }
103 
104         return;
105     }
106 
107     mom[MX] = MINMAX_OF(-MAXMOM, mo->mom[MX], MAXMOM);
108     mom[MY] = MINMAX_OF(-MAXMOM, mo->mom[MY], MAXMOM);
109     mo->mom[MX] = mom[MX];
110     mo->mom[MY] = mom[MY];
111 
112     //player = mo->player;
113 
114     do
115     {
116         /**
117          * DOOM.exe bug fix:
118          * Large negative displacements were never considered. This explains the
119          * tendency for Mancubus fireballs to pass through walls.
120          */
121 
122         largeNegative = false;
123         if(!cfg.moveBlock && (mom[MX] < -MAXMOMSTEP || mom[MY] < -MAXMOMSTEP))
124         {
125             // Make an exception for "north-only wallrunning".
126             if(!(cfg.wallRunNorthOnly && mo->wallRun))
127                 largeNegative = true;
128         }
129 
130         if(largeNegative || mom[MX] > MAXMOMSTEP || mom[MY] > MAXMOMSTEP)
131         {
132             pos[VX] = mo->origin[VX] + mom[VX] / 2;
133             pos[VY] = mo->origin[VY] + mom[VY] / 2;
134             mom[VX] /= 2;
135             mom[VY] /= 2;
136         }
137         else
138         {
139             pos[VX] = mo->origin[VX] + mom[MX];
140             pos[VY] = mo->origin[VY] + mom[MY];
141             mom[MX] = mom[MY] = 0;
142         }
143 
144         // If mobj was wallrunning - stop.
145         if(mo->wallRun)
146             mo->wallRun = false;
147 
148         // $dropoff_fix.
149         if(!P_TryMoveXY(mo, pos[VX], pos[VY], true, false))
150         {
151             // Blocked move.
152             if(mo->flags2 & MF2_SLIDE)
153             {
154                 // Try to slide along it.
155                 P_SlideMove(mo);
156             }
157             else if(mo->flags & MF_MISSILE)
158             {
159                 Sector* backSec;
160 
161                 /// @kludge: Prevent missiles exploding against the sky.
162                 if(tmCeilingLine &&
163                    (backSec = P_GetPtrp(tmCeilingLine, DMU_BACK_SECTOR)))
164                 {
165                     world_Material* mat = P_GetPtrp(backSec, DMU_CEILING_MATERIAL);
166 
167                     if((P_GetIntp(mat, DMU_FLAGS) & MATF_SKYMASK) &&
168                        mo->origin[VZ] > P_GetDoublep(backSec, DMU_CEILING_HEIGHT))
169                     {
170                         P_MobjRemove(mo, false);
171                         return;
172                     }
173                 }
174 
175                 if(tmFloorLine &&
176                    (backSec = P_GetPtrp(tmFloorLine, DMU_BACK_SECTOR)))
177                 {
178                     world_Material* mat = P_GetPtrp(backSec, DMU_FLOOR_MATERIAL);
179 
180                     if((P_GetIntp(mat, DMU_FLAGS) & MATF_SKYMASK) &&
181                        mo->origin[VZ] < P_GetDoublep(backSec, DMU_FLOOR_HEIGHT))
182                     {
183                         P_MobjRemove(mo, false);
184                         return;
185                     }
186                 }
187                 // kludge end.
188 
189                 P_ExplodeMissile(mo);
190             }
191             else
192             {
193                 mo->mom[MX] = mo->mom[MY] = 0;
194             }
195         }
196     } while(!INRANGE_OF(mom[MX], 0, NOMOM_THRESHOLD) ||
197             !INRANGE_OF(mom[MY], 0, NOMOM_THRESHOLD));
198 
199     // Slow down.
200     Mobj_XYMoveStopping(mo);
201 }
202 
203 /*
204 static int PIT_Splash(Sector* sector, void* parameters)
205 {
206     mobj_t* mo = (mobj_t*)parameters;
207     coord_t floorheight = P_GetDoublep(sector, DMU_FLOOR_HEIGHT);
208 
209     // Is the mobj touching the floor of this sector?
210     if(mo->origin[VZ] < floorheight &&
211        mo->origin[VZ] + mo->height / 2 > floorheight)
212     {
213         /// @todo Play a sound, spawn a generator, etc.
214     }
215 
216     // Continue checking.
217     return false;
218 }
219 */
220 
P_HitFloor(mobj_t * mo)221 void P_HitFloor(mobj_t* mo)
222 {
223     //Mobj_TouchedSectorsIterator(mo, PIT_Splash, mo);
224 }
225 
P_MobjMoveZ(mobj_t * mo)226 void P_MobjMoveZ(mobj_t *mo)
227 {
228     coord_t gravity = XS_Gravity(Mobj_Sector(mo));
229     coord_t dist, delta;
230 
231     // $democam: cameramen get special z movement.
232     if(P_CameraZMovement(mo))
233         return;
234 
235     // $voodoodolls: Check for smooth step up unless a voodoo doll.
236     if(mo->player && mo->player->plr->mo == mo &&
237        mo->origin[VZ] < mo->floorZ)
238     {
239         mo->player->viewHeight -= mo->floorZ - mo->origin[VZ];
240         mo->player->viewHeightDelta =
241             (cfg.common.plrViewHeight - mo->player->viewHeight) / 8;
242     }
243 
244     // Adjust height.
245     mo->origin[VZ] += mo->mom[MZ];
246 
247     if((mo->flags2 & MF2_FLY) &&
248        mo->onMobj && mo->origin[VZ] > mo->onMobj->origin[VZ] + mo->onMobj->height)
249         mo->onMobj = NULL; // We were on a mobj, we are NOT now.
250 
251     if(!((mo->flags ^ MF_FLOAT) & (MF_FLOAT | MF_SKULLFLY | MF_INFLOAT)) &&
252        mo->target && !P_MobjIsCamera(mo->target))
253     {
254         // Float down towards target if too close.
255         dist = M_ApproxDistance(mo->origin[VX] - mo->target->origin[VX],
256                                 mo->origin[VY] - mo->target->origin[VY]);
257 
258         //delta = (mo->target->origin[VZ] + (mo->height / 2)) - mo->origin[VZ];
259         delta = (mo->target->origin[VZ] + mo->target->height / 2) -
260                 (mo->origin[VZ] + mo->height / 2);
261 
262         // Don't go INTO the target.
263         if(!(dist < mo->radius + mo->target->radius &&
264              fabs(delta) < mo->height + mo->target->height))
265         {
266             if(delta < 0 && dist < -(delta * 3))
267             {
268                 mo->origin[VZ] -= FLOATSPEED;
269                 P_MobjSetSRVOZ(mo, -FLOATSPEED);
270             }
271             else if(delta > 0 && dist < (delta * 3))
272             {
273                 mo->origin[VZ] += FLOATSPEED;
274                 P_MobjSetSRVOZ(mo, FLOATSPEED);
275             }
276         }
277     }
278 
279     // Do some fly-bobbing.
280     if(mo->player && (mo->flags2 & MF2_FLY) && mo->origin[VZ] > mo->floorZ &&
281        !mo->onMobj && (mapTime & 2))
282     {
283         mo->origin[VZ] += FIX2FLT(finesine[(FINEANGLES / 20 * mapTime >> 2) & FINEMASK]);
284     }
285 
286     // Clip movement. Another thing?
287     // jd64 >
288     if(mo->origin[VZ] <= mo->floorZ && (mo->flags & MF_MISSILE))
289     {
290         // Hit the floor
291         mo->origin[VZ] = mo->floorZ;
292         P_ExplodeMissile(mo);
293         return;
294     }
295     // < jd64
296 
297     // Clip movement. Another thing?
298     if(mo->onMobj && mo->origin[VZ] <= mo->onMobj->origin[VZ] + mo->onMobj->height)
299     {
300         if(mo->mom[MZ] < 0)
301         {
302             if(mo->player && mo->mom[MZ] < -gravity * 8 &&
303                !(mo->flags2 & MF2_FLY))
304             {
305                 // Squat down.
306                 // Decrease viewheight for a moment after hitting the ground
307                 // (hard), and utter appropriate sound.
308                 mo->player->viewHeightDelta = mo->mom[MZ] / 8;
309 
310                 if(mo->player->health > 0)
311                     S_StartSound(SFX_OOF, mo);
312             }
313 
314             mo->mom[MZ] = 0;
315         }
316 
317         if(mo->mom[MZ] == 0)
318             mo->origin[VZ] = mo->onMobj->origin[VZ] + mo->onMobj->height;
319 
320         if((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
321         {
322             P_ExplodeMissile(mo);
323             return;
324         }
325     }
326 
327     // jd64 >
328     // MotherDemon's Fire attacks can climb up/down stairs
329     // DJS - FIXME!
330 #if 0
331     if((mo->flags & MF_MISSILE) && (mo->type == MT_FIREEND))
332     {
333         mo->origin[VZ] = mo->floorZ;
334 
335         if(mo->type == MT_FIREEND)
336         {
337             return;
338         }
339         else
340         {
341             P_ExplodeMissile(mo);
342             return;
343         }
344     }
345 #endif
346     // < d64tc
347 
348     // The floor.
349     if(mo->origin[VZ] <= mo->floorZ)
350     {   // Hit the floor.
351         dd_bool             movingDown;
352 
353         // Note (id):
354         //  somebody left this after the setting momz to 0,
355         //  kinda useless there.
356         //
357         // cph - This was the a bug in the linuxdoom-1.10 source which
358         //  caused it not to sync Doom 2 v1.9 demos. Someone
359         //  added the above comment and moved up the following code. So
360         //  demos would desync in close lost soul fights.
361         // Note that this only applies to original Doom 1 or Doom2 demos - not
362         //  Final Doom and Ultimate Doom.  So we test demo_compatibility *and*
363         //  gameMission. (Note we assume that Doom1 is always Ult Doom, which
364         //  seems to hold for most published demos.)
365         //
366         //  fraggle - cph got the logic here slightly wrong.  There are three
367         //  versions of Doom 1.9:
368         //
369         //  * The version used in registered doom 1.9 + doom2 - no bounce
370         //  * The version used in ultimate doom - has bounce
371         //  * The version used in final doom - has bounce
372         //
373         // So we need to check that this is either retail or commercial
374         // (but not doom2)
375         int correctLostSoulBounce = true;
376 
377         if(correctLostSoulBounce && (mo->flags & MF_SKULLFLY))
378         {
379             // The skull slammed into something.
380             mo->mom[MZ] = -mo->mom[MZ];
381         }
382 
383         if((movingDown = (mo->mom[MZ] < 0)))
384         {
385             if(mo->player && mo->player->plr->mo == mo &&
386                mo->mom[MZ] < -gravity * 8 && !(mo->flags2 & MF2_FLY))
387             {
388                 // Squat down.
389                 // Decrease viewheight for a moment after hitting the ground
390                 // (hard), and utter appropriate sound.
391                 mo->player->viewHeightDelta = mo->mom[MZ] / 8;
392                 mo->player->jumpTics = 10;
393 
394                 /**
395                  * DOOM bug:
396                  * Dead players would grunt when hitting the ground (e.g.,
397                  * after an archvile attack).
398                  */
399                 if(mo->player->health > 0)
400                     S_StartSound(SFX_OOF, mo);
401             }
402         }
403 
404         mo->origin[VZ] = mo->floorZ;
405 
406         if(movingDown)
407             P_HitFloor(mo);
408 
409         /**
410          * See lost soul bouncing comment above. We need this here for bug
411          * compatibility with original Doom2 v1.9 - if a soul is charging
412          * and hit by a raising floor this would incorrectly reverse it's
413          * Y momentum.
414          */
415         if(!correctLostSoulBounce && (mo->flags & MF_SKULLFLY))
416             mo->mom[MZ] = -mo->mom[MZ];
417 
418         if(!((mo->flags ^ MF_MISSILE) & (MF_MISSILE | MF_NOCLIP)))
419         {
420             if(mo->flags2 & MF2_FLOORBOUNCE)
421             {
422                 P_FloorBounceMissile(mo);
423                 return;
424             }
425             else
426             {
427                 P_ExplodeMissile(mo);
428                 return;
429             }
430         }
431 
432         if(movingDown && mo->mom[MZ] < 0)
433             mo->mom[MZ] = 0;
434     }
435     else if(mo->flags2 & MF2_LOGRAV)
436     {
437         if(mo->mom[MZ] == 0)
438             mo->mom[MZ] = -(gravity / 8) * 2;
439         else
440             mo->mom[MZ] -= (gravity / 8);
441     }
442     else if(!(mo->flags & MF_NOGRAVITY))
443     {
444         if(mo->mom[MZ] == 0)
445             mo->mom[MZ] = -gravity * 2;
446         else
447             mo->mom[MZ] -= gravity;
448     }
449 
450     if(mo->origin[VZ] + mo->height > mo->ceilingZ)
451     {   // Hit the ceiling.
452         if(mo->mom[MZ] > 0)
453             mo->mom[MZ] = 0;
454 
455         mo->origin[VZ] = mo->ceilingZ - mo->height;
456 
457         if(mo->flags & MF_SKULLFLY)
458         {   // The skull slammed into something.
459             mo->mom[MZ] = -mo->mom[MZ];
460         }
461 
462         if(!((mo->flags ^ MF_MISSILE) & (MF_MISSILE | MF_NOCLIP)))
463         {
464             // Don't explode against sky.
465             if(P_GetIntp(P_GetPtrp(Mobj_Sector(mo), DMU_CEILING_MATERIAL),
466                          DMU_FLAGS) & MATF_SKYMASK)
467             {
468                 P_MobjRemove(mo, false);
469             }
470             else
471             {
472                 P_ExplodeMissile(mo);
473             }
474         }
475     }
476 }
477 
P_NightmareRespawn(mobj_t * mobj)478 void P_NightmareRespawn(mobj_t* mobj)
479 {
480     mobj_t*             mo;
481 
482     // Something is occupying it's position?
483     if(!P_CheckPositionXY(mobj, mobj->spawnSpot.origin[VX],
484                           mobj->spawnSpot.origin[VY]))
485         return; // No respwan.
486 
487     if((mo = P_SpawnMobj(mobj->type, mobj->spawnSpot.origin,
488                             mobj->spawnSpot.angle, mobj->spawnSpot.flags)))
489     {
490         mo->reactionTime = 18;
491 
492         // Spawn a teleport fog at old spot.
493         if((mo = P_SpawnMobjXYZ(MT_TFOG, mobj->origin[VX], mobj->origin[VY], 0,
494                                mobj->angle, MSF_Z_FLOOR)))
495             S_StartSound(SFX_TELEPT, mo);
496 
497         // Spawn a teleport fog at the new spot.
498         if((mo = P_SpawnMobj(MT_TFOG, mobj->spawnSpot.origin,
499                                 mobj->spawnSpot.angle,
500                                 mobj->spawnSpot.flags)))
501             S_StartSound(SFX_TELEPT, mo);
502     }
503 
504     // Remove the old monster.
505     P_MobjRemove(mobj, true);
506 }
507 
P_MobjThinker(void * mobjThinkerPtr)508 void P_MobjThinker(void *mobjThinkerPtr)
509 {
510     mobj_t *mobj = mobjThinkerPtr;
511 
512     if(mobj->ddFlags & DDMF_REMOTE)
513         return; // Remote mobjs are handled separately.
514 
515     // The first three bits of the selector special byte contain a
516     // relative health level.
517     P_UpdateHealthBits(mobj);
518 
519 #if __JHERETIC__
520     // Lightsources must stay where they're hooked.
521     if(mobj->type == MT_LIGHTSOURCE)
522     {
523         if(mobj->moveDir > 0)
524             mobj->origin[VZ] = P_GetDoublep(Mobj_Sector(mobj), DMU_FLOOR_HEIGHT);
525         else
526             mobj->origin[VZ] = P_GetDoublep(Mobj_Sector(mobj), DMU_CEILING_HEIGHT);
527 
528         mobj->origin[VZ] += FIX2FLT(mobj->moveDir);
529         return;
530     }
531 #endif
532 
533     // Handle X and Y momentums.
534     if(NON_ZERO(mobj->mom[MX]) || NON_ZERO(mobj->mom[MY]) || (mobj->flags & MF_SKULLFLY))
535     {
536         P_MobjMoveXY(mobj);
537 
538         //// @todo decent NOP/NULL/Nil function pointer please.
539         if(mobj->thinker.function == (thinkfunc_t) NOPFUNC)
540             return; // Mobj was removed.
541     }
542 
543     if(mobj->flags2 & MF2_FLOATBOB)
544     {   // Floating item bobbing motion.
545         // Keep it on the floor.
546         mobj->origin[VZ] = mobj->floorZ;
547         mobj->floorClip = 0;
548 
549         if(mobj->floorClip < -MAX_BOB_OFFSET)
550         {
551             // We don't want it going through the floor.
552             mobj->floorClip = -MAX_BOB_OFFSET;
553         }
554     }
555     else if(!FEQUAL(mobj->origin[VZ], mobj->floorZ) || NON_ZERO(mobj->mom[MZ]))
556     {
557         P_MobjMoveZ(mobj);
558         if(mobj->thinker.function != (thinkfunc_t) P_MobjThinker) // Must've been removed.
559             return; // Mobj was removed.
560     }
561     // Non-sentient objects at rest.
562     else if(!(mobj->mom[MX] == 0 && mobj->mom[MY] == 0) && !sentient(mobj) &&
563             !(mobj->player) && !((mobj->flags & MF_CORPSE) &&
564             cfg.slidingCorpses))
565     {
566         // Objects fall off ledges if they are hanging off slightly push
567         // off of ledge if hanging more than halfway off
568 
569         if(mobj->origin[VZ] > mobj->dropOffZ && // Only objects contacting dropoff.
570            !(mobj->flags & MF_NOGRAVITY) && cfg.fallOff)
571         {
572             P_ApplyTorque(mobj);
573         }
574         else
575         {
576             mobj->intFlags &= ~MIF_FALLING;
577             mobj->gear = 0; // Reset torque.
578         }
579     }
580 
581     if(cfg.slidingCorpses)
582     {
583         if(((mobj->flags & MF_CORPSE)? mobj->origin[VZ] > mobj->dropOffZ :
584                                        mobj->origin[VZ] - mobj->dropOffZ > 24) && // Only objects contacting drop off
585            !(mobj->flags & MF_NOGRAVITY)) // Only objects which fall.
586         {
587             P_ApplyTorque(mobj); // Apply torque.
588         }
589         else
590         {
591             mobj->intFlags &= ~MIF_FALLING;
592             mobj->gear = 0; // Reset torque.
593         }
594     }
595 
596     // $vanish: dead monsters disappear after some time.
597     if(cfg.corpseTime && (mobj->flags & MF_CORPSE) && mobj->corpseTics != -1)
598     {
599         if(++mobj->corpseTics < cfg.corpseTime * TICSPERSEC)
600         {
601             mobj->translucency = 0; // Opaque.
602         }
603         else if(mobj->corpseTics < cfg.corpseTime * TICSPERSEC + VANISHTICS)
604         {
605             // Translucent during vanishing.
606             mobj->translucency =
607                 ((mobj->corpseTics -
608                   cfg.corpseTime * TICSPERSEC) * 255) / VANISHTICS;
609         }
610         else
611         {
612             // Too long; get rid of the corpse.
613             mobj->corpseTics = -1;
614             return;
615         }
616     }
617 
618     // jd64 >
619     // Fade monsters upon spawning.
620     if(mobj->intFlags & MIF_FADE)
621     {
622         if(++mobj->spawnFadeTics < SPAWNFADETICS)
623         {
624             mobj->translucency = MINMAX_OF(0, 255 -
625                 255 * mobj->spawnFadeTics / SPAWNFADETICS, 255);
626         }
627         else
628         {
629             mobj->intFlags &= ~MIF_FADE;
630             mobj->translucency = 0;
631         }
632     }
633     // < d64tc
634 
635     // Cycle through states, calling action functions at transitions.
636     if(mobj->tics != -1)
637     {
638         mobj->tics--;
639 
640         P_MobjAngleSRVOTicker(mobj); // "angle-servo"; smooth actor turning.
641 
642         // You can cycle through multiple STATES in a tic.
643         if(!mobj->tics)
644         {
645             P_MobjClearSRVO(mobj);
646             if(!P_MobjChangeState(mobj, mobj->state->nextState))
647                 return; // Freed itself.
648         }
649     }
650     else if(!IS_CLIENT)
651     {
652         // Check for nightmare respawn.
653         if(!(mobj->flags & MF_COUNTKILL))
654             return;
655 
656         if(!gfw_Rule(respawnMonsters))
657             return;
658 
659         mobj->moveCount++;
660 
661         if(mobj->moveCount < 12 * 35)
662             return;
663 
664         if(mapTime & 31)
665             return;
666 
667         if(P_Random() > 4)
668             return;
669 
670         P_NightmareRespawn(mobj);
671     }
672 }
673 
P_SpawnMobjXYZ(mobjtype_t type,coord_t x,coord_t y,coord_t z,angle_t angle,int spawnFlags)674 mobj_t *P_SpawnMobjXYZ(mobjtype_t type, coord_t x, coord_t y, coord_t z, angle_t angle,
675     int spawnFlags)
676 {
677     mobjinfo_t *info;
678     int ddflags = 0;
679     coord_t space;
680     mobj_t *mo;
681 
682     if(type < MT_FIRST || type >= Get(DD_NUMMOBJTYPES))
683     {
684         App_Log(DE2_MAP_ERROR, "Attempt to spawn unknown mobj type %i", type);
685         return NULL;
686     }
687 
688     info = &MOBJINFO[type];
689 
690     // Clients only spawn local objects.
691     if(!(info->flags & MF_LOCAL) && IS_CLIENT)
692         return NULL;
693 
694     // Not for deathmatch?
695     if(gfw_Rule(deathmatch) && (info->flags & MF_NOTDMATCH))
696         return NULL;
697 
698     // Check for specific disabled objects.
699     if(IS_NETGAME)
700     {
701         // Cooperative weapons?
702         if(cfg.noCoopWeapons && !gfw_Rule(deathmatch) && type >= MT_CLIP &&
703            type <= MT_SUPERSHOTGUN)
704             return NULL;
705 
706         /**
707          * @todo cfg.noCoopAnything: Exactly which objects is this supposed to
708          * prevent spawning? (at least not MT_PLAYER*...). -jk
709          */
710 #if 0
711         // Don't spawn any special objects in coop?
712         if(cfg.noCoopAnything && !deathmatch)
713             return NULL;
714 #endif
715 
716         // BFG disabled in netgames?
717         if(cfg.noNetBFG && type == MT_MISC25)
718             return NULL;
719     }
720 
721     // Don't spawn any monsters?
722     if(gfw_Rule(noMonsters) && ((info->flags & MF_COUNTKILL) || type == MT_SKULL))
723         return NULL;
724 
725     if(info->flags & MF_SOLID)
726         ddflags |= DDMF_SOLID;
727     if(info->flags2 & MF2_DONTDRAW)
728         ddflags |= DDMF_DONTDRAW;
729 
730     mo = Mobj_CreateXYZ(P_MobjThinker, x, y, z, angle, info->radius,
731                          info->height, ddflags);
732     mo->type = type;
733     mo->info = info;
734     mo->flags = info->flags;
735     mo->flags2 = info->flags2;
736     mo->flags3 = info->flags3;
737     mo->damage = info->damage;
738     mo->health = info->spawnHealth * (IS_NETGAME ? cfg.common.netMobHealthModifier : 1);
739     mo->moveDir = DI_NODIR;
740 
741     // Spectres get selector = 1.
742     mo->selector = (type == MT_SHADOWS)? 1 : 0;
743     P_UpdateHealthBits(mo); // Set the health bits of the selector.
744 
745     mo->reactionTime = info->reactionTime;
746 
747     mo->lastLook = P_Random() % MAXPLAYERS;
748 
749     // Must link before setting state (ID assigned for the mo).
750     Mobj_SetState(mo, P_GetState(mo->type, SN_SPAWN));
751 
752     // Set BSP leaf and/or block links.
753     P_MobjLink(mo);
754 
755     mo->floorZ   = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT);
756     mo->dropOffZ = mo->floorZ;
757     mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT);
758 
759     if((spawnFlags & MSF_Z_CEIL) || (info->flags & MF_SPAWNCEILING))
760     {
761         mo->origin[VZ] = mo->ceilingZ - mo->info->height - z;
762     }
763     else if((spawnFlags & MSF_Z_RANDOM) || (info->flags2 & MF2_SPAWNFLOAT))
764     {
765         space = mo->ceilingZ - mo->info->height - mo->floorZ;
766         if(space > 48)
767         {
768             space -= 40;
769             mo->origin[VZ] = ((space * P_Random()) / 256) + mo->floorZ + 40;
770         }
771         else
772         {
773             mo->origin[VZ] = mo->floorZ;
774         }
775     }
776     else if(spawnFlags & MSF_Z_FLOOR)
777     {
778         mo->origin[VZ] = mo->floorZ + z;
779     }
780 
781     /*if(spawnFlags & MTF_FLOAT)
782     {
783         mo->origin[VZ] += 96;
784         mo->flags |= (MF_FLOAT | MF_NOGRAVITY);
785     }*/
786 
787     if(spawnFlags & MSF_DEAF)
788         mo->flags |= MF_AMBUSH;
789 
790     mo->floorClip = 0;
791 
792     if((mo->flags2 & MF2_FLOORCLIP) &&
793        FEQUAL(mo->origin[VZ], P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT)))
794     {
795         terraintype_t const *tt = P_MobjFloorTerrain(mo);
796         if(tt->flags & TTF_FLOORCLIP)
797         {
798             mo->floorClip = 10;
799         }
800     }
801 
802     /*if(spawnFlags & MTF_WALKOFF)
803         mo->flags |= (MF_FLOAT | MF_DROPOFF);
804 
805     if(spawnFlags & MTF_TRANSLUCENT)
806         mo->flags |= MF_SHADOW;*/
807 
808     // Copy spawn attributes to the new mobj.
809     mo->spawnSpot.origin[VX] = x;
810     mo->spawnSpot.origin[VY] = y;
811     mo->spawnSpot.origin[VZ] = z;
812     mo->spawnSpot.angle = angle;
813     mo->spawnSpot.flags = spawnFlags;
814 
815     return mo;
816 }
817 
P_SpawnMobj(mobjtype_t type,coord_t const pos[3],angle_t angle,int spawnFlags)818 mobj_t *P_SpawnMobj(mobjtype_t type, coord_t const pos[3], angle_t angle, int spawnFlags)
819 {
820     return P_SpawnMobjXYZ(type, pos[VX], pos[VY], pos[VZ], angle, spawnFlags);
821 }
822 
P_SpawnBlood(coord_t x,coord_t y,coord_t z,int damage,angle_t angle)823 void P_SpawnBlood(coord_t x, coord_t y, coord_t z, int damage, angle_t angle)
824 {
825     mobj_t* mo;
826 
827     z += FIX2FLT((P_Random() - P_Random()) << 10);
828     if((mo = P_SpawnMobjXYZ(MT_BLOOD, x, y, z, angle, 0)))
829     {
830         mo->mom[MZ] = 2;
831         mo->tics -= P_Random() & 3;
832 
833         if(mo->tics < 1)
834             mo->tics = 1;
835 
836         if(damage <= 12 && damage >= 9)
837             P_MobjChangeState(mo, S_BLOOD2);
838         else if(damage < 9)
839             P_MobjChangeState(mo, S_BLOOD3);
840     }
841 }
842 
843 /**
844  * Moves the missile forward a bit and possibly explodes it right there.
845  *
846  * @param th            The missile to be checked.
847  *
848  * @return              @c true, if the missile is at a valid location else
849  *                      @c false
850  */
P_CheckMissileSpawn(mobj_t * mo)851 dd_bool P_CheckMissileSpawn(mobj_t* mo)
852 {
853     // Move forward slightly so an angle can be computed if it explodes
854     // immediately.
855     mo->origin[VX] += mo->mom[MX] / 2;
856     mo->origin[VY] += mo->mom[MY] / 2;
857     mo->origin[VZ] += mo->mom[MZ] / 2;
858 
859     if(!P_TryMoveXY(mo, mo->origin[VX], mo->origin[VY], false, false))
860     {
861         P_ExplodeMissile(mo);
862         return false;
863     }
864 
865     return true;
866 }
867 
P_SpawnMissile(mobjtype_t type,mobj_t * source,mobj_t * dest)868 mobj_t* P_SpawnMissile(mobjtype_t type, mobj_t *source, mobj_t *dest)
869 {
870     coord_t pos[3], spawnZOff = 0, dist = 0;
871     angle_t angle = 0;
872     //float slope = 0;
873     mobj_t *th = 0;
874     uint an;
875 
876     memcpy(pos, source->origin, sizeof(pos));
877 
878     if(source->player)
879     {
880         // See which target is to be aimed at.
881         angle = source->angle;
882         /*slope =*/ P_AimLineAttack(source, angle, 16 * 64);
883         if(!cfg.common.noAutoAim)
884             if(!lineTarget)
885             {
886                 angle += 1 << 26;
887                 /*slope =*/ P_AimLineAttack(source, angle, 16 * 64);
888 
889                 if(!lineTarget)
890                 {
891                     angle -= 2 << 26;
892                     /*slope =*/ P_AimLineAttack(source, angle, 16 * 64);
893                 }
894 
895                 if(!lineTarget)
896                 {
897                     angle = source->angle;
898                     //slope = tan(LOOKDIR2RAD(source->dPlayer->lookDir)) / 1.2f;
899                 }
900             }
901 
902         if(!P_MobjIsCamera(source->player->plr->mo))
903             spawnZOff = cfg.common.plrViewHeight - 9 +
904                 source->player->plr->lookDir / 173;
905     }
906     else
907     {
908         spawnZOff = 32;
909     }
910 
911     pos[VZ] += spawnZOff;
912     pos[VZ] -= source->floorClip;
913 
914     angle = M_PointToAngle2(pos, dest->origin);
915 
916     if(!source->player)
917     {
918         // Fuzzy player.
919         if(dest->flags & MF_SHADOW)
920             angle += (P_Random() - P_Random()) << 20;
921     }
922 
923     if(!(th = P_SpawnMobj(type, pos, angle, 0)))
924         return NULL;
925 
926     if(th->info->seeSound)
927         S_StartSound(th->info->seeSound, th);
928 
929     th->target = source; // Where it came from.
930     an = angle >> ANGLETOFINESHIFT;
931     th->mom[MX] = th->info->speed * FIX2FLT(finecosine[an]);
932     th->mom[MY] = th->info->speed * FIX2FLT(finesine[an]);
933 
934     /*if(source->player)
935     {   // Allow free-aim with the BFG in deathmatch?
936         if(deathmatch && cfg.netBFGFreeLook == 0 && type == MT_BFG)
937             th->mom[MZ] = 0;
938         else
939             th->mom[MZ] = th->info->speed * slope;
940     }
941     else*/
942     {
943         dist = M_ApproxDistance(dest->origin[VX] - pos[VX],
944                                 dest->origin[VY] - pos[VY]);
945         dist /= th->info->speed;
946         if(dist < 1)
947             dist = 1;
948         th->mom[MZ] = (dest->origin[VZ] - source->origin[VZ]) / dist;
949     }
950 
951     // Make sure the speed is right (in 3D).
952     dist = M_ApproxDistance(M_ApproxDistance(th->mom[MX], th->mom[MY]), th->mom[MZ]);
953     if(dist < 1) dist = 1;
954     dist = th->info->speed / dist;
955 
956     th->mom[MX] *= dist;
957     th->mom[MY] *= dist;
958     th->mom[MZ] *= dist;
959 
960     th->tics -= P_Random() & 3;
961     if(th->tics < 1)
962         th->tics = 1;
963 
964     if(P_CheckMissileSpawn(th))
965         return th;
966 
967     return NULL;
968 }
969 
970 /**
971  * d64tc
972  * dj - It would appear this routine has been stolen from HEXEN and then
973  *      butchered...
974  * @todo: Make sure this still works correctly.
975  */
P_SPMAngle(mobjtype_t type,mobj_t * source,angle_t sourceAngle)976 mobj_t* P_SPMAngle(mobjtype_t type, mobj_t* source, angle_t sourceAngle)
977 {
978     coord_t pos[3], spawnZOff;
979     float fangle = LOOKDIR2RAD(source->player->plr->lookDir), movfactor = 1;
980     angle_t angle;
981     float slope;
982     mobj_t* th;
983     uint an;
984 
985     pos[VX] = source->origin[VX];
986     pos[VY] = source->origin[VY];
987     pos[VZ] = source->origin[VZ];
988 
989     // See which target is to be aimed at.
990     angle = sourceAngle;
991     slope = P_AimLineAttack(source, angle, 16 * 64);
992     if(!lineTarget)
993     {
994         angle += 1 << 26;
995         slope = P_AimLineAttack(source, angle, 16 * 64);
996         if(!lineTarget)
997         {
998             angle -= 2 << 26;
999             slope = P_AimLineAttack(source, angle, 16 * 64);
1000         }
1001 
1002         if(!lineTarget)
1003         {
1004             angle = sourceAngle;
1005 
1006             slope = sin(fangle) / 1.2;
1007             movfactor = cos(fangle);
1008         }
1009     }
1010 
1011     if(!P_MobjIsCamera(source->player->plr->mo))
1012         spawnZOff = cfg.common.plrViewHeight - 9 +
1013                         source->player->plr->lookDir / 173;
1014     else
1015         spawnZOff = 0;
1016 
1017     pos[VZ] += spawnZOff;
1018     pos[VZ] -= source->floorClip;
1019 
1020     if((th = P_SpawnMobj(type, pos, angle, 0)))
1021     {
1022         th->target = source;
1023         an = angle >> ANGLETOFINESHIFT;
1024         th->mom[MX] = movfactor * th->info->speed * FIX2FLT(finecosine[an]);
1025         th->mom[MY] = movfactor * th->info->speed * FIX2FLT(finesine[an]);
1026         th->mom[MZ] = th->info->speed * slope;
1027 
1028         if(th->info->seeSound)
1029             S_StartSound(th->info->seeSound, th);
1030 
1031         th->tics -= P_Random() & 3;
1032         if(th->tics < 1)
1033             th->tics = 1;
1034 
1035         P_CheckMissileSpawn(th);
1036     }
1037 
1038     return th;
1039 }
1040 
1041 /**
1042  * d64tc
1043  */
P_SpawnMotherMissile(mobjtype_t type,coord_t x,coord_t y,coord_t z,mobj_t * source,mobj_t * dest)1044 mobj_t* P_SpawnMotherMissile(mobjtype_t type, coord_t x, coord_t y, coord_t z,
1045     mobj_t* source, mobj_t* dest)
1046 {
1047     angle_t angle;
1048     coord_t dist;
1049     mobj_t* th;
1050     uint an;
1051 
1052     z -= source->floorClip;
1053 
1054     angle = M_PointXYToAngle2(x, y, dest->origin[VX], dest->origin[VY]);
1055     if(dest->flags & MF_SHADOW) // Invisible target
1056     {
1057         angle += (P_Random() - P_Random()) << 21;
1058     }
1059 
1060     if(!(th = P_SpawnMobjXYZ(type, x, y, z, angle, 0)))
1061         return NULL;
1062 
1063     if(th->info->seeSound)
1064         S_StartSound(th->info->seeSound, th);
1065 
1066     th->target = source; // Originator
1067     an = angle >> ANGLETOFINESHIFT;
1068     th->mom[MX] = th->info->speed * FIX2FLT(finecosine[an]);
1069     th->mom[MY] = th->info->speed * FIX2FLT(finesine[an]);
1070 
1071     dist = M_ApproxDistance(dest->origin[VX] - x, dest->origin[VY] - y);
1072     dist /= th->info->speed;
1073 
1074     if(dist < 1)
1075         dist = 1;
1076     th->mom[MZ] = (dest->origin[VZ] - z + 30) / dist;
1077 
1078     th->tics -= P_Random() & 3;
1079     if(th->tics < 1)
1080         th->tics = 1;
1081 
1082     P_CheckMissileSpawn(th);
1083     return th;
1084 }
1085