1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard, Andrey Budko
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 // Movement, collision handling.
17 // Shooting and aiming.
18 //
19
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "deh_misc.h"
24
25 #include "m_bbox.h"
26 #include "m_random.h"
27 #include "i_system.h"
28
29 #include "doomdef.h"
30 #include "m_argv.h"
31 #include "m_misc.h"
32 #include "p_local.h"
33
34 #include "s_sound.h"
35
36 // State.
37 #include "doomstat.h"
38 #include "r_state.h"
39 // Data.
40 #include "sounds.h"
41
42 // Spechit overrun magic value.
43 //
44 // This is the value used by PrBoom-plus. I think the value below is
45 // actually better and works with more demos. However, I think
46 // it's better for the spechits emulation to be compatible with
47 // PrBoom-plus, at least so that the big spechits emulation list
48 // on Doomworld can also be used with Chocolate Doom.
49
50 #define DEFAULT_SPECHIT_MAGIC 0x01C09C98
51
52 // This is from a post by myk on the Doomworld forums,
53 // outputted from entryway's spechit_magic generator for
54 // s205n546.lmp. The _exact_ value of this isn't too
55 // important; as long as it is in the right general
56 // range, it will usually work. Otherwise, we can use
57 // the generator (hacked doom2.exe) and provide it
58 // with -spechit.
59
60 //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8
61
62
63 fixed_t tmbbox[4];
64 mobj_t* tmthing;
65 int tmflags;
66 fixed_t tmx;
67 fixed_t tmy;
68
69
70 // If "floatok" true, move would be ok
71 // if within "tmfloorz - tmceilingz".
72 boolean floatok;
73
74 fixed_t tmfloorz;
75 fixed_t tmceilingz;
76 fixed_t tmdropoffz;
77
78 // keep track of the line that lowers the ceiling,
79 // so missiles don't explode against sky hack walls
80 line_t* ceilingline;
81
82 // keep track of special lines as they are hit,
83 // but don't process them until the move is proven valid
84
85 line_t** spechit; // [crispy] remove SPECHIT limit
86 int numspechit;
87 static int spechit_max; // [crispy] remove SPECHIT limit
88
89
90
91 //
92 // TELEPORT MOVE
93 //
94
95 //
96 // PIT_StompThing
97 //
PIT_StompThing(mobj_t * thing)98 boolean PIT_StompThing (mobj_t* thing)
99 {
100 fixed_t blockdist;
101
102 if (!(thing->flags & MF_SHOOTABLE) )
103 return true;
104
105 blockdist = thing->radius + tmthing->radius;
106
107 if ( abs(thing->x - tmx) >= blockdist
108 || abs(thing->y - tmy) >= blockdist )
109 {
110 // didn't hit it
111 return true;
112 }
113
114 // don't clip against self
115 if (thing == tmthing)
116 return true;
117
118 // monsters don't stomp things except on boss level
119 if ( !tmthing->player && gamemap != 30)
120 return false;
121
122 P_DamageMobj (thing, tmthing, tmthing, 10000);
123
124 return true;
125 }
126
127
128 //
129 // P_TeleportMove
130 //
131 boolean
P_TeleportMove(mobj_t * thing,fixed_t x,fixed_t y)132 P_TeleportMove
133 ( mobj_t* thing,
134 fixed_t x,
135 fixed_t y )
136 {
137 int xl;
138 int xh;
139 int yl;
140 int yh;
141 int bx;
142 int by;
143
144 subsector_t* newsubsec;
145
146 // kill anything occupying the position
147 tmthing = thing;
148 tmflags = thing->flags;
149
150 tmx = x;
151 tmy = y;
152
153 tmbbox[BOXTOP] = y + tmthing->radius;
154 tmbbox[BOXBOTTOM] = y - tmthing->radius;
155 tmbbox[BOXRIGHT] = x + tmthing->radius;
156 tmbbox[BOXLEFT] = x - tmthing->radius;
157
158 newsubsec = R_PointInSubsector (x,y);
159 ceilingline = NULL;
160
161 // The base floor/ceiling is from the subsector
162 // that contains the point.
163 // Any contacted lines the step closer together
164 // will adjust them.
165 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
166 tmceilingz = newsubsec->sector->ceilingheight;
167
168 validcount++;
169 numspechit = 0;
170
171 // stomp on any things contacted
172 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
173 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
174 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
175 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
176
177 for (bx=xl ; bx<=xh ; bx++)
178 for (by=yl ; by<=yh ; by++)
179 if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
180 return false;
181
182 // the move is ok,
183 // so link the thing into its new position
184 P_UnsetThingPosition (thing);
185
186 thing->floorz = tmfloorz;
187 thing->ceilingz = tmceilingz;
188 thing->x = x;
189 thing->y = y;
190
191 // [AM] Don't interpolate mobjs that pass
192 // through teleporters
193 thing->interp = false;
194
195 P_SetThingPosition (thing);
196
197 return true;
198 }
199
200
201 //
202 // MOVEMENT ITERATOR FUNCTIONS
203 //
204
205 static void SpechitOverrun(line_t *ld);
206
207 //
208 // PIT_CheckLine
209 // Adjusts tmfloorz and tmceilingz as lines are contacted
210 //
PIT_CheckLine(line_t * ld)211 boolean PIT_CheckLine (line_t* ld)
212 {
213 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
214 || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
215 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
216 || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
217 return true;
218
219 if (P_BoxOnLineSide (tmbbox, ld) != -1)
220 return true;
221
222 // A line has been hit
223
224 // The moving thing's destination position will cross
225 // the given line.
226 // If this should not be allowed, return false.
227 // If the line is special, keep track of it
228 // to process later if the move is proven ok.
229 // NOTE: specials are NOT sorted by order,
230 // so two special lines that are only 8 pixels apart
231 // could be crossed in either order.
232
233 if (!ld->backsector)
234 return false; // one sided line
235
236 if (!(tmthing->flags & MF_MISSILE) )
237 {
238 if ( ld->flags & ML_BLOCKING )
239 return false; // explicitly blocking everything
240
241 if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )
242 return false; // block monsters only
243 }
244
245 // set openrange, opentop, openbottom
246 P_LineOpening (ld);
247
248 // adjust floor / ceiling heights
249 if (opentop < tmceilingz)
250 {
251 tmceilingz = opentop;
252 ceilingline = ld;
253 }
254
255 if (openbottom > tmfloorz)
256 tmfloorz = openbottom;
257
258 if (lowfloor < tmdropoffz)
259 tmdropoffz = lowfloor;
260
261 // if contacted a special line, add it to the list
262 if (ld->special)
263 {
264 // [crispy] remove SPECHIT limit
265 if (numspechit >= spechit_max)
266 {
267 spechit_max = spechit_max ? spechit_max * 2 : MAXSPECIALCROSS;
268 spechit = I_Realloc(spechit, sizeof(*spechit) * spechit_max);
269 }
270 spechit[numspechit] = ld;
271 numspechit++;
272
273 // fraggle: spechits overrun emulation code from prboom-plus
274 if (numspechit > MAXSPECIALCROSS_ORIGINAL)
275 {
276 // [crispy] print a warning
277 if (numspechit == MAXSPECIALCROSS_ORIGINAL + 1)
278 fprintf(stderr, "PIT_CheckLine: Triggered SPECHITS overflow!\n");
279 SpechitOverrun(ld);
280 }
281 }
282
283 return true;
284 }
285
286 //
287 // PIT_CheckThing
288 //
PIT_CheckThing(mobj_t * thing)289 boolean PIT_CheckThing (mobj_t* thing)
290 {
291 fixed_t blockdist;
292 boolean solid;
293 boolean unblocking = false;
294 int damage;
295
296 if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
297 return true;
298
299 blockdist = thing->radius + tmthing->radius;
300
301 if ( abs(thing->x - tmx) >= blockdist
302 || abs(thing->y - tmy) >= blockdist )
303 {
304 // didn't hit it
305 return true;
306 }
307
308 // don't clip against self
309 if (thing == tmthing)
310 return true;
311
312 // check for skulls slamming into things
313 if (tmthing->flags & MF_SKULLFLY)
314 {
315 // [crispy] check if attacking skull flies over player
316 if (critical->overunder && thing->player)
317 {
318 if (tmthing->z > thing->z + thing->height)
319 {
320 return true;
321 }
322 }
323
324 damage = ((P_Random()%8)+1)*tmthing->info->damage;
325
326 P_DamageMobj (thing, tmthing, tmthing, damage);
327
328 tmthing->flags &= ~MF_SKULLFLY;
329 tmthing->momx = tmthing->momy = tmthing->momz = 0;
330
331 P_SetMobjState (tmthing, tmthing->info->spawnstate);
332
333 return false; // stop moving
334 }
335
336
337 // missiles can hit other things
338 if (tmthing->flags & MF_MISSILE)
339 {
340 // [crispy] mobj or actual sprite height
341 const fixed_t thingheight = (tmthing->target && tmthing->target->player &&
342 critical->freeaim == FREEAIM_DIRECT) ?
343 thing->info->actualheight : thing->height;
344 // see if it went over / under
345 if (tmthing->z > thing->z + thingheight)
346 return true; // overhead
347 if (tmthing->z+tmthing->height < thing->z)
348 return true; // underneath
349
350 if (tmthing->target
351 && (tmthing->target->type == thing->type ||
352 (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
353 (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
354 {
355 // Don't hit same species as originator.
356 if (thing == tmthing->target)
357 return true;
358
359 // sdh: Add deh_species_infighting here. We can override the
360 // "monsters of the same species cant hurt each other" behavior
361 // through dehacked patches
362
363 if (thing->type != MT_PLAYER && !deh_species_infighting)
364 {
365 // Explode, but do no damage.
366 // Let players missile other players.
367 return false;
368 }
369 }
370
371 if (! (thing->flags & MF_SHOOTABLE) )
372 {
373 // didn't do any damage
374 return !(thing->flags & MF_SOLID);
375 }
376
377 // damage / explode
378 damage = ((P_Random()%8)+1)*tmthing->info->damage;
379 P_DamageMobj (thing, tmthing, tmthing->target, damage);
380
381 // don't traverse any more
382 return false;
383 }
384
385 // check for special pickup
386 if (thing->flags & MF_SPECIAL)
387 {
388 solid = (thing->flags & MF_SOLID) != 0;
389 if (tmflags&MF_PICKUP)
390 {
391 // can remove thing
392 P_TouchSpecialThing (thing, tmthing);
393 }
394 return !solid;
395 }
396
397 if (critical->overunder)
398 {
399 // [crispy] a solid hanging body will allow sufficiently small things underneath it
400 if (thing->flags & MF_SOLID && thing->flags & MF_SPAWNCEILING)
401 {
402 if (tmthing->z + tmthing->height <= thing->z)
403 {
404 if (thing->z < tmceilingz)
405 {
406 tmceilingz = thing->z;
407 }
408 return true;
409 }
410 }
411
412 // [crispy] allow players to walk over/under shootable objects
413 if (tmthing->player && thing->flags & MF_SHOOTABLE)
414 {
415 // [crispy] allow the usual 24 units step-up even across monsters' heads,
416 // only if the current height has not been reached by "low" jumping
417 fixed_t step_up = tmthing->player->jumpTics > 7 ? 0 : 24*FRACUNIT;
418
419 if (tmthing->z + step_up >= thing->z + thing->height)
420 {
421 // player walks over object
422 tmfloorz = MAX(thing->z + thing->height, tmfloorz);
423 thing->ceilingz = MIN(tmthing->z, thing->ceilingz);
424 return true;
425 }
426 else
427 if (tmthing->z + tmthing->height <= thing->z)
428 {
429 // player walks underneath object
430 tmceilingz = MIN(thing->z, tmceilingz);
431 thing->floorz = MAX(tmthing->z + tmthing->height, thing->floorz);
432 return true;
433 }
434
435 // [crispy] check if things are stuck and allow them to move further apart
436 // taken from doomretro/src/p_map.c:319-332
437 if (tmx == tmthing->x && tmy == tmthing->y)
438 {
439 unblocking = true;
440 }
441 else
442 {
443 fixed_t newdist = P_AproxDistance(thing->x - tmx, thing->y - tmy);
444 fixed_t olddist = P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y);
445
446 if (newdist > olddist)
447 {
448 unblocking = (tmthing->z < thing->z + thing->height
449 && tmthing->z + tmthing->height > thing->z);
450 }
451 }
452 }
453 }
454
455 return !(thing->flags & MF_SOLID) || unblocking;
456 }
457
458
459 //
460 // MOVEMENT CLIPPING
461 //
462
463 //
464 // P_CheckPosition
465 // This is purely informative, nothing is modified
466 // (except things picked up).
467 //
468 // in:
469 // a mobj_t (can be valid or invalid)
470 // a position to be checked
471 // (doesn't need to be related to the mobj_t->x,y)
472 //
473 // during:
474 // special things are touched if MF_PICKUP
475 // early out on solid lines?
476 //
477 // out:
478 // newsubsec
479 // floorz
480 // ceilingz
481 // tmdropoffz
482 // the lowest point contacted
483 // (monsters won't move to a dropoff)
484 // speciallines[]
485 // numspeciallines
486 //
487 boolean
P_CheckPosition(mobj_t * thing,fixed_t x,fixed_t y)488 P_CheckPosition
489 ( mobj_t* thing,
490 fixed_t x,
491 fixed_t y )
492 {
493 int xl;
494 int xh;
495 int yl;
496 int yh;
497 int bx;
498 int by;
499 subsector_t* newsubsec;
500
501 tmthing = thing;
502 tmflags = thing->flags;
503
504 tmx = x;
505 tmy = y;
506
507 tmbbox[BOXTOP] = y + tmthing->radius;
508 tmbbox[BOXBOTTOM] = y - tmthing->radius;
509 tmbbox[BOXRIGHT] = x + tmthing->radius;
510 tmbbox[BOXLEFT] = x - tmthing->radius;
511
512 newsubsec = R_PointInSubsector (x,y);
513 ceilingline = NULL;
514
515 // The base floor / ceiling is from the subsector
516 // that contains the point.
517 // Any contacted lines the step closer together
518 // will adjust them.
519 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
520 tmceilingz = newsubsec->sector->ceilingheight;
521
522 validcount++;
523 numspechit = 0;
524
525 if ( tmflags & MF_NOCLIP )
526 return true;
527
528 // Check things first, possibly picking things up.
529 // The bounding box is extended by MAXRADIUS
530 // because mobj_ts are grouped into mapblocks
531 // based on their origin point, and can overlap
532 // into adjacent blocks by up to MAXRADIUS units.
533 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
534 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
535 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
536 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
537
538 for (bx=xl ; bx<=xh ; bx++)
539 for (by=yl ; by<=yh ; by++)
540 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
541 return false;
542
543 // check lines
544 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
545 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
546 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
547 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
548
549 for (bx=xl ; bx<=xh ; bx++)
550 for (by=yl ; by<=yh ; by++)
551 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
552 return false;
553
554 return true;
555 }
556
557
558 //
559 // P_TryMove
560 // Attempt to move to a new position,
561 // crossing special lines unless MF_TELEPORT is set.
562 //
563 boolean
P_TryMove(mobj_t * thing,fixed_t x,fixed_t y)564 P_TryMove
565 ( mobj_t* thing,
566 fixed_t x,
567 fixed_t y )
568 {
569 fixed_t oldx;
570 fixed_t oldy;
571 int side;
572 int oldside;
573 line_t* ld;
574
575 floatok = false;
576 if (!P_CheckPosition (thing, x, y))
577 return false; // solid wall or thing
578
579 if ( !(thing->flags & MF_NOCLIP) )
580 {
581 if (tmceilingz - tmfloorz < thing->height)
582 return false; // doesn't fit
583
584 floatok = true;
585
586 if ( !(thing->flags&MF_TELEPORT)
587 &&tmceilingz - thing->z < thing->height)
588 return false; // mobj must lower itself to fit
589
590 if ( !(thing->flags&MF_TELEPORT)
591 && tmfloorz - thing->z > 24*FRACUNIT )
592 return false; // too big a step up
593
594 if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
595 && tmfloorz - tmdropoffz > 24*FRACUNIT )
596 return false; // don't stand over a dropoff
597 }
598
599 // the move is ok,
600 // so link the thing into its new position
601 P_UnsetThingPosition (thing);
602
603 oldx = thing->x;
604 oldy = thing->y;
605 thing->floorz = tmfloorz;
606 thing->ceilingz = tmceilingz;
607 thing->x = x;
608 thing->y = y;
609
610 P_SetThingPosition (thing);
611
612 // if any special lines were hit, do the effect
613 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
614 {
615 while (numspechit--)
616 {
617 // see if the line was crossed
618 ld = spechit[numspechit];
619 side = P_PointOnLineSide (thing->x, thing->y, ld);
620 oldside = P_PointOnLineSide (oldx, oldy, ld);
621 if (side != oldside)
622 {
623 if (ld->special)
624 P_CrossSpecialLine (ld-lines, oldside, thing);
625 }
626 }
627 }
628
629 return true;
630 }
631
632
633 //
634 // P_ThingHeightClip
635 // Takes a valid thing and adjusts the thing->floorz,
636 // thing->ceilingz, and possibly thing->z.
637 // This is called for all nearby monsters
638 // whenever a sector changes height.
639 // If the thing doesn't fit,
640 // the z will be set to the lowest value
641 // and false will be returned.
642 //
P_ThingHeightClip(mobj_t * thing)643 boolean P_ThingHeightClip (mobj_t* thing)
644 {
645 boolean onfloor;
646
647 onfloor = (thing->z == thing->floorz);
648
649 P_CheckPosition (thing, thing->x, thing->y);
650 // what about stranding a monster partially off an edge?
651
652 thing->floorz = tmfloorz;
653 thing->ceilingz = tmceilingz;
654
655 if (onfloor)
656 {
657 // walking monsters rise and fall with the floor
658 thing->z = thing->floorz;
659 }
660 else
661 {
662 // don't adjust a floating monster unless forced to
663 if (thing->z+thing->height > thing->ceilingz)
664 thing->z = thing->ceilingz - thing->height;
665 }
666
667 if (thing->ceilingz - thing->floorz < thing->height)
668 return false;
669
670 return true;
671 }
672
673
674
675 //
676 // SLIDE MOVE
677 // Allows the player to slide along any angled walls.
678 //
679 fixed_t bestslidefrac;
680 fixed_t secondslidefrac;
681
682 line_t* bestslideline;
683 line_t* secondslideline;
684
685 mobj_t* slidemo;
686
687 fixed_t tmxmove;
688 fixed_t tmymove;
689
690
691
692 //
693 // P_HitSlideLine
694 // Adjusts the xmove / ymove
695 // so that the next move will slide along the wall.
696 //
P_HitSlideLine(line_t * ld)697 void P_HitSlideLine (line_t* ld)
698 {
699 int side;
700
701 angle_t lineangle;
702 angle_t moveangle;
703 angle_t deltaangle;
704
705 fixed_t movelen;
706 fixed_t newlen;
707
708
709 if (ld->slopetype == ST_HORIZONTAL)
710 {
711 tmymove = 0;
712 return;
713 }
714
715 if (ld->slopetype == ST_VERTICAL)
716 {
717 tmxmove = 0;
718 return;
719 }
720
721 side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
722
723 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
724
725 if (side == 1)
726 lineangle += ANG180;
727
728 moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
729 deltaangle = moveangle-lineangle;
730
731 if (deltaangle > ANG180)
732 deltaangle += ANG180;
733 // I_Error ("SlideLine: ang>ANG180");
734
735 lineangle >>= ANGLETOFINESHIFT;
736 deltaangle >>= ANGLETOFINESHIFT;
737
738 movelen = P_AproxDistance (tmxmove, tmymove);
739 newlen = FixedMul (movelen, finecosine[deltaangle]);
740
741 tmxmove = FixedMul (newlen, finecosine[lineangle]);
742 tmymove = FixedMul (newlen, finesine[lineangle]);
743 }
744
745
746 //
747 // PTR_SlideTraverse
748 //
PTR_SlideTraverse(intercept_t * in)749 boolean PTR_SlideTraverse (intercept_t* in)
750 {
751 line_t* li;
752
753 if (!in->isaline)
754 I_Error ("PTR_SlideTraverse: not a line?");
755
756 li = in->d.line;
757
758 if ( ! (li->flags & ML_TWOSIDED) )
759 {
760 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
761 {
762 // don't hit the back side
763 return true;
764 }
765 goto isblocking;
766 }
767
768 // set openrange, opentop, openbottom
769 P_LineOpening (li);
770
771 if (openrange < slidemo->height)
772 goto isblocking; // doesn't fit
773
774 if (opentop - slidemo->z < slidemo->height)
775 goto isblocking; // mobj is too high
776
777 if (openbottom - slidemo->z > 24*FRACUNIT )
778 goto isblocking; // too big a step up
779
780 // this line doesn't block movement
781 return true;
782
783 // the line does block movement,
784 // see if it is closer than best so far
785 isblocking:
786 if (in->frac < bestslidefrac)
787 {
788 secondslidefrac = bestslidefrac;
789 secondslideline = bestslideline;
790 bestslidefrac = in->frac;
791 bestslideline = li;
792 }
793
794 return false; // stop
795 }
796
797
798
799 //
800 // P_SlideMove
801 // The momx / momy move is bad, so try to slide
802 // along a wall.
803 // Find the first line hit, move flush to it,
804 // and slide along it
805 //
806 // This is a kludgy mess.
807 //
P_SlideMove(mobj_t * mo)808 void P_SlideMove (mobj_t* mo)
809 {
810 fixed_t leadx;
811 fixed_t leady;
812 fixed_t trailx;
813 fixed_t traily;
814 fixed_t newx;
815 fixed_t newy;
816 int hitcount;
817
818 slidemo = mo;
819 hitcount = 0;
820
821 retry:
822 if (++hitcount == 3)
823 goto stairstep; // don't loop forever
824
825
826 // trace along the three leading corners
827 if (mo->momx > 0)
828 {
829 leadx = mo->x + mo->radius;
830 trailx = mo->x - mo->radius;
831 }
832 else
833 {
834 leadx = mo->x - mo->radius;
835 trailx = mo->x + mo->radius;
836 }
837
838 if (mo->momy > 0)
839 {
840 leady = mo->y + mo->radius;
841 traily = mo->y - mo->radius;
842 }
843 else
844 {
845 leady = mo->y - mo->radius;
846 traily = mo->y + mo->radius;
847 }
848
849 bestslidefrac = FRACUNIT+1;
850
851 P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
852 PT_ADDLINES, PTR_SlideTraverse );
853 P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
854 PT_ADDLINES, PTR_SlideTraverse );
855 P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
856 PT_ADDLINES, PTR_SlideTraverse );
857
858 // move up to the wall
859 if (bestslidefrac == FRACUNIT+1)
860 {
861 // the move most have hit the middle, so stairstep
862 stairstep:
863 if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
864 P_TryMove (mo, mo->x + mo->momx, mo->y);
865 return;
866 }
867
868 // fudge a bit to make sure it doesn't hit
869 bestslidefrac -= 0x800;
870 if (bestslidefrac > 0)
871 {
872 newx = FixedMul (mo->momx, bestslidefrac);
873 newy = FixedMul (mo->momy, bestslidefrac);
874
875 if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
876 goto stairstep;
877 }
878
879 // Now continue along the wall.
880 // First calculate remainder.
881 bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
882
883 if (bestslidefrac > FRACUNIT)
884 bestslidefrac = FRACUNIT;
885
886 if (bestslidefrac <= 0)
887 return;
888
889 tmxmove = FixedMul (mo->momx, bestslidefrac);
890 tmymove = FixedMul (mo->momy, bestslidefrac);
891
892 P_HitSlideLine (bestslideline); // clip the moves
893
894 mo->momx = tmxmove;
895 mo->momy = tmymove;
896
897 if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
898 {
899 goto retry;
900 }
901 }
902
903
904 //
905 // P_LineAttack
906 //
907 mobj_t* linetarget; // who got hit (or NULL)
908 mobj_t* shootthing;
909
910 // Height if not aiming up or down
911 // ???: use slope for monsters?
912 fixed_t shootz;
913
914 int la_damage;
915 fixed_t attackrange;
916
917 fixed_t aimslope;
918
919 // slopes to top and bottom of target
920 extern fixed_t topslope;
921 extern fixed_t bottomslope;
922
923 extern degenmobj_t *laserspot;
924
925 //
926 // PTR_AimTraverse
927 // Sets linetaget and aimslope when a target is aimed at.
928 //
929 boolean
PTR_AimTraverse(intercept_t * in)930 PTR_AimTraverse (intercept_t* in)
931 {
932 line_t* li;
933 mobj_t* th;
934 fixed_t slope;
935 fixed_t thingtopslope;
936 fixed_t thingbottomslope;
937 fixed_t dist;
938
939 if (in->isaline)
940 {
941 li = in->d.line;
942
943 if ( !(li->flags & ML_TWOSIDED) )
944 return false; // stop
945
946 // Crosses a two sided line.
947 // A two sided line will restrict
948 // the possible target ranges.
949 P_LineOpening (li);
950
951 if (openbottom >= opentop)
952 return false; // stop
953
954 dist = FixedMul (attackrange, in->frac);
955
956 if (li->backsector == NULL
957 || li->frontsector->floorheight != li->backsector->floorheight)
958 {
959 slope = FixedDiv (openbottom - shootz , dist);
960 if (slope > bottomslope)
961 bottomslope = slope;
962 }
963
964 if (li->backsector == NULL
965 || li->frontsector->ceilingheight != li->backsector->ceilingheight)
966 {
967 slope = FixedDiv (opentop - shootz , dist);
968 if (slope < topslope)
969 topslope = slope;
970 }
971
972 if (topslope <= bottomslope)
973 return false; // stop
974
975 return true; // shot continues
976 }
977
978 // shoot a thing
979 th = in->d.thing;
980 if (th == shootthing)
981 return true; // can't shoot self
982
983 if (!(th->flags&MF_SHOOTABLE))
984 return true; // corpse or something
985
986 // check angles to see if the thing can be aimed at
987 dist = FixedMul (attackrange, in->frac);
988 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
989
990 if (thingtopslope < bottomslope)
991 return true; // shot over the thing
992
993 thingbottomslope = FixedDiv (th->z - shootz, dist);
994
995 if (thingbottomslope > topslope)
996 return true; // shot under the thing
997
998 // this thing can be hit!
999 if (thingtopslope > topslope)
1000 thingtopslope = topslope;
1001
1002 if (thingbottomslope < bottomslope)
1003 thingbottomslope = bottomslope;
1004
1005 aimslope = (thingtopslope+thingbottomslope)/2;
1006 linetarget = th;
1007
1008 return false; // don't go any farther
1009 }
1010
1011
1012 //
1013 // PTR_ShootTraverse
1014 //
PTR_ShootTraverse(intercept_t * in)1015 boolean PTR_ShootTraverse (intercept_t* in)
1016 {
1017 fixed_t x;
1018 fixed_t y;
1019 fixed_t z;
1020 fixed_t frac;
1021
1022 line_t* li;
1023
1024 mobj_t* th;
1025
1026 fixed_t slope;
1027 fixed_t dist;
1028 fixed_t thingtopslope;
1029 fixed_t thingbottomslope;
1030 fixed_t thingheight; // [crispy] mobj or actual sprite height
1031
1032 if (in->isaline)
1033 {
1034 boolean safe = false;
1035 li = in->d.line;
1036
1037 // [crispy] laser spot does not shoot any line
1038 if (li->special && la_damage > INT_MIN)
1039 P_ShootSpecialLine (shootthing, li);
1040
1041 if ( !(li->flags & ML_TWOSIDED) )
1042 goto hitline;
1043
1044 // crosses a two sided line
1045 P_LineOpening (li);
1046
1047 dist = FixedMul (attackrange, in->frac);
1048
1049 // e6y: emulation of missed back side on two-sided lines.
1050 // backsector can be NULL when emulating missing back side.
1051
1052 if (li->backsector == NULL)
1053 {
1054 slope = FixedDiv (openbottom - shootz , dist);
1055 if (slope > aimslope)
1056 goto hitline;
1057
1058 slope = FixedDiv (opentop - shootz , dist);
1059 if (slope < aimslope)
1060 goto hitline;
1061 }
1062 else
1063 {
1064 if (li->frontsector->floorheight != li->backsector->floorheight)
1065 {
1066 slope = FixedDiv (openbottom - shootz , dist);
1067 if (slope > aimslope)
1068 goto hitline;
1069 }
1070
1071 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1072 {
1073 slope = FixedDiv (opentop - shootz , dist);
1074 if (slope < aimslope)
1075 goto hitline;
1076 }
1077 }
1078
1079 // shot continues
1080 return true;
1081
1082
1083 // hit line
1084 hitline:
1085 // position a bit closer
1086 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1087 x = trace.x + FixedMul (trace.dx, frac);
1088 y = trace.y + FixedMul (trace.dy, frac);
1089 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1090
1091 if (li->frontsector->ceilingpic == skyflatnum)
1092 {
1093 // don't shoot the sky!
1094 if (z > li->frontsector->ceilingheight)
1095 return false;
1096
1097 // it's a sky hack wall
1098 if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1099 {
1100 // [crispy] fix bullet puffs and laser spot not appearing in outdoor areas
1101 if (li->backsector->ceilingheight < z)
1102 return false;
1103 else
1104 safe = true;
1105 }
1106 }
1107
1108 // [crispy] check if the pullet puff's z-coordinate is below of above
1109 // its spawning sector's floor or ceiling, respectively, and move its
1110 // coordinates to the point where the trajectory hits the plane
1111 if (aimslope)
1112 {
1113 const int lineside = P_PointOnLineSide(x, y, li);
1114 int side;
1115
1116 if ((side = li->sidenum[lineside]) != NO_INDEX)
1117 {
1118 const sector_t *const sector = sides[side].sector;
1119
1120 if (z < sector->floorheight ||
1121 (z > sector->ceilingheight && sector->ceilingpic != skyflatnum))
1122 {
1123 z = BETWEEN(sector->floorheight, sector->ceilingheight, z);
1124 frac = FixedDiv(z - shootz, FixedMul(aimslope, attackrange));
1125 x = trace.x + FixedMul (trace.dx, frac);
1126 y = trace.y + FixedMul (trace.dy, frac);
1127 }
1128 }
1129 }
1130
1131 // [crispy] update laser spot position and return
1132 if (la_damage == INT_MIN)
1133 {
1134 laserspot->thinker.function.acv = (actionf_v) (1);
1135 laserspot->x = x;
1136 laserspot->y = y;
1137 laserspot->z = z;
1138 return false;
1139 }
1140
1141 // Spawn bullet puffs.
1142 P_SpawnPuffSafe (x, y, z, safe);
1143
1144 // don't go any farther
1145 return false;
1146 }
1147
1148 // shoot a thing
1149 th = in->d.thing;
1150 if (th == shootthing)
1151 return true; // can't shoot self
1152
1153 if (!(th->flags&MF_SHOOTABLE))
1154 return true; // corpse or something
1155
1156 // check angles to see if the thing can be aimed at
1157 dist = FixedMul (attackrange, in->frac);
1158 // [crispy] mobj or actual sprite height
1159 thingheight = (shootthing->player && critical->freeaim == FREEAIM_DIRECT) ?
1160 th->info->actualheight : th->height;
1161 thingtopslope = FixedDiv (th->z+thingheight - shootz , dist);
1162
1163 if (thingtopslope < aimslope)
1164 return true; // shot over the thing
1165
1166 thingbottomslope = FixedDiv (th->z - shootz, dist);
1167
1168 if (thingbottomslope > aimslope)
1169 return true; // shot under the thing
1170
1171
1172 // hit thing
1173 // position a bit closer
1174 frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1175
1176 x = trace.x + FixedMul (trace.dx, frac);
1177 y = trace.y + FixedMul (trace.dy, frac);
1178 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1179
1180 // [crispy] update laser spot position and return
1181 if (la_damage == INT_MIN)
1182 {
1183 // [crispy] pass through Spectres
1184 if (th->flags & MF_SHADOW)
1185 return true;
1186
1187 laserspot->thinker.function.acv = (actionf_v) (1);
1188 laserspot->x = th->x;
1189 laserspot->y = th->y;
1190 laserspot->z = z;
1191 return false;
1192 }
1193
1194 // Spawn bullet puffs or blod spots,
1195 // depending on target type.
1196 if (in->d.thing->flags & MF_NOBLOOD)
1197 P_SpawnPuff (x,y,z);
1198 else
1199 P_SpawnBlood (x,y,z, la_damage, th); // [crispy] pass thing type
1200
1201 if (la_damage)
1202 P_DamageMobj (th, shootthing, shootthing, la_damage);
1203
1204 // don't go any farther
1205 return false;
1206
1207 }
1208
1209
1210 //
1211 // P_AimLineAttack
1212 //
1213 fixed_t
P_AimLineAttack(mobj_t * t1,angle_t angle,fixed_t distance)1214 P_AimLineAttack
1215 ( mobj_t* t1,
1216 angle_t angle,
1217 fixed_t distance )
1218 {
1219 fixed_t x2;
1220 fixed_t y2;
1221
1222 t1 = P_SubstNullMobj(t1);
1223
1224 angle >>= ANGLETOFINESHIFT;
1225 shootthing = t1;
1226
1227 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1228 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1229 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1230
1231 // can't shoot outside view angles
1232 topslope = (ORIGHEIGHT/2)*FRACUNIT/(ORIGWIDTH/2);
1233 bottomslope = -(ORIGHEIGHT/2)*FRACUNIT/(ORIGWIDTH/2);
1234
1235 attackrange = distance;
1236 linetarget = NULL;
1237
1238 P_PathTraverse ( t1->x, t1->y,
1239 x2, y2,
1240 PT_ADDLINES|PT_ADDTHINGS,
1241 PTR_AimTraverse );
1242
1243 if (linetarget)
1244 return aimslope;
1245
1246 return 0;
1247 }
1248
1249
1250 //
1251 // P_LineAttack
1252 // If damage == 0, it is just a test trace
1253 // that will leave linetarget set.
1254 // [crispy] if damage == INT_MIN, it is a trace
1255 // to update the laser spot position
1256 //
1257 void
P_LineAttack(mobj_t * t1,angle_t angle,fixed_t distance,fixed_t slope,int damage)1258 P_LineAttack
1259 ( mobj_t* t1,
1260 angle_t angle,
1261 fixed_t distance,
1262 fixed_t slope,
1263 int damage )
1264 {
1265 // [crispy] smooth laser spot movement with uncapped framerate
1266 const fixed_t t1x = (damage == INT_MIN) ? viewx : t1->x;
1267 const fixed_t t1y = (damage == INT_MIN) ? viewy : t1->y;
1268 fixed_t x2;
1269 fixed_t y2;
1270
1271 angle >>= ANGLETOFINESHIFT;
1272 shootthing = t1;
1273 la_damage = damage;
1274 x2 = t1x + (distance>>FRACBITS)*finecosine[angle];
1275 y2 = t1y + (distance>>FRACBITS)*finesine[angle];
1276 shootz = (damage == INT_MIN) ? viewz : t1->z + (t1->height>>1) + 8*FRACUNIT;
1277 attackrange = distance;
1278 aimslope = slope;
1279
1280 P_PathTraverse ( t1x, t1y,
1281 x2, y2,
1282 PT_ADDLINES|PT_ADDTHINGS,
1283 PTR_ShootTraverse );
1284 }
1285
1286 // [crispy] update laser spot position
1287 // call P_AimLineAttack() to check if a target is aimed at (linetarget)
1288 // then call P_LineAttack() with either aimslope or the passed slope
1289 void
P_LineLaser(mobj_t * t1,angle_t angle,fixed_t distance,fixed_t slope)1290 P_LineLaser
1291 ( mobj_t* t1,
1292 angle_t angle,
1293 fixed_t distance,
1294 fixed_t slope )
1295 {
1296 fixed_t lslope;
1297
1298 laserspot->thinker.function.acv = (actionf_v) (0);
1299
1300 // [crispy] intercepts overflow guard
1301 crispy->crosshair |= CROSSHAIR_INTERCEPT;
1302
1303 // [crispy] set the linetarget pointer
1304 lslope = P_AimLineAttack(t1, angle, distance);
1305
1306 if (critical->freeaim == FREEAIM_DIRECT)
1307 {
1308 lslope = slope;
1309 }
1310 else
1311 {
1312 // [crispy] increase accuracy
1313 if (!linetarget)
1314 {
1315 angle_t an = angle;
1316
1317 an += 1<<26;
1318 lslope = P_AimLineAttack(t1, an, distance);
1319
1320 if (!linetarget)
1321 {
1322 an -= 2<<26;
1323 lslope = P_AimLineAttack(t1, an, distance);
1324
1325 if (!linetarget && critical->freeaim == FREEAIM_BOTH)
1326 {
1327 lslope = slope;
1328 }
1329
1330 }
1331 }
1332 }
1333
1334 if ((crispy->crosshair & ~CROSSHAIR_INTERCEPT) == CROSSHAIR_PROJECTED)
1335 {
1336 // [crispy] don't aim at Spectres
1337 if (linetarget && !(linetarget->flags & MF_SHADOW) && (crispy->freeaim != FREEAIM_DIRECT))
1338 P_LineAttack(t1, angle, distance, aimslope, INT_MIN);
1339 else
1340 // [crispy] double the auto aim distance
1341 P_LineAttack(t1, angle, 2*distance, lslope, INT_MIN);
1342 }
1343
1344 // [crispy] intercepts overflow guard
1345 crispy->crosshair &= ~CROSSHAIR_INTERCEPT;
1346 }
1347
1348
1349 //
1350 // USE LINES
1351 //
1352 mobj_t* usething;
1353
PTR_UseTraverse(intercept_t * in)1354 boolean PTR_UseTraverse (intercept_t* in)
1355 {
1356 int side;
1357
1358 if (!in->d.line->special)
1359 {
1360 P_LineOpening (in->d.line);
1361 if (openrange <= 0)
1362 {
1363 S_StartSound (usething, sfx_noway);
1364
1365 // can't use through a wall
1366 return false;
1367 }
1368 // not a special line, but keep checking
1369 return true ;
1370 }
1371
1372 side = 0;
1373 if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1374 side = 1;
1375
1376 // return false; // don't use back side
1377
1378 P_UseSpecialLine (usething, in->d.line, side);
1379
1380 // can't use for than one special line in a row
1381 return false;
1382 }
1383
1384
1385 //
1386 // P_UseLines
1387 // Looks for special lines in front of the player to activate.
1388 //
P_UseLines(player_t * player)1389 void P_UseLines (player_t* player)
1390 {
1391 int angle;
1392 fixed_t x1;
1393 fixed_t y1;
1394 fixed_t x2;
1395 fixed_t y2;
1396
1397 usething = player->mo;
1398
1399 angle = player->mo->angle >> ANGLETOFINESHIFT;
1400
1401 x1 = player->mo->x;
1402 y1 = player->mo->y;
1403 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1404 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1405
1406 P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1407 }
1408
1409
1410 //
1411 // RADIUS ATTACK
1412 //
1413 mobj_t* bombsource;
1414 mobj_t* bombspot;
1415 int bombdamage;
1416
1417
1418 //
1419 // PIT_RadiusAttack
1420 // "bombsource" is the creature
1421 // that caused the explosion at "bombspot".
1422 //
PIT_RadiusAttack(mobj_t * thing)1423 boolean PIT_RadiusAttack (mobj_t* thing)
1424 {
1425 fixed_t dx;
1426 fixed_t dy;
1427 fixed_t dist;
1428
1429 if (!(thing->flags & MF_SHOOTABLE) )
1430 return true;
1431
1432 // Boss spider and cyborg
1433 // take no damage from concussion.
1434 if (thing->type == MT_CYBORG
1435 || thing->type == MT_SPIDER)
1436 return true;
1437
1438 dx = abs(thing->x - bombspot->x);
1439 dy = abs(thing->y - bombspot->y);
1440
1441 dist = dx>dy ? dx : dy;
1442 dist = (dist - thing->radius) >> FRACBITS;
1443
1444 if (dist < 0)
1445 dist = 0;
1446
1447 if (dist >= bombdamage)
1448 return true; // out of range
1449
1450 if ( P_CheckSight (thing, bombspot) )
1451 {
1452 // must be in direct path
1453 P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1454 }
1455
1456 return true;
1457 }
1458
1459
1460 //
1461 // P_RadiusAttack
1462 // Source is the creature that caused the explosion at spot.
1463 //
1464 void
P_RadiusAttack(mobj_t * spot,mobj_t * source,int damage)1465 P_RadiusAttack
1466 ( mobj_t* spot,
1467 mobj_t* source,
1468 int damage )
1469 {
1470 int x;
1471 int y;
1472
1473 int xl;
1474 int xh;
1475 int yl;
1476 int yh;
1477
1478 fixed_t dist;
1479
1480 dist = (damage+MAXRADIUS)<<FRACBITS;
1481 yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1482 yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1483 xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1484 xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1485 bombspot = spot;
1486 bombsource = source;
1487 bombdamage = damage;
1488
1489 for (y=yl ; y<=yh ; y++)
1490 for (x=xl ; x<=xh ; x++)
1491 P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1492 }
1493
1494
1495
1496 //
1497 // SECTOR HEIGHT CHANGING
1498 // After modifying a sectors floor or ceiling height,
1499 // call this routine to adjust the positions
1500 // of all things that touch the sector.
1501 //
1502 // If anything doesn't fit anymore, true will be returned.
1503 // If crunch is true, they will take damage
1504 // as they are being crushed.
1505 // If Crunch is false, you should set the sector height back
1506 // the way it was and call P_ChangeSector again
1507 // to undo the changes.
1508 //
1509 boolean crushchange;
1510 boolean nofit;
1511
1512
1513 //
1514 // PIT_ChangeSector
1515 //
PIT_ChangeSector(mobj_t * thing)1516 boolean PIT_ChangeSector (mobj_t* thing)
1517 {
1518 mobj_t* mo;
1519
1520 if (P_ThingHeightClip (thing))
1521 {
1522 // keep checking
1523 return true;
1524 }
1525
1526
1527 // crunch bodies to giblets
1528 if (thing->health <= 0)
1529 {
1530 P_SetMobjState (thing, S_GIBS);
1531
1532 // [crispy] no blood, no giblets
1533 if (crispy->coloredblood && (thing->flags & MF_NOBLOOD))
1534 {
1535 thing->sprite = SPR_TNT1;
1536 }
1537
1538 if (gameversion > exe_doom_1_2)
1539 thing->flags &= ~MF_SOLID;
1540 thing->height = 0;
1541 thing->radius = 0;
1542
1543 // [crispy] connect giblet object with the crushed monster
1544 thing->target = thing;
1545
1546 // keep checking
1547 return true;
1548 }
1549
1550 // crunch dropped items
1551 if (thing->flags & MF_DROPPED)
1552 {
1553 P_RemoveMobj (thing);
1554
1555 // keep checking
1556 return true;
1557 }
1558
1559 if (! (thing->flags & MF_SHOOTABLE) )
1560 {
1561 // assume it is bloody gibs or something
1562 return true;
1563 }
1564
1565 nofit = true;
1566
1567 if (crushchange && !(leveltime&3) )
1568 {
1569 P_DamageMobj(thing,NULL,NULL,10);
1570
1571 // spray blood in a random direction
1572 mo = P_SpawnMobj (thing->x,
1573 thing->y,
1574 // [crispy] Lost Souls and Barrels bleed Puffs
1575 thing->z + thing->height/2, (thing->flags & MF_NOBLOOD) ? MT_PUFF : MT_BLOOD);
1576
1577 mo->momx = P_SubRandom() << 12;
1578 mo->momy = P_SubRandom() << 12;
1579
1580 // [crispy] connect blood object with the monster that bleeds it
1581 mo->target = thing;
1582
1583 // [crispy] Spectres bleed spectre blood
1584 if (crispy->coloredblood)
1585 mo->flags |= (thing->flags & MF_SHADOW);
1586 }
1587
1588 // keep checking (crush other things)
1589 return true;
1590 }
1591
1592
1593
1594 //
1595 // P_ChangeSector
1596 //
1597 boolean
P_ChangeSector(sector_t * sector,boolean crunch)1598 P_ChangeSector
1599 ( sector_t* sector,
1600 boolean crunch )
1601 {
1602 int x;
1603 int y;
1604
1605 nofit = false;
1606 crushchange = crunch;
1607
1608 // re-check heights for all things near the moving sector
1609 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1610 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1611 P_BlockThingsIterator (x, y, PIT_ChangeSector);
1612
1613
1614 return nofit;
1615 }
1616
1617 // Code to emulate the behavior of Vanilla Doom when encountering an overrun
1618 // of the spechit array. This is by Andrey Budko (e6y) and comes from his
1619 // PrBoom plus port. A big thanks to Andrey for this.
1620
SpechitOverrun(line_t * ld)1621 static void SpechitOverrun(line_t *ld)
1622 {
1623 static unsigned int baseaddr = 0;
1624 unsigned int addr;
1625
1626 if (baseaddr == 0)
1627 {
1628 int p;
1629
1630 // This is the first time we have had an overrun. Work out
1631 // what base address we are going to use.
1632 // Allow a spechit value to be specified on the command line.
1633
1634 //!
1635 // @category compat
1636 // @arg <n>
1637 //
1638 // Use the specified magic value when emulating spechit overruns.
1639 //
1640
1641 p = M_CheckParmWithArgs("-spechit", 1);
1642
1643 if (p > 0)
1644 {
1645 M_StrToInt(myargv[p+1], (int *) &baseaddr);
1646 }
1647 else
1648 {
1649 baseaddr = DEFAULT_SPECHIT_MAGIC;
1650 }
1651 }
1652
1653 // Calculate address used in doom2.exe
1654
1655 addr = baseaddr + (ld - lines) * 0x3E;
1656
1657 switch(numspechit)
1658 {
1659 case 9:
1660 case 10:
1661 case 11:
1662 case 12:
1663 tmbbox[numspechit-9] = addr;
1664 break;
1665 case 13:
1666 crushchange = addr;
1667 break;
1668 case 14:
1669 nofit = addr;
1670 break;
1671 default:
1672 fprintf(stderr, "SpechitOverrun: Warning: unable to emulate"
1673 "an overrun where numspechit=%i\n",
1674 numspechit);
1675 break;
1676 }
1677 }
1678
1679