1 /** @file p_mobj.c World map object interaction.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 1999 Activision
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #include <math.h>
23 #include <string.h>
24 
25 #include "jheretic.h"
26 
27 #include "d_netcl.h"
28 #include "dmu_lib.h"
29 #include "hu_stuff.h"
30 #include "g_common.h"
31 #include "p_map.h"
32 #include "p_terraintype.h"
33 #include "player.h"
34 #include "p_tick.h"
35 
36 #include <assert.h>
37 
38 #define VANISHTICS              (2*TICSPERSEC)
39 
40 #define MAX_BOB_OFFSET          (8)
41 
42 #define NOMOMENTUM_THRESHOLD    (0.000001)
43 
44 mobj_t *missileMobj;
45 
P_ExplodeMissile(mobj_t * mo)46 void P_ExplodeMissile(mobj_t *mo)
47 {
48     if(!mo->info) return;
49 
50     if(mo->type == MT_WHIRLWIND)
51     {
52         if(++mo->special2 < 60)
53         {
54             return;
55         }
56     }
57 
58     mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;
59     P_MobjChangeState(mo, P_GetState(mo->type, SN_DEATH));
60 
61     if(mo->flags & MF_MISSILE)
62     {
63         mo->flags &= ~MF_MISSILE;
64         mo->flags |= MF_VIEWALIGN;
65         if(mo->flags & MF_BRIGHTEXPLODE)
66             mo->flags |= MF_BRIGHTSHADOW;
67     }
68 
69     if(mo->info->deathSound)
70     {
71         S_StartSound(mo->info->deathSound, mo);
72     }
73 }
74 
P_FloorBounceMissile(mobj_t * mo)75 void P_FloorBounceMissile(mobj_t* mo)
76 {
77     mo->mom[MZ] = -mo->mom[MZ];
78     P_MobjChangeState(mo, P_GetState(mo->type, SN_DEATH));
79 }
80 
P_ThrustMobj(mobj_t * mo,angle_t angle,coord_t move)81 void P_ThrustMobj(mobj_t* mo, angle_t angle, coord_t move)
82 {
83     uint an = angle >> ANGLETOFINESHIFT;
84 
85     mo->mom[MX] += move * FIX2FLT(finecosine[an]);
86     mo->mom[MY] += move * FIX2FLT(finesine[an]);
87 }
88 
89 /**
90  * @param delta  The amount 'source' needs to turn.
91  *
92  * @return  @c 1, = 'source' needs to turn clockwise, or
93  *          @c 0, = 'source' needs to turn counter clockwise.
94  */
P_FaceMobj(mobj_t * source,mobj_t * target,angle_t * delta)95 int P_FaceMobj(mobj_t* source, mobj_t* target, angle_t* delta)
96 {
97     angle_t diff, angle1, angle2;
98 
99     angle1 = source->angle;
100     angle2 = M_PointToAngle2(source->origin, target->origin);
101     if(angle2 > angle1)
102     {
103         diff = angle2 - angle1;
104         if(diff > ANGLE_180)
105         {
106             *delta = ANGLE_MAX - diff;
107             return 0;
108         }
109         else
110         {
111             *delta = diff;
112             return 1;
113         }
114     }
115     else
116     {
117         diff = angle1 - angle2;
118         if(diff > ANGLE_180)
119         {
120             *delta = ANGLE_MAX - diff;
121             return 1;
122         }
123         else
124         {
125             *delta = diff;
126             return 0;
127         }
128     }
129 }
130 
131 /**
132  * The missile tracer field must be the target.
133  *
134  * @return              @c true, if target was tracked else @c false.
135  */
P_SeekerMissile(mobj_t * actor,angle_t thresh,angle_t turnMax)136 dd_bool P_SeekerMissile(mobj_t* actor, angle_t thresh, angle_t turnMax)
137 {
138     int dir;
139     uint an;
140     coord_t dist;
141     angle_t delta;
142     mobj_t* target;
143 
144     target = actor->tracer;
145     if(!target) return false;
146 
147     if(!(target->flags & MF_SHOOTABLE))
148     {   // Target died.
149         actor->tracer = NULL;
150         return false;
151     }
152 
153     dir = P_FaceMobj(actor, target, &delta);
154     if(delta > thresh)
155     {
156         delta >>= 1;
157         if(delta > turnMax)
158         {
159             delta = turnMax;
160         }
161     }
162 
163     if(dir)
164     {   // Turn clockwise.
165         actor->angle += delta;
166     }
167     else
168     {   // Turn counter clockwise.
169         actor->angle -= delta;
170     }
171 
172     an = actor->angle >> ANGLETOFINESHIFT;
173     actor->mom[MX] = actor->info->speed * FIX2FLT(finecosine[an]);
174     actor->mom[MY] = actor->info->speed * FIX2FLT(finesine[an]);
175 
176     if(actor->origin[VZ]  + actor->height  < target->origin[VZ] ||
177        target->origin[VZ] + target->height < actor->origin[VZ])
178     {   // Need to seek vertically.
179         dist = M_ApproxDistance(target->origin[VX] - actor->origin[VX],
180                                 target->origin[VY] - actor->origin[VY]);
181         dist /= actor->info->speed;
182         if(dist < 1)
183             dist = 1;
184 
185         actor->mom[MZ] = (target->origin[VZ] - actor->origin[VZ]) / dist;
186     }
187 
188     return true;
189 }
190 
191 /**
192  * Wind pushes the mobj, if its sector special is a wind type.
193  */
P_WindThrust(mobj_t * mo)194 void P_WindThrust(mobj_t *mo)
195 {
196     static int windTab[3] = { 2048 * 5, 2048 * 10, 2048 * 25 };
197 
198     Sector *sec = Mobj_Sector(mo);
199     int special = P_ToXSector(sec)->special;
200 
201     switch(special)
202     {
203     case 40: // Wind_East
204     case 41:
205     case 42:
206         P_ThrustMobj(mo, 0, FIX2FLT(windTab[special - 40]));
207         break;
208 
209     case 43: // Wind_North
210     case 44:
211     case 45:
212         P_ThrustMobj(mo, ANG90, FIX2FLT(windTab[special - 43]));
213         break;
214 
215     case 46: // Wind_South
216     case 47:
217     case 48:
218         P_ThrustMobj(mo, ANG270, FIX2FLT(windTab[special - 46]));
219         break;
220 
221     case 49: // Wind_West
222     case 50:
223     case 51:
224         P_ThrustMobj(mo, ANG180, FIX2FLT(windTab[special - 49]));
225         break;
226 
227     default:
228         break;
229     }
230 }
231 
P_MobjMoveXY(mobj_t * mo)232 void P_MobjMoveXY(mobj_t *mo)
233 {
234     coord_t pos[2], mom[2];
235     //player_t *player;
236     dd_bool largeNegative;
237 
238     // $democam: cameramen have their own movement code
239     if(P_CameraXYMovement(mo))
240         return;
241 
242     mom[MX] = MINMAX_OF(-MAXMOM, mo->mom[MX], MAXMOM);
243     mom[MY] = MINMAX_OF(-MAXMOM, mo->mom[MY], MAXMOM);
244     mo->mom[MX] = mom[MX];
245     mo->mom[MY] = mom[MY];
246 
247     if(IS_ZERO(mom[MX]) && IS_ZERO(mom[MY]))
248     {
249         if(mo->flags & MF_SKULLFLY)
250         {
251             // A flying mobj slammed into something.
252             mo->flags &= ~MF_SKULLFLY;
253             mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;
254             P_MobjChangeState(mo, P_GetState(mo->type, SN_SEE));
255         }
256         return;
257     }
258 
259     if(mo->flags2 & MF2_WINDTHRUST)
260         P_WindThrust(mo);
261 
262     //player = mo->player;
263     do
264     {
265         /**
266          * DOOM.exe bug fix:
267          * Large negative displacements were never considered. This explains
268          * the tendency for Mancubus fireballs to pass through walls.
269          */
270 
271         largeNegative = false;
272         if(!cfg.moveBlock && (mom[MX] < -MAXMOMSTEP || mom[MY] < -MAXMOMSTEP))
273         {
274             // Make an exception for "north-only wallrunning".
275             if(!(cfg.wallRunNorthOnly && mo->wallRun))
276                 largeNegative = true;
277         }
278 
279         if(largeNegative || mom[MX] > MAXMOMSTEP || mom[MY] > MAXMOMSTEP)
280         {
281             pos[VX] = mo->origin[VX] + mom[MX] / 2;
282             pos[VY] = mo->origin[VY] + mom[MY] / 2;
283             mom[MX] /= 2;
284             mom[MY] /= 2;
285         }
286         else
287         {
288             pos[VX] = mo->origin[VX] + mom[MX];
289             pos[VY] = mo->origin[VY] + mom[MY];
290             mom[MX] = mom[MY] = 0;
291         }
292 
293         // If mobj was wallrunning - stop.
294         if(mo->wallRun)
295             mo->wallRun = false;
296 
297         // $dropoff_fix
298         if(!P_TryMoveXY(mo, pos[VX], pos[VY], true, false))
299         {   // Blocked mom.
300             if(mo->flags2 & MF2_SLIDE)
301             {   // Try to slide along it.
302                 P_SlideMove(mo);
303             }
304             else if(mo->flags & MF_MISSILE)
305             {
306                 if (mo->flags3 & MF3_WALLBOUNCE)
307                 {
308                     if (P_BounceWall(mo))
309                     {
310                         return;
311                     }
312                 }
313 
314                 // Explode a missile
315                 Sector* backSec;
316 
317                 // @kludge: Prevent missiles exploding against the sky.
318                 if(tmCeilingLine && (backSec = P_GetPtrp(tmCeilingLine, DMU_BACK_SECTOR)))
319                 {
320                     if((P_GetIntp(P_GetPtrp(backSec, DMU_CEILING_MATERIAL), DMU_FLAGS) & MATF_SKYMASK) &&
321                        mo->origin[VZ] > P_GetDoublep(backSec, DMU_CEILING_HEIGHT))
322                     {
323                         if(mo->type == MT_BLOODYSKULL)
324                         {
325                             mo->mom[MX] = mo->mom[MY] = 0;
326                             mo->mom[MZ] = -1;
327                         }
328                         else
329                         {
330                             P_MobjRemove(mo, false);
331                         }
332                         return;
333                     }
334                 }
335                 if(tmFloorLine && (backSec = P_GetPtrp(tmFloorLine, DMU_BACK_SECTOR)))
336                 {
337                     if((P_GetIntp(P_GetPtrp(backSec, DMU_FLOOR_MATERIAL), DMU_FLAGS) & MATF_SKYMASK) &&
338                        mo->origin[VZ] < P_GetDoublep(backSec, DMU_FLOOR_HEIGHT))
339                     {
340                         if(mo->type == MT_BLOODYSKULL)
341                         {
342                             mo->mom[MX] = mo->mom[MY] = 0;
343                             mo->mom[MZ] = -1;
344                         }
345                         else
346                         {
347                             P_MobjRemove(mo, false);
348                         }
349                         return;
350                     }
351                 }
352                 // kludge end.
353 
354                 P_ExplodeMissile(mo);
355             }
356             else
357             {
358                 mo->mom[MX] = mo->mom[MY] = 0;
359             }
360         }
361     } while(!INRANGE_OF(mom[MX], 0, NOMOM_THRESHOLD) ||
362             !INRANGE_OF(mom[MY], 0, NOMOM_THRESHOLD));
363 
364     // Slow down.
365     Mobj_XYMoveStopping(mo);
366 }
367 
P_MobjMoveZ(mobj_t * mo)368 void P_MobjMoveZ(mobj_t *mo)
369 {
370     coord_t gravity;
371     coord_t dist;
372     coord_t delta;
373 
374     // $democam: cameramen get special z movement
375     if(P_CameraZMovement(mo))
376         return;
377 
378     gravity = XS_Gravity(Mobj_Sector(mo));
379 
380     // $voodoodolls: Check for smooth step up unless a voodoo doll.
381     if(mo->player && mo->player->plr->mo == mo && mo->origin[VZ] < mo->floorZ)
382     {
383         mo->player->viewHeight -= mo->floorZ - mo->origin[VZ];
384         mo->player->viewHeightDelta =
385             (cfg.common.plrViewHeight - mo->player->viewHeight) / 8;
386     }
387 
388     // Adjust height.
389     mo->origin[VZ] += mo->mom[MZ];
390 
391     if ((mo->flags2 & MF2_FLY) && mo->onMobj &&
392         mo->origin[VZ] > mo->onMobj->origin[VZ] + mo->onMobj->height)
393     {
394         mo->onMobj = NULL; // We were on a mobj, we are NOT now.
395     }
396 
397     if((mo->flags & MF_FLOAT) && mo->target && !P_MobjIsCamera(mo->target))
398     {
399         // Float down towards target if too close.
400         if(!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
401         {
402             coord_t oldZ = mo->origin[VZ];
403 
404             dist = M_ApproxDistance(mo->origin[VX] - mo->target->origin[VX],
405                                     mo->origin[VY] - mo->target->origin[VY]);
406 
407             delta = (mo->target->origin[VZ] + mo->target->height /2) -
408                     (mo->origin[VZ] + mo->height /2);
409 
410             if(dist < mo->radius + mo->target->radius &&
411                fabs(delta) < mo->height + mo->target->height)
412             {
413                 // Don't go INTO the target.
414                 delta = 0;
415             }
416 
417             if(delta < 0 && dist < -(delta * 3))
418             {
419                 mo->origin[VZ] -= FLOATSPEED;
420                 P_MobjSetSRVOZ(mo, -FLOATSPEED);
421             }
422             else if(delta > 0 && dist < (delta * 3))
423             {
424                 mo->origin[VZ] += FLOATSPEED;
425                 P_MobjSetSRVOZ(mo, FLOATSPEED);
426             }
427             if(delta)
428             {
429                 // Where did we end up?
430                 if(!P_CheckPosition(mo, mo->origin))
431                 {
432                     // Not a valid position; undo the move.
433                     mo->origin[VZ] = oldZ;
434                     P_MobjSetSRVOZ(mo, 0);
435                 }
436             }
437         }
438     }
439 
440     if(cfg.allowMonsterFloatOverBlocking && (mo->flags & MF_FLOAT) && !mo->player && !(mo->flags & MF_SKULLFLY))
441     {
442         if(!P_CheckPosition(mo, mo->origin))
443         {
444             App_Log(DE2_DEV_MAP_WARNING, "Floating thing %i has gotten stuck!");
445             App_Log(DE2_DEV_MAP_MSG, "  onmobj=%i z=%f flz=%f tmfz=%f", mo->thinker.id,
446                     mo->onMobj? mo->onMobj->thinker.id : 0, mo->origin[VZ],
447                     mo->floorZ, tmFloorZ);
448 
449             if(mo->origin[VZ] < tmFloorZ)
450             {
451                 mo->origin[VZ] = mo->floorZ = tmFloorZ;
452             }
453         }
454     }
455 
456     // Do some fly-bobbing.
457     if (mo->player && mo->player->plr->mo == mo && (mo->flags2 & MF2_FLY) &&
458         mo->origin[VZ] > mo->floorZ && !mo->onMobj && (mapTime & 2))
459     {
460         mo->origin[VZ] += FIX2FLT(finesine[(FINEANGLES / 20 * mapTime >> 2) & FINEMASK]);
461     }
462 
463     // Clip movement. Another thing?
464     if (mo->onMobj && mo->origin[VZ] <= mo->onMobj->origin[VZ] + mo->onMobj->height)
465     {
466         if (mo->mom[MZ] < 0)
467         {
468             if(mo->player && mo->mom[MZ] < -gravity * 8 && !(mo->flags2 & MF2_FLY))
469             {
470                 // Squat down. Decrease viewheight for a moment after
471                 // hitting the ground (hard), and utter appropriate sound.
472                 mo->player->viewHeightDelta = mo->mom[MZ] / 8;
473 
474                 if(mo->player->health > 0)
475                     S_StartSound(SFX_PLROOF, mo);
476             }
477             mo->mom[MZ] = 0;
478         }
479 
480         if (IS_ZERO(mo->mom[MZ]))
481         {
482             mo->origin[VZ] = mo->onMobj->origin[VZ] + mo->onMobj->height;
483         }
484 
485         if((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
486         {
487             P_ExplodeMissile(mo);
488             return;
489         }
490     }
491 
492     // The floor.
493     if(mo->origin[VZ] <= mo->floorZ)
494     {
495         // Hit the floor.
496         dd_bool             movingDown;
497 
498         // Note (id):
499         //  somebody left this after the setting mom[MZ] to 0,
500         //  kinda useless there.
501         //
502         // cph - This was the a bug in the linuxdoom-1.10 source which
503         //  caused it not to sync Doom 2 v1.9 demos. Someone
504         //  added the above comment and moved up the following code. So
505         //  demos would desync in close lost soul fights.
506         // Note that this only applies to original Doom 1 or Doom2 demos - not
507         //  Final Doom and Ultimate Doom.  So we test demo_compatibility *and*
508         //  gamemission. (Note we assume that Doom1 is always Ult Doom, which
509         //  seems to hold for most published demos.)
510         //
511         //  fraggle - cph got the logic here slightly wrong.  There are three
512         //  versions of Doom 1.9:
513         //
514         //  * The version used in registered doom 1.9 + doom2 - no bounce
515         //  * The version used in ultimate doom - has bounce
516         //  * The version used in final doom - has bounce
517         //
518         // So we need to check that this is either retail or commercial
519         // (but not doom2)
520         int correct_lost_soul_bounce = false;
521 
522         if(correct_lost_soul_bounce && (mo->flags & MF_SKULLFLY))
523         {
524             // the skull slammed into something
525             mo->mom[MZ] = -mo->mom[MZ];
526         }
527 
528         if((movingDown = (mo->mom[MZ] < 0)))
529         {
530             if(mo->player && mo->mom[MZ] < -gravity * 8 && !(mo->flags2 & MF2_FLY))
531             {
532                 // Squat down. Decrease viewheight for a moment after
533                 // hitting the ground hard and utter appropriate sound.
534                 mo->player->viewHeightDelta = mo->mom[MZ] / 8;
535 //#if __JHERETIC__
536                 mo->player->jumpTics = 12; // Can't jump in a while.
537 //#endif
538                 // Fix DOOM bug - dead players grunting when hitting the ground
539                 // (e.g., after an archvile attack)
540                 if(mo->player->health > 0)
541                     S_StartSound(SFX_PLROOF, mo);
542             }
543         }
544 
545         mo->origin[VZ] = mo->floorZ;
546 
547         if(movingDown)
548             P_HitFloor(mo);
549 
550         // cph 2001/05/26 -
551         // See lost soul bouncing comment above. We need this here for bug
552         // compatibility with original Doom2 v1.9 - if a soul is charging and
553         // hit by a raising floor this incorrectly reverses its Y momentum.
554 
555         if(!correct_lost_soul_bounce && (mo->flags & MF_SKULLFLY))
556             mo->mom[MZ] = -mo->mom[MZ];
557 
558         if((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
559         {
560             if(mo->flags2 & MF2_FLOORBOUNCE)
561             {
562                 P_FloorBounceMissile(mo);
563                 return;
564             }
565             else if(mo->type == MT_MNTRFX2)
566             {
567                 // Minotaur floor fire can go up steps
568                 return;
569             }
570             else
571             {
572                 P_ExplodeMissile(mo);
573                 return;
574             }
575         }
576 
577         if(movingDown && mo->mom[MZ] < 0)
578         {
579             mo->mom[MZ] = 0;
580         }
581 
582         // Set corpses to CRASH state.
583         {
584             statenum_t state;
585             if((state = P_GetState(mo->type, SN_CRASH)) != S_NULL &&
586                (mo->flags & MF_CORPSE))
587             {
588                 P_MobjChangeState(mo, state);
589                 return;
590             }
591         }
592     }
593     else if(mo->flags2 & MF2_LOGRAV)
594     {
595         if(IS_ZERO(mo->mom[MZ]))
596             mo->mom[MZ] = -(gravity / 8) * 2;
597         else
598             mo->mom[MZ] -= gravity / 8;
599     }
600     else if(!(mo->flags & MF_NOGRAVITY))
601     {
602         if(IS_ZERO(mo->mom[MZ]))
603             mo->mom[MZ] = -gravity * 2;
604         else
605             mo->mom[MZ] -= gravity;
606     }
607 
608     if(mo->origin[VZ] + mo->height > mo->ceilingZ)
609     {
610         // hit the ceiling
611         if(mo->mom[MZ] > 0)
612             mo->mom[MZ] = 0;
613 
614         mo->origin[VZ] = mo->ceilingZ - mo->height;
615 
616         if(mo->flags & MF_SKULLFLY)
617         {                       // the skull slammed into something
618             mo->mom[MZ] = -mo->mom[MZ];
619         }
620 
621         if((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
622         {
623             if(P_GetIntp(P_GetPtrp(Mobj_Sector(mo), DMU_CEILING_MATERIAL),
624                          DMU_FLAGS) & MATF_SKYMASK)
625             {
626                 if(mo->type == MT_BLOODYSKULL)
627                 {
628                     mo->mom[MX] = mo->mom[MY] = 0;
629                     mo->mom[MZ] = -1;
630                 }
631                 else
632                 {
633                     // Don't explode against sky.
634                     P_MobjRemove(mo, false);
635                 }
636                 return;
637             }
638 
639             P_ExplodeMissile(mo);
640             return;
641         }
642     }
643 }
644 
P_NightmareRespawn(mobj_t * mobj)645 void P_NightmareRespawn(mobj_t* mobj)
646 {
647     mobj_t*             mo;
648 
649     // Something is occupying it's position?
650     if(!P_CheckPositionXY(mobj, mobj->spawnSpot.origin[VX],
651                           mobj->spawnSpot.origin[VY]))
652         return; // No respwan.
653 
654     if((mo = P_SpawnMobj(mobj->type, mobj->spawnSpot.origin,
655                             mobj->spawnSpot.angle, mobj->spawnSpot.flags)))
656     {
657         mo->reactionTime = 18;
658 
659         // Spawn a teleport fog at old spot because of removal of the body?
660         if((mo = P_SpawnMobjXYZ(MT_TFOG, mobj->origin[VX], mobj->origin[VY],
661                                TELEFOGHEIGHT, mobj->angle, MSF_Z_FLOOR)))
662             S_StartSound(SFX_TELEPT, mo);
663 
664         // Spawn a teleport fog at the new spot.
665         if((mo = P_SpawnMobjXYZ(MT_TFOG, mobj->spawnSpot.origin[VX],
666                                mobj->spawnSpot.origin[VY], TELEFOGHEIGHT,
667                                mobj->spawnSpot.angle, MSF_Z_FLOOR)))
668             S_StartSound(SFX_TELEPT, mo);
669     }
670 
671     // Remove the old monster.
672     P_MobjRemove(mobj, true);
673 }
674 
675 // Fake the zmovement so that we can check if a move is legal
676 // (from vanilla Heretic)
P_FakeZMovement(mobj_t * mo)677 static void P_FakeZMovement(mobj_t *mo)
678 {
679     coord_t dist  = 0;
680     coord_t delta = 0;
681     //
682     // adjust height
683     //
684     mo->origin[VZ] += mo->mom[VZ];
685     if (mo->flags & MF_FLOAT && mo->target)
686     {
687         // float down towards target if too close
688         if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
689         {
690             dist  = M_ApproxDistance(mo->origin[VX] - mo->target->origin[VX],
691                                      mo->origin[VY] - mo->target->origin[VY]);
692             delta = (mo->target->origin[VZ] + (mo->height / 2)) - mo->origin[VZ];
693             if (delta < 0 && dist < -(delta * 3))
694                 mo->origin[VZ] -= FLOATSPEED;
695             else if (delta > 0 && dist < (delta * 3))
696                 mo->origin[VZ] += FLOATSPEED;
697         }
698     }
699     if (mo->player && mo->flags2 & MF2_FLY && !(mo->origin[VZ] <= mo->floorZ) && (mapTime & 2))
700     {
701         mo->origin[VZ] += finesine[(FINEANGLES / 20 * mapTime >> 2) & FINEMASK];
702     }
703 
704     //
705     // clip movement
706     //
707     if (mo->origin[VZ] <= mo->floorZ)
708     {
709         // Hit the floor
710         mo->origin[VZ] = mo->floorZ;
711         if (mo->mom[VZ] < 0)
712         {
713             mo->mom[VZ] = 0;
714         }
715         if (mo->flags & MF_SKULLFLY)
716         {
717             // The skull slammed into something
718             mo->mom[VZ] = -mo->mom[VZ];
719         }
720         if (MOBJINFO[mo->type].states[SN_CRASH] && (mo->flags & MF_CORPSE))
721         {
722             return;
723         }
724     }
725     else if (mo->flags2 & MF2_LOGRAV)
726     {
727         coord_t GRAVITY = XS_Gravity(Mobj_Sector(mo));
728 
729         if (FEQUAL(mo->mom[VZ], 0))
730             mo->mom[VZ] = -(GRAVITY / 8) * 2;
731         else
732             mo->mom[VZ] -= GRAVITY / 8;
733     }
734     else if (!(mo->flags & MF_NOGRAVITY))
735     {
736         coord_t GRAVITY = XS_Gravity(Mobj_Sector(mo));
737 
738         if (FEQUAL(mo->mom[VZ], 0))
739             mo->mom[VZ] = -GRAVITY * 2;
740         else
741             mo->mom[VZ] -= GRAVITY;
742     }
743 
744     if (mo->origin[VZ] + mo->height > mo->ceilingZ)
745     {
746         // hit the ceiling
747         if (mo->mom[VZ] > 0) mo->mom[VZ] = 0;
748         mo->origin[VZ] = mo->ceilingZ - mo->height;
749         if (mo->flags & MF_SKULLFLY)
750         {
751             // the skull slammed into something
752             mo->mom[VZ] = -mo->mom[VZ];
753         }
754     }
755 }
756 
757 struct checkonmobjz_s
758 {
759     mobj_t *checkThing;
760     mobj_t *onMobj;
761 };
762 
PIT_CheckOnmobjZ(mobj_t * thing,void * dataPtr)763 static int PIT_CheckOnmobjZ(mobj_t *thing, void *dataPtr)
764 {
765     struct checkonmobjz_s *data    = dataPtr;
766     const mobj_t *         tmthing = data->checkThing;
767     coord_t                blockdist;
768 
769     if (thing == tmthing)
770     {
771         // Don't clip against self
772         return false;
773     }
774     if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)))
775     {
776         // Can't hit thing
777         return false;
778     }
779     blockdist = thing->radius + tmthing->radius;
780     if (fabs(thing->origin[VX] - tmthing->origin[VX]) >= blockdist ||
781         fabs(thing->origin[VY] - tmthing->origin[VY]) >= blockdist)
782     {
783         // Didn't hit thing
784         return false;
785     }
786     if (tmthing->origin[VZ] > thing->origin[VZ] + thing->height)
787     {
788         return false;
789     }
790     else if (tmthing->origin[VZ] + tmthing->height < thing->origin[VZ])
791     {
792         // under thing
793         return false;
794     }
795     if (thing->flags & MF_SOLID)
796     {
797         data->onMobj = thing;
798     }
799     return (thing->flags & MF_SOLID) != 0;
800 }
801 
802 // Checks if the new Z position is legal
803 // (from vanilla Heretic)
P_CheckOnmobj(mobj_t * thing)804 static mobj_t *P_CheckOnmobj(mobj_t *thing)
805 {
806 #if 0
807     int			xl,xh,yl,yh,bx,by;
808     subsector_t		*newsubsec;
809     fixed_t x;
810     fixed_t y;
811     mobj_t oldmo;
812 
813     x = thing->x;
814     y = thing->y;
815     tmthing = thing;
816     tmflags = thing->flags;
817     oldmo = *thing; // save the old mobj before the fake zmovement
818     P_FakeZMovement(tmthing);
819 #endif
820 
821     coord_t oldOrigin[3];
822     coord_t oldMom[3];
823     AABoxd  bounds;
824 
825     struct checkonmobjz_s data = {thing, NULL};
826 
827     memcpy(oldOrigin, thing->origin, sizeof(oldOrigin));
828     memcpy(oldMom, thing->mom, sizeof(oldMom));
829 
830     P_FakeZMovement(thing);
831 
832 //    tmx = x;
833 //    tmy = y;
834 
835 //    tmbbox[BOXTOP] = y + tmthing->radius;
836 //    tmbbox[BOXBOTTOM] = y - tmthing->radius;
837 //    tmbbox[BOXRIGHT] = x + tmthing->radius;
838 //    tmbbox[BOXLEFT] = x - tmthing->radius;
839 
840 //    newsubsec = R_PointInSubsector (x,y);
841 //    ceilingline = NULL;
842 
843 ////
844 //// the base floor / ceiling is from the subsector that contains the
845 //// point.  Any contacted lines the step closer together will adjust them
846 ////
847 //    tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
848 //    tmceilingz = newsubsec->sector->ceilingheight;
849 
850 //    validcount++;
851 //    numspechit = 0;
852 
853     if (!(thing->flags & MF_NOCLIP))
854     {
855         bounds.minX = thing->origin[VX] - thing->radius;
856         bounds.minY = thing->origin[VY] - thing->radius;
857         bounds.maxX = thing->origin[VX] + thing->radius;
858         bounds.maxY = thing->origin[VY] + thing->radius;
859 
860         VALIDCOUNT++;
861         Mobj_BoxIterator(&bounds, PIT_CheckOnmobjZ, &data);
862     }
863 
864     // Restore state.
865     memcpy(thing->origin, oldOrigin, sizeof(oldOrigin));
866     memcpy(thing->mom, oldMom, sizeof(oldMom));
867 
868     return data.onMobj;
869 }
870 
P_MobjThinker(void * thinkerPtr)871 void P_MobjThinker(void *thinkerPtr)
872 {
873     mobj_t *mobj = thinkerPtr;
874 
875     if (IS_CLIENT && !ClMobj_IsValid(mobj)) return; // We should not touch this right now.
876 
877     if (mobj->type == MT_BLASTERFX1)
878     {
879         int     i;
880         coord_t frac[3];
881         coord_t z;
882         dd_bool changexy;
883 
884         // Handle movement
885         if (NON_ZERO(mobj->mom[MX]) || NON_ZERO(mobj->mom[MY]) || NON_ZERO(mobj->mom[MZ]) ||
886             !FEQUAL(mobj->origin[VZ], mobj->floorZ))
887         {
888             frac[MX] = mobj->mom[MX] / 8;
889             frac[MY] = mobj->mom[MY] / 8;
890             frac[MZ] = mobj->mom[MZ] / 8;
891 
892             changexy = (NON_ZERO(frac[MX]) || NON_ZERO(frac[MY]));
893             for (i = 0; i < 8; ++i)
894             {
895                 if (changexy)
896                 {
897                     if (!P_TryMoveXY(mobj,
898                                      mobj->origin[VX] + frac[MX],
899                                      mobj->origin[VY] + frac[MY],
900                                      false,
901                                      false))
902                     {
903                         // Blocked move.
904                         P_ExplodeMissile(mobj);
905                         return;
906                     }
907                 }
908 
909                 mobj->origin[VZ] += frac[MZ];
910                 if (mobj->origin[VZ] <= mobj->floorZ)
911                 {
912                     // Hit the floor.
913                     mobj->origin[VZ] = mobj->floorZ;
914                     P_HitFloor(mobj);
915                     P_ExplodeMissile(mobj);
916                     return;
917                 }
918 
919                 if (mobj->origin[VZ] + mobj->height > mobj->ceilingZ)
920                 {
921                     // Hit the ceiling.
922                     mobj->origin[VZ] = mobj->ceilingZ - mobj->height;
923                     P_ExplodeMissile(mobj);
924                     return;
925                 }
926 
927                 if (changexy && (P_Random() < 64))
928                 {
929                     z = mobj->origin[VZ] - 8;
930                     if (z < mobj->floorZ)
931                     {
932                         z = mobj->floorZ;
933                     }
934 
935                     P_SpawnMobjXYZ(MT_BLASTERSMOKE,
936                                    mobj->origin[VX],
937                                    mobj->origin[VY],
938                                    z,
939                                    P_Random() << 24,
940                                    0);
941                 }
942             }
943         }
944 
945         // Advance the state.
946         if (mobj->tics != -1)
947         {
948             mobj->tics--;
949             while (!mobj->tics)
950             {
951                 if (!P_MobjChangeState(mobj, mobj->state->nextState))
952                 { // Mobj was removed.
953                     return;
954                 }
955             }
956         }
957 
958         return;
959     }
960 
961     // The first three bits of the selector special byte contain a relative
962     // health level.
963     P_UpdateHealthBits(mobj);
964 
965     // Handle X and Y momentums.
966     if (NON_ZERO(mobj->mom[MX]) || NON_ZERO(mobj->mom[MY]) || (mobj->flags & MF_SKULLFLY))
967     {
968         P_MobjMoveXY(mobj);
969 
970         if (mobj->thinker.function == (thinkfunc_t) NOPFUNC)
971         {
972             return; // Mobj was removed.
973         }
974     }
975 
976     if (mobj->flags2 & MF2_FLOATBOB)
977     {
978         // Floating item bobbing motion.
979         // Keep it on the floor.
980         mobj->origin[VZ] = mobj->floorZ;
981 
982         // Negative floorclip raises the mobj off the floor.
983         mobj->floorClip = -mobj->special1;
984         if (mobj->floorClip < -MAX_BOB_OFFSET)
985         {
986             // We don't want it going through the floor.
987             mobj->floorClip = -MAX_BOB_OFFSET;
988         }
989     }
990     else if (!FEQUAL(mobj->origin[VZ], mobj->floorZ) || NON_ZERO(mobj->mom[MZ]))
991     {
992         coord_t oldZ = mobj->origin[VZ];
993 
994         if (mobj->type == MT_POD)
995         {
996             // Use vanilla behavior for gas pods. The newer routines do not produce the
997             // correct behavior when pods interact with each other.
998             if ((mobj->onMobj = P_CheckOnmobj(mobj)) == NULL)
999             {
1000                 P_MobjMoveZ(mobj);
1001             }
1002             else
1003             {
1004                 // Stop pod's downward momentum when landing on something.
1005                 if (/*mobj->player &&*/ mobj->mom[VZ] < 0)
1006                 {
1007                     mobj->mom[VZ] = 0;
1008                 }
1009                 // This is exclusive to pods, so the code below is not relevant.
1010                 /*
1011                 if (mobj->player && (onmo->player || onmo->type == MT_POD))
1012                 {
1013                     mobj->mom[VX] = onmo->mom[VX];
1014                     mobj->mom[VY] = onmo->mom[VY];
1015                     if (onmo->origin[VZ] < onmo->floorZ)
1016                     {
1017                         mobj->origin[VZ] += onmo->floorZ - onmo->origin[VZ];
1018                         if (onmo->player)
1019                         {
1020                             onmo->player->viewHeight -= onmo->floorZ - onmo->origin[VZ];
1021                             onmo->player->viewHeightDelta =
1022                                 (VIEWHEIGHT - onmo->player->viewHeight) / 8;
1023                         }
1024                         onmo->origin[VZ] = onmo->floorZ;
1025                     }
1026                 }
1027                 */
1028             }
1029         }
1030         else
1031         {
1032             P_MobjMoveZ(mobj);
1033         }
1034 
1035         if (mobj->thinker.function != (thinkfunc_t) P_MobjThinker) return; // mobj was removed
1036 
1037         /**
1038          * @todo Instead of this post-move check, we should fix the root cause why
1039          * the SKULLFLYer is ending up in an invalid position during P_MobjMoveZ().
1040          * If only the movement validity checks weren't so convoluted... -jk
1041          */
1042         if ((mobj->flags & MF_SKULLFLY) && !P_CheckPosition(mobj, mobj->origin))
1043         {
1044             // Let's not get stuck.
1045             if (mobj->origin[VZ] > oldZ && mobj->mom[VZ] > 0) mobj->mom[VZ] = 0;
1046             if (mobj->origin[VZ] < oldZ && mobj->mom[VZ] < 0) mobj->mom[VZ] = 0;
1047             mobj->origin[VZ] = oldZ;
1048         }
1049     }
1050     // Non-sentient objects at rest.
1051     else if (!(NON_ZERO(mobj->mom[MX]) || NON_ZERO(mobj->mom[MY])) && !sentient(mobj) &&
1052              !mobj->player && !((mobj->flags & MF_CORPSE) && cfg.slidingCorpses))
1053     {
1054         /**
1055          * Objects fall off ledges if they are hanging off slightly push off
1056          * of ledge if hanging more than halfway off.
1057          */
1058 
1059         if (mobj->origin[VZ] > mobj->dropOffZ && // Only objects contacting dropoff
1060             !(mobj->flags & MF_NOGRAVITY) && cfg.fallOff)
1061         {
1062             P_ApplyTorque(mobj);
1063         }
1064         else
1065         {
1066             mobj->intFlags &= ~MIF_FALLING;
1067             mobj->gear = 0; // Reset torque.
1068         }
1069     }
1070 
1071     if (cfg.slidingCorpses)
1072     {
1073         if (((mobj->flags & MF_CORPSE)
1074                  ? mobj->origin[VZ] > mobj->dropOffZ
1075                  : mobj->origin[VZ] - mobj->dropOffZ > 24) && // Only objects contacting drop off.
1076             !(mobj->flags & MF_NOGRAVITY))                    // Only objects which fall.
1077         {
1078             P_ApplyTorque(mobj); // Apply torque.
1079         }
1080         else
1081         {
1082             mobj->intFlags &= ~MIF_FALLING;
1083             mobj->gear = 0; // Reset torque.
1084         }
1085     }
1086 
1087     // $vanish: dead monsters disappear after some time.
1088     if (cfg.corpseTime && (mobj->flags & MF_CORPSE) && mobj->corpseTics != -1)
1089     {
1090         if (++mobj->corpseTics < cfg.corpseTime * TICSPERSEC)
1091         {
1092             mobj->translucency = 0; // Opaque.
1093         }
1094         else if (mobj->corpseTics < cfg.corpseTime * TICSPERSEC + VANISHTICS)
1095         {
1096             // Translucent during vanishing.
1097             mobj->translucency =
1098                 ((mobj->corpseTics - cfg.corpseTime * TICSPERSEC) * 255) / VANISHTICS;
1099         }
1100         else
1101         {
1102             // Too long; get rid of the corpse.
1103             mobj->corpseTics = -1;
1104             return;
1105         }
1106     }
1107 
1108     // Cycle through states, calling action functions at transitions.
1109     if (mobj->tics != -1)
1110     {
1111         mobj->tics--;
1112 
1113         P_MobjAngleSRVOTicker(mobj); // "angle-servo"; smooth actor turning.
1114 
1115         // You can cycle through multiple states in a tic.
1116         if (!mobj->tics)
1117         {
1118             P_MobjClearSRVO(mobj);
1119             if (!P_MobjChangeState(mobj, mobj->state->nextState)) return; // Freed itself.
1120         }
1121     }
1122     else if (!IS_CLIENT)
1123     {
1124         // Check for nightmare respawn.
1125         if (!(mobj->flags & MF_COUNTKILL)) return;
1126 
1127         if (!gfw_Rule(respawnMonsters)) return;
1128 
1129         mobj->moveCount++;
1130 
1131         if (mobj->moveCount < 12 * 35) return;
1132 
1133         if (mapTime & 31) return;
1134 
1135         if (P_Random() > 4) return;
1136 
1137         P_NightmareRespawn(mobj);
1138     }
1139 }
1140 
1141 /**
1142  * Spawns a mobj of "type" at the specified position.
1143  */
P_SpawnMobjXYZ(mobjtype_t type,coord_t x,coord_t y,coord_t z,angle_t angle,int spawnFlags)1144 mobj_t* P_SpawnMobjXYZ(mobjtype_t type, coord_t x, coord_t y, coord_t z,
1145     angle_t angle, int spawnFlags)
1146 {
1147     mobj_t* mo;
1148     mobjinfo_t* info;
1149     coord_t space;
1150     int ddflags = 0;
1151 
1152     if(type < MT_FIRST || type >= Get(DD_NUMMOBJTYPES))
1153     {
1154 #ifdef _DEBUG
1155         Con_Error("P_SpawnMobj: Illegal mo type %i.\n", type);
1156 #endif
1157         return NULL;
1158     }
1159 
1160     info = &MOBJINFO[type];
1161 
1162     /*
1163     // Clients only spawn local objects.
1164     if(!(info->flags & MF_LOCAL) && IS_CLIENT)
1165         return NULL;
1166      */
1167 
1168     // Not for deathmatch?
1169     if(gfw_Rule(deathmatch) && (info->flags & MF_NOTDMATCH))
1170         return NULL;
1171 
1172     // Check for specific disabled objects.
1173     switch(type)
1174     {
1175     case MT_WSKULLROD:
1176     case MT_WPHOENIXROD:
1177     case MT_AMSKRDWIMPY:
1178     case MT_AMSKRDHEFTY:
1179     case MT_AMPHRDWIMPY:
1180     case MT_AMPHRDHEFTY:
1181     case MT_AMMACEWIMPY:
1182     case MT_AMMACEHEFTY:
1183     case MT_ARTISUPERHEAL:
1184     case MT_ARTITELEPORT:
1185     case MT_ITEMSHIELD2:
1186         if(gameMode == heretic_shareware)
1187         {
1188             return 0;// Don't place on map.
1189         }
1190         break;
1191 
1192     default:
1193         break;
1194     }
1195 
1196     // Don't spawn any monsters?
1197     if(gfw_Rule(noMonsters) && (info->flags & MF_COUNTKILL))
1198         return 0;
1199 
1200     if(info->flags & MF_SOLID)
1201         ddflags |= DDMF_SOLID;
1202     if(info->flags2 & MF2_DONTDRAW)
1203         ddflags |= DDMF_DONTDRAW;
1204 
1205     mo = Mobj_CreateXYZ(P_MobjThinker, x, y, z, angle, info->radius,
1206                       info->height, ddflags);
1207     mo->type = type;
1208     mo->info = info;
1209     mo->flags = info->flags;
1210     mo->flags2 = info->flags2;
1211     mo->flags3 = info->flags3;
1212     mo->damage = info->damage;
1213     mo->health = info->spawnHealth * (IS_NETGAME ? cfg.common.netMobHealthModifier : 1);
1214     mo->moveDir = DI_NODIR;
1215     mo->selector = 0;
1216     P_UpdateHealthBits(mo); // Set the health bits of the selector.
1217 
1218     if(gfw_Rule(skill) != SM_NIGHTMARE)
1219         mo->reactionTime = info->reactionTime;
1220 
1221     mo->lastLook = P_Random() % MAXPLAYERS;
1222 
1223     // Must link before setting state (ID assigned for the mo).
1224     Mobj_SetState(mo, P_GetState(mo->type, SN_SPAWN));
1225 
1226     if(mo->type == MT_MACEFX1 || mo->type == MT_MACEFX2 ||
1227        mo->type == MT_MACEFX3)
1228         mo->special3 = 1000;
1229 
1230     // Link the mobj into the world.
1231     P_MobjLink(mo);
1232 
1233     mo->floorZ   = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT);
1234     mo->dropOffZ = mo->floorZ;
1235     mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT);
1236 
1237     if((spawnFlags & MSF_Z_CEIL) || (info->flags & MF_SPAWNCEILING))
1238     {
1239         mo->origin[VZ] = mo->ceilingZ - mo->info->height - z;
1240     }
1241     else if((spawnFlags & MSF_Z_RANDOM) || (info->flags2 & MF2_SPAWNFLOAT))
1242     {
1243         space = mo->ceilingZ - mo->info->height - mo->floorZ;
1244         if(space > 48)
1245         {
1246             space -= 40;
1247             mo->origin[VZ] = ((space * P_Random()) / 256) + mo->floorZ + 40;
1248         }
1249         else
1250         {
1251             mo->origin[VZ] = mo->floorZ;
1252         }
1253     }
1254     else if(spawnFlags & MSF_Z_FLOOR)
1255     {
1256         mo->origin[VZ] = mo->floorZ + z;
1257     }
1258 
1259     if(spawnFlags & MSF_AMBUSH)
1260         mo->flags |= MF_AMBUSH;
1261 
1262     mo->floorClip = 0;
1263 
1264     if((mo->flags2 & MF2_FLOORCLIP) &&
1265        FEQUAL(mo->origin[VZ], P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT)))
1266     {
1267         const terraintype_t* tt = P_MobjFloorTerrain(mo);
1268 
1269         if(tt->flags & TTF_FLOORCLIP)
1270         {
1271             mo->floorClip = 10;
1272         }
1273     }
1274 
1275     // Copy spawn attributes to the new mobj.
1276     mo->spawnSpot.origin[VX] = x;
1277     mo->spawnSpot.origin[VY] = y;
1278     mo->spawnSpot.origin[VZ] = z;
1279     mo->spawnSpot.angle = angle;
1280     mo->spawnSpot.flags = spawnFlags;
1281 
1282     return mo;
1283 }
1284 
P_SpawnMobj(mobjtype_t type,coord_t const pos[3],angle_t angle,int spawnFlags)1285 mobj_t* P_SpawnMobj(mobjtype_t type, coord_t const pos[3], angle_t angle, int spawnFlags)
1286 {
1287     return P_SpawnMobjXYZ(type, pos[VX], pos[VY], pos[VZ], angle, spawnFlags);
1288 }
1289 
P_RepositionMace(mobj_t * mo)1290 void P_RepositionMace(mobj_t *mo)
1291 {
1292     mapspot_t const *mapSpot;
1293     Sector *sector;
1294 
1295     if (gfw_MapInfoFlags() & MIF_SPAWN_ALL_FIREMACES)
1296     {
1297         // Randomized Firemace spawning is disabled.
1298         return;
1299     }
1300 
1301     DENG_ASSERT(mo && mo->type == MT_WMACE);
1302     App_Log(DE2_DEV_MAP_MSG, "P_RepositionMace: Repositioning mobj [%p], thinkerId:%i", mo, mo->thinker.id);
1303 
1304     mapSpot = P_ChooseRandomMaceSpot();
1305     if(!mapSpot)
1306     {
1307         App_Log(DE2_DEV_MAP_WARNING, "P_RepositionMace: Failed to choose a map spot, aborting...");
1308         return;
1309     }
1310 
1311     P_MobjUnlink(mo);
1312     {
1313         mo->origin[VX] = mapSpot->origin[VX];
1314         mo->origin[VY] = mapSpot->origin[VY];
1315         sector = Sector_AtPoint_FixedPrecision(mo->origin);
1316 
1317         mo->floorZ = P_GetDoublep(sector, DMU_CEILING_HEIGHT);
1318         mo->origin[VZ] = mo->floorZ;
1319 
1320         mo->ceilingZ = P_GetDoublep(sector, DMU_CEILING_HEIGHT);
1321     }
1322     P_MobjLink(mo);
1323 
1324     App_Log(DE2_DEV_MAP_MSG, "P_RepositionMace: Mobj [%p], thinkerId:%i - now at (%.2f, %.2f, %.2f)",
1325             mo, mo->thinker.id, mo->origin[VX], mo->origin[VY], mo->origin[VZ]);
1326 }
1327 
P_SpawnBloodSplatter(coord_t x,coord_t y,coord_t z,mobj_t * originator)1328 void P_SpawnBloodSplatter(coord_t x, coord_t y, coord_t z, mobj_t* originator)
1329 {
1330     mobj_t* mo;
1331 
1332     if((mo = P_SpawnMobjXYZ(MT_BLOODSPLATTER, x, y, z, P_Random() << 24, 0)))
1333     {
1334         mo->target = originator;
1335         mo->mom[MX] = FIX2FLT((P_Random() - P_Random()) << 9);
1336         mo->mom[MY] = FIX2FLT((P_Random() - P_Random()) << 9);
1337         mo->mom[MZ] = 2;
1338     }
1339 }
1340 
1341 /**
1342  * @return @c true, if mobj contacted a non-solid floor.
1343  */
P_HitFloor(mobj_t * thing)1344 dd_bool P_HitFloor(mobj_t* thing)
1345 {
1346     mobj_t* mo;
1347     const terraintype_t* tt;
1348 
1349     if(IS_CLIENT && thing->player)
1350     {
1351         // The client notifies the server, which will handle the splash.
1352         NetCl_FloorHitRequest(thing->player);
1353         return false;
1354     }
1355 
1356     if(!FEQUAL(thing->floorZ, P_GetDoublep(Mobj_Sector(thing), DMU_FLOOR_HEIGHT)))
1357     {
1358         // Don't splash if landing on the edge above water/lava/etc...
1359         return false;
1360     }
1361 
1362     // Things that don't splash go here.
1363     switch(thing->type)
1364     {
1365     case MT_LAVASMOKE:
1366     case MT_SPLASH:
1367     case MT_SLUDGECHUNK:
1368         return false;
1369 
1370     default:
1371         if(P_MobjIsCamera(thing))
1372             return false;
1373         break;
1374     }
1375 
1376     tt = P_MobjFloorTerrain(thing);
1377     if(tt->flags & TTF_SPAWN_SPLASHES)
1378     {
1379         P_SpawnMobjXYZ(MT_SPLASHBASE, thing->origin[VX], thing->origin[VY],
1380                        0, thing->angle + ANG180, MSF_Z_FLOOR);
1381 
1382         if((mo = P_SpawnMobjXYZ(MT_SPLASH, thing->origin[VX], thing->origin[VY], 0,
1383                                 thing->angle, MSF_Z_FLOOR)))
1384         {
1385             mo->target = thing;
1386             mo->mom[MX] = FIX2FLT((P_Random() - P_Random()) << 8);
1387             mo->mom[MY] = FIX2FLT((P_Random() - P_Random()) << 8);
1388             mo->mom[MZ] = 2 + FIX2FLT(P_Random() << 8);
1389 
1390             S_StartSound(SFX_GLOOP, mo);
1391         }
1392 
1393         return true;
1394     }
1395     else if(tt->flags & TTF_SPAWN_SMOKE)
1396     {
1397         P_SpawnMobjXYZ(MT_LAVASPLASH, thing->origin[VX], thing->origin[VY], 0,
1398                        thing->angle + ANG180, MSF_Z_FLOOR);
1399 
1400         if((mo = P_SpawnMobjXYZ(MT_LAVASMOKE, thing->origin[VX], thing->origin[VY], 0,
1401                                 P_Random() << 24, MSF_Z_FLOOR)))
1402         {
1403             mo->mom[MZ] = 1 + FIX2FLT((P_Random() << 7));
1404 
1405             S_StartSound(SFX_BURN, mo);
1406         }
1407 
1408         return true;
1409     }
1410     else if(tt->flags & TTF_SPAWN_SLUDGE)
1411     {
1412         P_SpawnMobjXYZ(MT_SLUDGESPLASH, thing->origin[VX], thing->origin[VY], 0,
1413                        thing->angle + ANG180, MSF_Z_FLOOR);
1414 
1415         if((mo = P_SpawnMobjXYZ(MT_SLUDGECHUNK, thing->origin[VX], thing->origin[VY], 0,
1416                                 P_Random() << 24, MSF_Z_FLOOR)))
1417         {
1418             mo->target = thing;
1419             mo->mom[MX] = FIX2FLT((P_Random() - P_Random()) << 8);
1420             mo->mom[MY] = FIX2FLT((P_Random() - P_Random()) << 8);
1421             mo->mom[MZ] = 1 + FIX2FLT(P_Random() << 8);
1422         }
1423         return true;
1424     }
1425 
1426     return false;
1427 }
1428 
1429 /**
1430  * @return  @c true, if the missile is at a valid spawn point,
1431  *          otherwise; explode it and return @false.
1432  */
P_CheckMissileSpawn(mobj_t * mo)1433 dd_bool P_CheckMissileSpawn(mobj_t *mo)
1434 {
1435     // Move a little forward so an angle can be computed if it immediately
1436     // explodes
1437     P_MobjUnlink(mo);
1438     if(mo->type == MT_BLASTERFX1)
1439     {
1440         // Ultra-fast ripper spawning missile.
1441         mo->origin[VX] += mo->mom[MX] / 8;
1442         mo->origin[VY] += mo->mom[MY] / 8;
1443         mo->origin[VZ] += mo->mom[MZ] / 8;
1444     }
1445     else
1446     {
1447         mo->origin[VX] += mo->mom[MX] / 2;
1448         mo->origin[VY] += mo->mom[MY] / 2;
1449         mo->origin[VZ] += mo->mom[MZ] / 2;
1450     }
1451     P_MobjLink(mo);
1452 
1453     if(!P_TryMoveXY(mo, mo->origin[VX], mo->origin[VY], false, false))
1454     {
1455         P_ExplodeMissile(mo);
1456         return false;
1457     }
1458 
1459     return true;
1460 }
1461 
P_SpawnMissile(mobjtype_t type,mobj_t * source,mobj_t * dest,dd_bool checkSpawn)1462 mobj_t* P_SpawnMissile(mobjtype_t type, mobj_t* source, mobj_t* dest, dd_bool checkSpawn)
1463 {
1464     coord_t pos[3];
1465     mobj_t* th = 0;
1466     unsigned int an = 0;
1467     angle_t angle = 0;
1468     coord_t dist = 0;
1469     float slope = 0;
1470     coord_t spawnZOff = 0;
1471     int spawnFlags = 0;
1472 
1473     memcpy(pos, source->origin, sizeof(pos));
1474 
1475     if(source->player)
1476     {
1477         // see which target is to be aimed at
1478         angle = source->angle;
1479         slope = P_AimLineAttack(source, angle, 16 * 64);
1480         if(!cfg.common.noAutoAim)
1481             if(!lineTarget)
1482             {
1483                 angle += 1 << 26;
1484                 slope = P_AimLineAttack(source, angle, 16 * 64);
1485                 if(!lineTarget)
1486                 {
1487                     angle -= 2 << 26;
1488                     slope = P_AimLineAttack(source, angle, 16 * 64);
1489                 }
1490 
1491                 if(!lineTarget)
1492                 {
1493                     angle = source->angle;
1494                     slope =
1495                         tan(LOOKDIR2RAD(source->dPlayer->lookDir)) / 1.2f;
1496                 }
1497             }
1498 
1499         if(!P_MobjIsCamera(source->player->plr->mo))
1500             spawnZOff = cfg.common.plrViewHeight - 9 +
1501                 source->player->plr->lookDir / 173;
1502     }
1503     else
1504     {
1505         // Type specific offset to spawn height z.
1506         switch(type)
1507         {
1508         case MT_MNTRFX1: // Minotaur swing attack missile.
1509             spawnZOff = 40;
1510             break;
1511 
1512         case MT_SRCRFX1: // Sorcerer Demon fireball.
1513             spawnZOff = 48;
1514             break;
1515 
1516         case MT_KNIGHTAXE: // Knight normal axe.
1517         case MT_REDAXE: // Knight red power axe.
1518             spawnZOff = 36;
1519             break;
1520 
1521         case MT_MNTRFX2:
1522             spawnZOff = 0;
1523             break;
1524 
1525         default:
1526             spawnZOff = 32;
1527             break;
1528         }
1529     }
1530 
1531     if(type == MT_MNTRFX2) // always exactly on the floor.
1532     {
1533         pos[VZ] = 0;
1534         spawnFlags |= MSF_Z_FLOOR;
1535     }
1536     else
1537     {
1538         pos[VZ] += spawnZOff;
1539         pos[VZ] -= source->floorClip;
1540     }
1541 
1542     if(!source->player)
1543     {
1544         angle = M_PointToAngle2(pos, dest->origin);
1545         // Fuzzy player.
1546         if(dest->flags & MF_SHADOW)
1547             angle += (P_Random() - P_Random()) << 21; // note << 20 in jDoom
1548     }
1549 
1550     if(!(th = P_SpawnMobj(type, pos, angle, spawnFlags)))
1551         return NULL;
1552 
1553     if(th->info->seeSound)
1554         S_StartSound(th->info->seeSound, th);
1555 
1556     th->target = source; // Where it came from.
1557     an = angle >> ANGLETOFINESHIFT;
1558     th->mom[MX] = th->info->speed * FIX2FLT(finecosine[an]);
1559     th->mom[MY] = th->info->speed * FIX2FLT(finesine[an]);
1560 
1561     if(source->player)
1562     {
1563         th->mom[MZ] = th->info->speed * slope;
1564     }
1565     else
1566     {
1567         dist = M_ApproxDistance(dest->origin[VX] - pos[VX],
1568                                 dest->origin[VY] - pos[VY]);
1569         dist /= th->info->speed;
1570         if(dist < 1)
1571             dist = 1;
1572         th->mom[MZ] = (dest->origin[VZ] - source->origin[VZ]) / dist;
1573     }
1574 
1575     // Make sure the speed is right (in 3D).
1576     dist = M_ApproxDistance(M_ApproxDistance(th->mom[MX], th->mom[MY]),
1577                             th->mom[MZ]);
1578     if(!dist)
1579         dist = 1;
1580     dist = th->info->speed / dist;
1581 
1582     th->mom[MX] *= dist;
1583     th->mom[MY] *= dist;
1584     th->mom[MZ] *= dist;
1585 
1586 //#if __JHERETIC__
1587     /// @kludge Set this global ptr as we need access to the mobj even if it
1588     ///         explodes instantly in order to assign values to it.
1589     missileMobj = th;
1590     // kludge end.
1591 //#endif
1592 
1593     if(checkSpawn)
1594         return (P_CheckMissileSpawn(th)? th : NULL);
1595 
1596     return th;
1597 }
1598 
Vanilla_P_SpawnMissileAngle(mobj_t * source,mobjtype_t type,angle_t angle,coord_t momZ)1599 mobj_t *Vanilla_P_SpawnMissileAngle(mobj_t *source, mobjtype_t type, angle_t angle, coord_t momZ)
1600 {
1601     /*
1602      * NOTE: This function is intended to exactly replicate vanilla Heretic
1603      * behavior. Do not modify!
1604      */
1605 
1606     coord_t pos[3] = { source->origin[VX], source->origin[VY], source->origin[VZ] + 32 };
1607     mobj_t *mo;
1608     int spawnFlags = 0;
1609 
1610     // Determine missile spawn position.
1611     switch(type)
1612     {
1613     case MT_MNTRFX1: // Minotaur swing attack missile
1614         pos[VZ] = source->origin[VZ] + 40;
1615         break;
1616 
1617     case MT_MNTRFX2: // Minotaur floor fire missile
1618         spawnFlags |= MSF_Z_FLOOR;
1619         break;
1620 
1621     case MT_SRCRFX1: // Sorcerer Demon fireball
1622         pos[VZ] = source->origin[VZ] + 48;
1623         break;
1624 
1625     default:
1626         break;
1627     }
1628 
1629     pos[VZ] -= source->floorClip;
1630 
1631     mo = P_SpawnMobj(type, pos, angle, spawnFlags);
1632 
1633     mo->target = source; // Originator
1634     mo->angle = angle;
1635     angle >>= ANGLETOFINESHIFT;
1636     mo->mom[VX] = mo->info->speed * FIX2FLT(finecosine[angle]);
1637     mo->mom[VY] = mo->info->speed * FIX2FLT(finesine[angle]);
1638     mo->mom[VZ] = momZ;
1639 
1640     if(mo->info->seeSound)
1641     {
1642         S_StartSound(mo->info->seeSound, mo);
1643     }
1644 
1645     return (P_CheckMissileSpawn(mo)? mo : NULL);
1646 }
1647 
P_SpawnMissileAngle(mobjtype_t type,mobj_t * source,angle_t mangle,coord_t momZ)1648 mobj_t* P_SpawnMissileAngle(mobjtype_t type, mobj_t* source, angle_t mangle, coord_t momZ)
1649 {
1650     coord_t pos[3];
1651     mobj_t* th = 0;
1652     unsigned int an = 0;
1653     angle_t angle = 0;
1654     coord_t dist = 0;
1655     float slope = 0;
1656     coord_t spawnZOff = 0;
1657     int spawnFlags = 0;
1658 
1659     memcpy(pos, source->origin, sizeof(pos));
1660 
1661     angle = mangle;
1662     if(source->player)
1663     {
1664         // Try to find a target.
1665         slope = P_AimLineAttack(source, angle, 16 * 64);
1666         if(!cfg.common.noAutoAim)
1667             if(!lineTarget)
1668             {
1669                 angle += 1 << 26;
1670                 slope = P_AimLineAttack(source, angle, 16 * 64);
1671                 if(!lineTarget)
1672                 {
1673                     angle -= 2 << 26;
1674                     slope = P_AimLineAttack(source, angle, 16 * 64);
1675                 }
1676 
1677                 if(!lineTarget)
1678                 {
1679                     angle = mangle;
1680                     slope =
1681                         tan(LOOKDIR2RAD(source->dPlayer->lookDir)) / 1.2f;
1682                 }
1683             }
1684 
1685         if(!(source->player->plr->flags & DDPF_CAMERA))
1686             spawnZOff = cfg.common.plrViewHeight - 9 +
1687                         (source->player->plr->lookDir) / 173;
1688     }
1689     else
1690     {
1691         // Type specific offset to spawn height z.
1692         switch(type)
1693         {
1694         case MT_MNTRFX1: // Minotaur swing attack missile.
1695             spawnZOff = 40;
1696             break;
1697 
1698         case MT_SRCRFX1: // Sorcerer Demon fireball.
1699             spawnZOff = 48;
1700             break;
1701 
1702         case MT_KNIGHTAXE: // Knight normal axe.
1703         case MT_REDAXE: // Knight red power axe.
1704             spawnZOff = 36;
1705             break;
1706 
1707         default:
1708             spawnZOff = 32;
1709             break;
1710         }
1711     }
1712 
1713     if(type == MT_MNTRFX2) // Always exactly on the floor.
1714     {
1715         spawnFlags |= MSF_Z_FLOOR;
1716     }
1717     else
1718     {
1719         pos[VZ] += spawnZOff;
1720         pos[VZ] -= source->floorClip;
1721     }
1722 
1723     if(!(th = P_SpawnMobj(type, pos, angle, spawnFlags)))
1724         return NULL;
1725 
1726     if(th->info->seeSound)
1727         S_StartSound(th->info->seeSound, th);
1728 
1729     th->target = source; // Where it came from.
1730     an = angle >> ANGLETOFINESHIFT;
1731     th->mom[MX] = th->info->speed * FIX2FLT(finecosine[an]);
1732     th->mom[MY] = th->info->speed * FIX2FLT(finesine[an]);
1733 
1734     if(source->player && momZ == -12345)
1735     {
1736         th->mom[MZ] = th->info->speed * slope;
1737 
1738         // Make sure the speed is right (in 3D).
1739         dist = M_ApproxDistance(M_ApproxDistance(th->mom[MX], th->mom[MY]),
1740                                 th->mom[MZ]);
1741         if(dist < 1)
1742             dist = 1;
1743         dist = th->info->speed / dist;
1744 
1745         th->mom[MX] *= dist;
1746         th->mom[MY] *= dist;
1747         th->mom[MZ] *= dist;
1748     }
1749     else
1750     {
1751         th->mom[MZ] = momZ;
1752     }
1753 
1754 //#if __JHERETIC__
1755     /// @kludge Set this global ptr as we need access to the mobj even if it
1756     ///         explodes instantly in order to assign values to it.
1757     missileMobj = th;
1758     // kludge end.
1759 //#endif
1760 
1761     if(P_CheckMissileSpawn(th))
1762         return th;
1763 
1764     return NULL;
1765 }
1766 
A_ContMobjSound(mobj_t * actor)1767 void C_DECL A_ContMobjSound(mobj_t* actor)
1768 {
1769     switch(actor->type)
1770     {
1771     case MT_KNIGHTAXE:
1772         S_StartSound(SFX_KGTATK, actor);
1773         break;
1774 
1775     case MT_MUMMYFX1:
1776         S_StartSound(SFX_MUMHED, actor);
1777         break;
1778 
1779     default:
1780         break;
1781     }
1782 }
1783