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