aboutsummaryrefslogtreecommitdiff
path: root/src/p_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/p_map.c')
-rw-r--r--src/p_map.c2335
1 files changed, 2335 insertions, 0 deletions
diff --git a/src/p_map.c b/src/p_map.c
new file mode 100644
index 0000000..a8c3a6d
--- /dev/null
+++ b/src/p_map.c
@@ -0,0 +1,2335 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2004 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Movement, collision handling.
31 * Shooting and aiming.
32 *
33 *-----------------------------------------------------------------------------*/
34
35#include "doomstat.h"
36#include "r_main.h"
37#include "p_mobj.h"
38#include "p_maputl.h"
39#include "p_map.h"
40#include "p_setup.h"
41#include "p_spec.h"
42#include "s_sound.h"
43#include "sounds.h"
44#include "p_inter.h"
45#include "m_random.h"
46#include "m_bbox.h"
47#include "lprintf.h"
48
49static mobj_t *tmthing;
50static fixed_t tmx;
51static fixed_t tmy;
52static int pe_x; // Pain Elemental position for Lost Soul checks // phares
53static int pe_y; // Pain Elemental position for Lost Soul checks // phares
54static int ls_x; // Lost Soul position for Lost Soul checks // phares
55static int ls_y; // Lost Soul position for Lost Soul checks // phares
56
57// If "floatok" true, move would be ok
58// if within "tmfloorz - tmceilingz".
59
60boolean floatok;
61
62/* killough 11/98: if "felldown" true, object was pushed down ledge */
63boolean felldown;
64
65// The tm* items are used to hold information globally, usually for
66// line or object intersection checking
67
68fixed_t tmbbox[4]; // bounding box for line intersection checks
69fixed_t tmfloorz; // floor you'd hit if free to fall
70fixed_t tmceilingz; // ceiling of sector you're in
71fixed_t tmdropoffz; // dropoff on other side of line you're crossing
72
73// keep track of the line that lowers the ceiling,
74// so missiles don't explode against sky hack walls
75
76line_t *ceilingline;
77line_t *blockline; /* killough 8/11/98: blocking linedef */
78line_t *floorline; /* killough 8/1/98: Highest touched floor */
79static int tmunstuck; /* killough 8/1/98: whether to allow unsticking */
80
81// keep track of special lines as they are hit,
82// but don't process them until the move is proven valid
83
84// 1/11/98 killough: removed limit on special lines crossed
85line_t **spechit; // new code -- killough
86static int spechit_max; // killough
87
88int numspechit;
89
90// Temporary holder for thing_sectorlist threads
91msecnode_t* sector_list = NULL; // phares 3/16/98
92
93//
94// TELEPORT MOVE
95//
96
97//
98// PIT_StompThing
99//
100
101static boolean telefrag; /* killough 8/9/98: whether to telefrag at exit */
102
103boolean PIT_StompThing (mobj_t* thing)
104 {
105 fixed_t blockdist;
106
107 // phares 9/10/98: moved this self-check to start of routine
108
109 // don't clip against self
110
111 if (thing == tmthing)
112 return true;
113
114 if (!(thing->flags & MF_SHOOTABLE)) // Can't shoot it? Can't stomp it!
115 return true;
116
117 blockdist = thing->radius + tmthing->radius;
118
119 if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist)
120 return true; // didn't hit it
121
122 // monsters don't stomp things except on boss level
123 if (!telefrag) // killough 8/9/98: make consistent across all levels
124 return false;
125
126 P_DamageMobj (thing, tmthing, tmthing, 10000); // Stomp!
127
128 return true;
129 }
130
131
132/*
133 * killough 8/28/98:
134 *
135 * P_GetFriction()
136 *
137 * Returns the friction associated with a particular mobj.
138 */
139
140int P_GetFriction(const mobj_t *mo, int *frictionfactor)
141{
142 int friction = ORIG_FRICTION;
143 int movefactor = ORIG_FRICTION_FACTOR;
144 const msecnode_t *m;
145 const sector_t *sec;
146
147 /* Assign the friction value to objects on the floor, non-floating,
148 * and clipped. Normally the object's friction value is kept at
149 * ORIG_FRICTION and this thinker changes it for icy or muddy floors.
150 *
151 * When the object is straddling sectors with the same
152 * floorheight that have different frictions, use the lowest
153 * friction value (muddy has precedence over icy).
154 */
155
156 if (!(mo->flags & (MF_NOCLIP|MF_NOGRAVITY))
157 && (mbf_features || (mo->player && !compatibility)) &&
158 variable_friction)
159 for (m = mo->touching_sectorlist; m; m = m->m_tnext)
160 if ((sec = m->m_sector)->special & FRICTION_MASK &&
161 (sec->friction < friction || friction == ORIG_FRICTION) &&
162 (mo->z <= sec->floorheight ||
163 (sec->heightsec != -1 &&
164 mo->z <= sectors[sec->heightsec].floorheight &&
165 mbf_features)))
166 friction = sec->friction, movefactor = sec->movefactor;
167
168 if (frictionfactor)
169 *frictionfactor = movefactor;
170
171 return friction;
172}
173
174/* phares 3/19/98
175 * P_GetMoveFactor() returns the value by which the x,y
176 * movements are multiplied to add to player movement.
177 *
178 * killough 8/28/98: rewritten
179 */
180
181int P_GetMoveFactor(const mobj_t *mo, int *frictionp)
182{
183 int movefactor, friction;
184
185 //e6y
186 if (!mbf_features)
187 {
188 int momentum;
189
190 movefactor = ORIG_FRICTION_FACTOR;
191
192 if (!compatibility && variable_friction &&
193 !(mo->flags & (MF_NOGRAVITY | MF_NOCLIP)))
194 {
195 friction = mo->friction;
196 if (friction == ORIG_FRICTION) // normal floor
197 ;
198 else if (friction > ORIG_FRICTION) // ice
199 {
200 movefactor = mo->movefactor;
201 ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset
202 }
203 else // sludge
204 {
205
206 // phares 3/11/98: you start off slowly, then increase as
207 // you get better footing
208
209 momentum = (P_AproxDistance(mo->momx,mo->momy));
210 movefactor = mo->movefactor;
211 if (momentum > MORE_FRICTION_MOMENTUM<<2)
212 movefactor <<= 3;
213
214 else if (momentum > MORE_FRICTION_MOMENTUM<<1)
215 movefactor <<= 2;
216
217 else if (momentum > MORE_FRICTION_MOMENTUM)
218 movefactor <<= 1;
219
220 ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset
221 }
222 } // ^
223
224 return(movefactor); // |
225 }
226
227 // If the floor is icy or muddy, it's harder to get moving. This is where
228 // the different friction factors are applied to 'trying to move'. In
229 // p_mobj.c, the friction factors are applied as you coast and slow down.
230
231 if ((friction = P_GetFriction(mo, &movefactor)) < ORIG_FRICTION)
232 {
233 // phares 3/11/98: you start off slowly, then increase as
234 // you get better footing
235
236 int momentum = P_AproxDistance(mo->momx,mo->momy);
237
238 if (momentum > MORE_FRICTION_MOMENTUM<<2)
239 movefactor <<= 3;
240 else if (momentum > MORE_FRICTION_MOMENTUM<<1)
241 movefactor <<= 2;
242 else if (momentum > MORE_FRICTION_MOMENTUM)
243 movefactor <<= 1;
244 }
245
246 if (frictionp)
247 *frictionp = friction;
248
249 return movefactor;
250}
251
252//
253// P_TeleportMove
254//
255
256boolean P_TeleportMove (mobj_t* thing,fixed_t x,fixed_t y, boolean boss)
257 {
258 int xl;
259 int xh;
260 int yl;
261 int yh;
262 int bx;
263 int by;
264
265 subsector_t* newsubsec;
266
267 /* killough 8/9/98: make telefragging more consistent, preserve compatibility */
268 telefrag = thing->player ||
269 (!comp[comp_telefrag] ? boss : (gamemap==30));
270
271 // kill anything occupying the position
272
273 tmthing = thing;
274
275 tmx = x;
276 tmy = y;
277
278 tmbbox[BOXTOP] = y + tmthing->radius;
279 tmbbox[BOXBOTTOM] = y - tmthing->radius;
280 tmbbox[BOXRIGHT] = x + tmthing->radius;
281 tmbbox[BOXLEFT] = x - tmthing->radius;
282
283 newsubsec = R_PointInSubsector (x,y);
284 ceilingline = NULL;
285
286 // The base floor/ceiling is from the subsector
287 // that contains the point.
288 // Any contacted lines the step closer together
289 // will adjust them.
290
291 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
292 tmceilingz = newsubsec->sector->ceilingheight;
293
294 validcount++;
295 numspechit = 0;
296
297 // stomp on any things contacted
298
299 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
300 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
301 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
302 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
303
304 for (bx=xl ; bx<=xh ; bx++)
305 for (by=yl ; by<=yh ; by++)
306 if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
307 return false;
308
309 // the move is ok,
310 // so unlink from the old position & link into the new position
311
312 P_UnsetThingPosition (thing);
313
314 thing->floorz = tmfloorz;
315 thing->ceilingz = tmceilingz;
316 thing->dropoffz = tmdropoffz; // killough 11/98
317
318 thing->x = x;
319 thing->y = y;
320
321 P_SetThingPosition (thing);
322
323 thing->PrevX = x;
324 thing->PrevY = y;
325 thing->PrevZ = thing->floorz;
326
327 return true;
328 }
329
330
331//
332// MOVEMENT ITERATOR FUNCTIONS
333//
334
335// e6y: Spechits overrun emulation code
336static void SpechitOverrun(line_t *ld);
337
338// // phares
339// PIT_CrossLine // |
340// Checks to see if a PE->LS trajectory line crosses a blocking // V
341// line. Returns false if it does.
342//
343// tmbbox holds the bounding box of the trajectory. If that box
344// does not touch the bounding box of the line in question,
345// then the trajectory is not blocked. If the PE is on one side
346// of the line and the LS is on the other side, then the
347// trajectory is blocked.
348//
349// Currently this assumes an infinite line, which is not quite
350// correct. A more correct solution would be to check for an
351// intersection of the trajectory and the line, but that takes
352// longer and probably really isn't worth the effort.
353//
354
355static // killough 3/26/98: make static
356boolean PIT_CrossLine (line_t* ld)
357 {
358 if (!(ld->flags & ML_TWOSIDED) ||
359 (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS)))
360 if (!(tmbbox[BOXLEFT] > ld->bbox[BOXRIGHT] ||
361 tmbbox[BOXRIGHT] < ld->bbox[BOXLEFT] ||
362 tmbbox[BOXTOP] < ld->bbox[BOXBOTTOM] ||
363 tmbbox[BOXBOTTOM] > ld->bbox[BOXTOP]))
364 if (P_PointOnLineSide(pe_x,pe_y,ld) != P_PointOnLineSide(ls_x,ls_y,ld))
365 return(false); // line blocks trajectory // ^
366 return(true); // line doesn't block trajectory // |
367 } // phares
368
369
370/* killough 8/1/98: used to test intersection between thing and line
371 * assuming NO movement occurs -- used to avoid sticky situations.
372 */
373
374static int untouched(line_t *ld)
375{
376 fixed_t x, y, tmbbox[4];
377 return
378 (tmbbox[BOXRIGHT] = (x=tmthing->x)+tmthing->radius) <= ld->bbox[BOXLEFT] ||
379 (tmbbox[BOXLEFT] = x-tmthing->radius) >= ld->bbox[BOXRIGHT] ||
380 (tmbbox[BOXTOP] = (y=tmthing->y)+tmthing->radius) <= ld->bbox[BOXBOTTOM] ||
381 (tmbbox[BOXBOTTOM] = y-tmthing->radius) >= ld->bbox[BOXTOP] ||
382 P_BoxOnLineSide(tmbbox, ld) != -1;
383}
384
385//
386// PIT_CheckLine
387// Adjusts tmfloorz and tmceilingz as lines are contacted
388//
389
390static // killough 3/26/98: make static
391boolean PIT_CheckLine (line_t* ld)
392{
393 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
394 || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
395 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
396 || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
397 return true; // didn't hit it
398
399 if (P_BoxOnLineSide(tmbbox, ld) != -1)
400 return true; // didn't hit it
401
402 // A line has been hit
403
404 // The moving thing's destination position will cross the given line.
405 // If this should not be allowed, return false.
406 // If the line is special, keep track of it
407 // to process later if the move is proven ok.
408 // NOTE: specials are NOT sorted by order,
409 // so two special lines that are only 8 pixels apart
410 // could be crossed in either order.
411
412 // killough 7/24/98: allow player to move out of 1s wall, to prevent sticking
413 if (!ld->backsector) // one sided line
414 {
415 blockline = ld;
416 return tmunstuck && !untouched(ld) &&
417 FixedMul(tmx-tmthing->x,ld->dy) > FixedMul(tmy-tmthing->y,ld->dx);
418 }
419
420 // killough 8/10/98: allow bouncing objects to pass through as missiles
421 if (!(tmthing->flags & (MF_MISSILE | MF_BOUNCES)))
422 {
423 if (ld->flags & ML_BLOCKING) // explicitly blocking everything
424 return tmunstuck && !untouched(ld); // killough 8/1/98: allow escape
425
426 // killough 8/9/98: monster-blockers don't affect friends
427 if (!(tmthing->flags & MF_FRIEND || tmthing->player)
428 && ld->flags & ML_BLOCKMONSTERS)
429 return false; // block monsters only
430 }
431
432 // set openrange, opentop, openbottom
433 // these define a 'window' from one sector to another across this line
434
435 P_LineOpening (ld);
436
437 // adjust floor & ceiling heights
438
439 if (opentop < tmceilingz)
440 {
441 tmceilingz = opentop;
442 ceilingline = ld;
443 blockline = ld;
444 }
445
446 if (openbottom > tmfloorz)
447 {
448 tmfloorz = openbottom;
449 floorline = ld; // killough 8/1/98: remember floor linedef
450 blockline = ld;
451 }
452
453 if (lowfloor < tmdropoffz)
454 tmdropoffz = lowfloor;
455
456 // if contacted a special line, add it to the list
457
458 if (ld->special)
459 {
460 // 1/11/98 killough: remove limit on lines hit, by array doubling
461 if (numspechit >= spechit_max) {
462 spechit_max = spechit_max ? spechit_max*2 : 8;
463 spechit = realloc(spechit,sizeof *spechit*spechit_max); // killough
464 }
465 spechit[numspechit++] = ld;
466 // e6y: Spechits overrun emulation code
467 if (numspechit >= 8 && demo_compatibility)
468 SpechitOverrun(ld);
469 }
470
471 return true;
472}
473
474//
475// PIT_CheckThing
476//
477
478static boolean PIT_CheckThing(mobj_t *thing) // killough 3/26/98: make static
479{
480 fixed_t blockdist;
481 int damage;
482
483 // killough 11/98: add touchy things
484 if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE|MF_TOUCHY)))
485 return true;
486
487 blockdist = thing->radius + tmthing->radius;
488
489 if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist)
490 return true; // didn't hit it
491
492 // killough 11/98:
493 //
494 // This test has less information content (it's almost always false), so it
495 // should not be moved up to first, as it adds more overhead than it removes.
496
497 // don't clip against self
498
499 if (thing == tmthing)
500 return true;
501
502 /* killough 11/98:
503 *
504 * TOUCHY flag, for mines or other objects which die on contact with solids.
505 * If a solid object of a different type comes in contact with a touchy
506 * thing, and the touchy thing is not the sole one moving relative to fixed
507 * surroundings such as walls, then the touchy thing dies immediately.
508 */
509
510 if (thing->flags & MF_TOUCHY && // touchy object
511 tmthing->flags & MF_SOLID && // solid object touches it
512 thing->health > 0 && // touchy object is alive
513 (thing->intflags & MIF_ARMED || // Thing is an armed mine
514 sentient(thing)) && // ... or a sentient thing
515 (thing->type != tmthing->type || // only different species
516 thing->type == MT_PLAYER) && // ... or different players
517 thing->z + thing->height >= tmthing->z && // touches vertically
518 tmthing->z + tmthing->height >= thing->z &&
519 (thing->type ^ MT_PAIN) | // PEs and lost souls
520 (tmthing->type ^ MT_SKULL) && // are considered same
521 (thing->type ^ MT_SKULL) | // (but Barons & Knights
522 (tmthing->type ^ MT_PAIN)) // are intentionally not)
523 {
524 P_DamageMobj(thing, NULL, NULL, thing->health); // kill object
525 return true;
526 }
527
528 // check for skulls slamming into things
529
530 if (tmthing->flags & MF_SKULLFLY)
531 {
532 // A flying skull is smacking something.
533 // Determine damage amount, and the skull comes to a dead stop.
534
535 int damage = ((P_Random(pr_skullfly)%8)+1)*tmthing->info->damage;
536
537 P_DamageMobj (thing, tmthing, tmthing, damage);
538
539 tmthing->flags &= ~MF_SKULLFLY;
540 tmthing->momx = tmthing->momy = tmthing->momz = 0;
541
542 P_SetMobjState (tmthing, tmthing->info->spawnstate);
543
544 return false; // stop moving
545 }
546
547 // missiles can hit other things
548 // killough 8/10/98: bouncing non-solid things can hit other things too
549
550 if (tmthing->flags & MF_MISSILE || (tmthing->flags & MF_BOUNCES &&
551 !(tmthing->flags & MF_SOLID)))
552 {
553 // see if it went over / under
554
555 if (tmthing->z > thing->z + thing->height)
556 return true; // overhead
557
558 if (tmthing->z+tmthing->height < thing->z)
559 return true; // underneath
560
561 if (tmthing->target && (tmthing->target->type == thing->type ||
562 (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
563 (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT)))
564 {
565 if (thing == tmthing->target)
566 return true; // Don't hit same species as originator.
567 else
568 // e6y: Dehacked support - monsters infight
569 if (thing->type != MT_PLAYER && !monsters_infight) // Explode, but do no damage.
570 return false; // Let players missile other players.
571 }
572
573 // killough 8/10/98: if moving thing is not a missile, no damage
574 // is inflicted, and momentum is reduced if object hit is solid.
575
576 if (!(tmthing->flags & MF_MISSILE)) {
577 if (!(thing->flags & MF_SOLID)) {
578 return true;
579 } else {
580 tmthing->momx = -tmthing->momx;
581 tmthing->momy = -tmthing->momy;
582 if (!(tmthing->flags & MF_NOGRAVITY))
583 {
584 tmthing->momx >>= 2;
585 tmthing->momy >>= 2;
586 }
587 return false;
588 }
589 }
590
591 if (!(thing->flags & MF_SHOOTABLE))
592 return !(thing->flags & MF_SOLID); // didn't do any damage
593
594 // damage / explode
595
596 damage = ((P_Random(pr_damage)%8)+1)*tmthing->info->damage;
597 P_DamageMobj (thing, tmthing, tmthing->target, damage);
598
599 // don't traverse any more
600 return false;
601 }
602
603 // check for special pickup
604
605 if (thing->flags & MF_SPECIAL)
606 {
607 uint_64_t solid = thing->flags & MF_SOLID;
608 if (tmthing->flags & MF_PICKUP)
609 P_TouchSpecialThing(thing, tmthing); // can remove thing
610 return !solid;
611 }
612
613 // killough 3/16/98: Allow non-solid moving objects to move through solid
614 // ones, by allowing the moving thing (tmthing) to move if it's non-solid,
615 // despite another solid thing being in the way.
616 // killough 4/11/98: Treat no-clipping things as not blocking
617 // ...but not in demo_compatibility mode
618
619 return !(thing->flags & MF_SOLID)
620 || (!demo_compatibility
621 && (thing->flags & MF_NOCLIP || !(tmthing->flags & MF_SOLID)));
622
623 // return !(thing->flags & MF_SOLID); // old code -- killough
624}
625
626// This routine checks for Lost Souls trying to be spawned // phares
627// across 1-sided lines, impassible lines, or "monsters can't // |
628// cross" lines. Draw an imaginary line between the PE // V
629// and the new Lost Soul spawn spot. If that line crosses
630// a 'blocking' line, then disallow the spawn. Only search
631// lines in the blocks of the blockmap where the bounding box
632// of the trajectory line resides. Then check bounding box
633// of the trajectory vs. the bounding box of each blocking
634// line to see if the trajectory and the blocking line cross.
635// Then check the PE and LS to see if they're on different
636// sides of the blocking line. If so, return true, otherwise
637// false.
638
639boolean Check_Sides(mobj_t* actor, int x, int y)
640 {
641 int bx,by,xl,xh,yl,yh;
642
643 pe_x = actor->x;
644 pe_y = actor->y;
645 ls_x = x;
646 ls_y = y;
647
648 // Here is the bounding box of the trajectory
649
650 tmbbox[BOXLEFT] = pe_x < x ? pe_x : x;
651 tmbbox[BOXRIGHT] = pe_x > x ? pe_x : x;
652 tmbbox[BOXTOP] = pe_y > y ? pe_y : y;
653 tmbbox[BOXBOTTOM] = pe_y < y ? pe_y : y;
654
655 // Determine which blocks to look in for blocking lines
656
657 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
658 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
659 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
660 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
661
662 // xl->xh, yl->yh determine the mapblock set to search
663
664 validcount++; // prevents checking same line twice
665 for (bx = xl ; bx <= xh ; bx++)
666 for (by = yl ; by <= yh ; by++)
667 if (!P_BlockLinesIterator(bx,by,PIT_CrossLine))
668 return true; // ^
669 return(false); // |
670 } // phares
671
672//
673// MOVEMENT CLIPPING
674//
675
676//
677// P_CheckPosition
678// This is purely informative, nothing is modified
679// (except things picked up).
680//
681// in:
682// a mobj_t (can be valid or invalid)
683// a position to be checked
684// (doesn't need to be related to the mobj_t->x,y)
685//
686// during:
687// special things are touched if MF_PICKUP
688// early out on solid lines?
689//
690// out:
691// newsubsec
692// floorz
693// ceilingz
694// tmdropoffz
695// the lowest point contacted
696// (monsters won't move to a dropoff)
697// speciallines[]
698// numspeciallines
699//
700
701boolean P_CheckPosition (mobj_t* thing,fixed_t x,fixed_t y)
702 {
703 int xl;
704 int xh;
705 int yl;
706 int yh;
707 int bx;
708 int by;
709 subsector_t* newsubsec;
710
711 tmthing = thing;
712
713 tmx = x;
714 tmy = y;
715
716 tmbbox[BOXTOP] = y + tmthing->radius;
717 tmbbox[BOXBOTTOM] = y - tmthing->radius;
718 tmbbox[BOXRIGHT] = x + tmthing->radius;
719 tmbbox[BOXLEFT] = x - tmthing->radius;
720
721 newsubsec = R_PointInSubsector (x,y);
722 floorline = blockline = ceilingline = NULL; // killough 8/1/98
723
724 // Whether object can get out of a sticky situation:
725 tmunstuck = thing->player && /* only players */
726 thing->player->mo == thing && /* not voodoo dolls */
727 mbf_features; /* not under old demos */
728
729 // The base floor / ceiling is from the subsector
730 // that contains the point.
731 // Any contacted lines the step closer together
732 // will adjust them.
733
734 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
735 tmceilingz = newsubsec->sector->ceilingheight;
736 validcount++;
737 numspechit = 0;
738
739 if ( tmthing->flags & MF_NOCLIP )
740 return true;
741
742 // Check things first, possibly picking things up.
743 // The bounding box is extended by MAXRADIUS
744 // because mobj_ts are grouped into mapblocks
745 // based on their origin point, and can overlap
746 // into adjacent blocks by up to MAXRADIUS units.
747
748 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
749 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
750 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
751 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
752
753
754 for (bx=xl ; bx<=xh ; bx++)
755 for (by=yl ; by<=yh ; by++)
756 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
757 return false;
758
759 // check lines
760
761 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
762 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
763 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
764 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
765
766 for (bx=xl ; bx<=xh ; bx++)
767 for (by=yl ; by<=yh ; by++)
768 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
769 return false; // doesn't fit
770
771 return true;
772 }
773
774
775//
776// P_TryMove
777// Attempt to move to a new position,
778// crossing special lines unless MF_TELEPORT is set.
779//
780boolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y,
781 boolean dropoff) // killough 3/15/98: allow dropoff as option
782 {
783 fixed_t oldx;
784 fixed_t oldy;
785
786 felldown = floatok = false; // killough 11/98
787
788 if (!P_CheckPosition (thing, x, y))
789 return false; // solid wall or thing
790
791 if ( !(thing->flags & MF_NOCLIP) )
792 {
793 // killough 7/26/98: reformatted slightly
794 // killough 8/1/98: Possibly allow escape if otherwise stuck
795
796 if (tmceilingz - tmfloorz < thing->height || // doesn't fit
797 // mobj must lower to fit
798 (floatok = true, !(thing->flags & MF_TELEPORT) &&
799 tmceilingz - thing->z < thing->height) ||
800 // too big a step up
801 (!(thing->flags & MF_TELEPORT) &&
802 tmfloorz - thing->z > 24*FRACUNIT))
803 return tmunstuck
804 && !(ceilingline && untouched(ceilingline))
805 && !( floorline && untouched( floorline));
806
807 /* killough 3/15/98: Allow certain objects to drop off
808 * killough 7/24/98, 8/1/98:
809 * Prevent monsters from getting stuck hanging off ledges
810 * killough 10/98: Allow dropoffs in controlled circumstances
811 * killough 11/98: Improve symmetry of clipping on stairs
812 */
813
814 if (!(thing->flags & (MF_DROPOFF|MF_FLOAT))) {
815 if (comp[comp_dropoff])
816 {
817 if ((compatibility || !dropoff
818 // fix demosync bug in mbf compatibility mode
819 || (mbf_features && compatibility_level <= prboom_2_compatibility))
820 && (tmfloorz - tmdropoffz > 24*FRACUNIT))
821 return false; // don't stand over a dropoff
822 }
823 else
824 if (!dropoff || (dropoff==2 && // large jump down (e.g. dogs)
825 (tmfloorz-tmdropoffz > 128*FRACUNIT ||
826 !thing->target || thing->target->z >tmdropoffz)))
827 {
828 if (!monkeys || !mbf_features ?
829 tmfloorz - tmdropoffz > 24*FRACUNIT :
830 thing->floorz - tmfloorz > 24*FRACUNIT ||
831 thing->dropoffz - tmdropoffz > 24*FRACUNIT)
832 return false;
833 }
834 else { /* dropoff allowed -- check for whether it fell more than 24 */
835 felldown = !(thing->flags & MF_NOGRAVITY) &&
836 thing->z - tmfloorz > 24*FRACUNIT;
837 }
838 }
839
840 if (thing->flags & MF_BOUNCES && // killough 8/13/98
841 !(thing->flags & (MF_MISSILE|MF_NOGRAVITY)) &&
842 !sentient(thing) && tmfloorz - thing->z > 16*FRACUNIT)
843 return false; // too big a step up for bouncers under gravity
844
845 // killough 11/98: prevent falling objects from going up too many steps
846 if (thing->intflags & MIF_FALLING && tmfloorz - thing->z >
847 FixedMul(thing->momx,thing->momx)+FixedMul(thing->momy,thing->momy))
848 return false;
849 }
850
851 // the move is ok,
852 // so unlink from the old position and link into the new position
853
854 P_UnsetThingPosition (thing);
855
856 oldx = thing->x;
857 oldy = thing->y;
858 thing->floorz = tmfloorz;
859 thing->ceilingz = tmceilingz;
860 thing->dropoffz = tmdropoffz; // killough 11/98: keep track of dropoffs
861 thing->x = x;
862 thing->y = y;
863
864 P_SetThingPosition (thing);
865
866 // if any special lines were hit, do the effect
867
868 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
869 while (numspechit--)
870 if (spechit[numspechit]->special) // see if the line was crossed
871 {
872 int oldside;
873 if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) !=
874 P_PointOnLineSide(thing->x, thing->y, spechit[numspechit]))
875 P_CrossSpecialLine(spechit[numspechit], oldside, thing);
876 }
877
878 return true;
879 }
880
881/*
882 * killough 9/12/98:
883 *
884 * Apply "torque" to objects hanging off of ledges, so that they
885 * fall off. It's not really torque, since Doom has no concept of
886 * rotation, but it's a convincing effect which avoids anomalies
887 * such as lifeless objects hanging more than halfway off of ledges,
888 * and allows objects to roll off of the edges of moving lifts, or
889 * to slide up and then back down stairs, or to fall into a ditch.
890 * If more than one linedef is contacted, the effects are cumulative,
891 * so balancing is possible.
892 */
893
894static boolean PIT_ApplyTorque(line_t *ld)
895{
896 if (ld->backsector && // If thing touches two-sided pivot linedef
897 tmbbox[BOXRIGHT] > ld->bbox[BOXLEFT] &&
898 tmbbox[BOXLEFT] < ld->bbox[BOXRIGHT] &&
899 tmbbox[BOXTOP] > ld->bbox[BOXBOTTOM] &&
900 tmbbox[BOXBOTTOM] < ld->bbox[BOXTOP] &&
901 P_BoxOnLineSide(tmbbox, ld) == -1)
902 {
903 mobj_t *mo = tmthing;
904
905 fixed_t dist = // lever arm
906 + (ld->dx >> FRACBITS) * (mo->y >> FRACBITS)
907 - (ld->dy >> FRACBITS) * (mo->x >> FRACBITS)
908 - (ld->dx >> FRACBITS) * (ld->v1->y >> FRACBITS)
909 + (ld->dy >> FRACBITS) * (ld->v1->x >> FRACBITS);
910
911 if (dist < 0 ? // dropoff direction
912 ld->frontsector->floorheight < mo->z &&
913 ld->backsector->floorheight >= mo->z :
914 ld->backsector->floorheight < mo->z &&
915 ld->frontsector->floorheight >= mo->z)
916 {
917 /* At this point, we know that the object straddles a two-sided
918 * linedef, and that the object's center of mass is above-ground.
919 */
920
921 fixed_t x = D_abs(ld->dx), y = D_abs(ld->dy);
922
923 if (y > x)
924 {
925 fixed_t t = x;
926 x = y;
927 y = t;
928 }
929
930 y = finesine[(tantoangle[FixedDiv(y,x)>>DBITS] +
931 ANG90) >> ANGLETOFINESHIFT];
932
933 /* Momentum is proportional to distance between the
934 * object's center of mass and the pivot linedef.
935 *
936 * It is scaled by 2^(OVERDRIVE - gear). When gear is
937 * increased, the momentum gradually decreases to 0 for
938 * the same amount of pseudotorque, so that oscillations
939 * are prevented, yet it has a chance to reach equilibrium.
940 */
941 dist = FixedDiv(FixedMul(dist, (mo->gear < OVERDRIVE) ?
942 y << -(mo->gear - OVERDRIVE) :
943 y >> +(mo->gear - OVERDRIVE)), x);
944
945 /* Apply momentum away from the pivot linedef. */
946
947 x = FixedMul(ld->dy, dist);
948 y = FixedMul(ld->dx, dist);
949
950 /* Avoid moving too fast all of a sudden (step into "overdrive") */
951
952 dist = FixedMul(x,x) + FixedMul(y,y);
953
954 while (dist > FRACUNIT*4 && mo->gear < MAXGEAR)
955 ++mo->gear, x >>= 1, y >>= 1, dist >>= 1;
956
957 mo->momx -= x;
958 mo->momy += y;
959 }
960 }
961 return true;
962}
963
964/*
965 * killough 9/12/98
966 *
967 * Applies "torque" to objects, based on all contacted linedefs
968 */
969
970void P_ApplyTorque(mobj_t *mo)
971{
972 int xl = ((tmbbox[BOXLEFT] =
973 mo->x - mo->radius) - bmaporgx) >> MAPBLOCKSHIFT;
974 int xh = ((tmbbox[BOXRIGHT] =
975 mo->x + mo->radius) - bmaporgx) >> MAPBLOCKSHIFT;
976 int yl = ((tmbbox[BOXBOTTOM] =
977 mo->y - mo->radius) - bmaporgy) >> MAPBLOCKSHIFT;
978 int yh = ((tmbbox[BOXTOP] =
979 mo->y + mo->radius) - bmaporgy) >> MAPBLOCKSHIFT;
980 int bx,by,flags = mo->intflags; //Remember the current state, for gear-change
981
982 tmthing = mo;
983 validcount++; /* prevents checking same line twice */
984
985 for (bx = xl ; bx <= xh ; bx++)
986 for (by = yl ; by <= yh ; by++)
987 P_BlockLinesIterator(bx, by, PIT_ApplyTorque);
988
989 /* If any momentum, mark object as 'falling' using engine-internal flags */
990 if (mo->momx | mo->momy)
991 mo->intflags |= MIF_FALLING;
992 else // Clear the engine-internal flag indicating falling object.
993 mo->intflags &= ~MIF_FALLING;
994
995 /* If the object has been moving, step up the gear.
996 * This helps reach equilibrium and avoid oscillations.
997 *
998 * Doom has no concept of potential energy, much less
999 * of rotation, so we have to creatively simulate these
1000 * systems somehow :)
1001 */
1002
1003 if (!((mo->intflags | flags) & MIF_FALLING)) // If not falling for a while,
1004 mo->gear = 0; // Reset it to full strength
1005 else
1006 if (mo->gear < MAXGEAR) // Else if not at max gear,
1007 mo->gear++; // move up a gear
1008}
1009
1010//
1011// P_ThingHeightClip
1012// Takes a valid thing and adjusts the thing->floorz,
1013// thing->ceilingz, and possibly thing->z.
1014// This is called for all nearby monsters
1015// whenever a sector changes height.
1016// If the thing doesn't fit,
1017// the z will be set to the lowest value
1018// and false will be returned.
1019//
1020
1021boolean P_ThingHeightClip (mobj_t* thing)
1022{
1023 boolean onfloor;
1024
1025 onfloor = (thing->z == thing->floorz);
1026
1027 P_CheckPosition (thing, thing->x, thing->y);
1028
1029 /* what about stranding a monster partially off an edge?
1030 * killough 11/98: Answer: see below (upset balance if hanging off ledge)
1031 */
1032
1033 thing->floorz = tmfloorz;
1034 thing->ceilingz = tmceilingz;
1035 thing->dropoffz = tmdropoffz; /* killough 11/98: remember dropoffs */
1036
1037 if (onfloor)
1038 {
1039
1040 // walking monsters rise and fall with the floor
1041
1042 thing->z = thing->floorz;
1043
1044 /* killough 11/98: Possibly upset balance of objects hanging off ledges */
1045 if (thing->intflags & MIF_FALLING && thing->gear >= MAXGEAR)
1046 thing->gear = 0;
1047 }
1048 else
1049 {
1050
1051 // don't adjust a floating monster unless forced to
1052
1053 if (thing->z+thing->height > thing->ceilingz)
1054 thing->z = thing->ceilingz - thing->height;
1055 }
1056
1057 return thing->ceilingz - thing->floorz >= thing->height;
1058}
1059
1060
1061//
1062// SLIDE MOVE
1063// Allows the player to slide along any angled walls.
1064//
1065
1066/* killough 8/2/98: make variables static */
1067static fixed_t bestslidefrac;
1068static line_t* bestslideline;
1069static mobj_t* slidemo;
1070static fixed_t tmxmove;
1071static fixed_t tmymove;
1072
1073
1074//
1075// P_HitSlideLine
1076// Adjusts the xmove / ymove
1077// so that the next move will slide along the wall.
1078// If the floor is icy, then you can bounce off a wall. // phares
1079//
1080
1081void P_HitSlideLine (line_t* ld)
1082 {
1083 int side;
1084 angle_t lineangle;
1085 angle_t moveangle;
1086 angle_t deltaangle;
1087 fixed_t movelen;
1088 fixed_t newlen;
1089 boolean icyfloor; // is floor icy? // phares
1090 // |
1091 // Under icy conditions, if the angle of approach to the wall // V
1092 // is more than 45 degrees, then you'll bounce and lose half
1093 // your momentum. If less than 45 degrees, you'll slide along
1094 // the wall. 45 is arbitrary and is believable.
1095
1096 // Check for the special cases of horz or vert walls.
1097
1098 /* killough 10/98: only bounce if hit hard (prevents wobbling)
1099 * cph - DEMOSYNC - should only affect players in Boom demos? */
1100
1101 //e6y
1102 if (mbf_features)
1103 {
1104 icyfloor =
1105 P_AproxDistance(tmxmove, tmymove) > 4*FRACUNIT &&
1106 variable_friction && // killough 8/28/98: calc friction on demand
1107 slidemo->z <= slidemo->floorz &&
1108 P_GetFriction(slidemo, NULL) > ORIG_FRICTION;
1109 }
1110 else
1111 {
1112 extern boolean onground;
1113 icyfloor = !compatibility &&
1114 variable_friction &&
1115 slidemo->player &&
1116 onground &&
1117 slidemo->friction > ORIG_FRICTION;
1118 }
1119
1120 if (ld->slopetype == ST_HORIZONTAL)
1121 {
1122 if (icyfloor && (D_abs(tmymove) > D_abs(tmxmove)))
1123 {
1124 tmxmove /= 2; // absorb half the momentum
1125 tmymove = -tmymove/2;
1126 S_StartSound(slidemo,sfx_oof); // oooff!
1127 }
1128 else
1129 tmymove = 0; // no more movement in the Y direction
1130 return;
1131 }
1132
1133 if (ld->slopetype == ST_VERTICAL)
1134 {
1135 if (icyfloor && (D_abs(tmxmove) > D_abs(tmymove)))
1136 {
1137 tmxmove = -tmxmove/2; // absorb half the momentum
1138 tmymove /= 2;
1139 S_StartSound(slidemo,sfx_oof); // oooff! // ^
1140 } // |
1141 else // phares
1142 tmxmove = 0; // no more movement in the X direction
1143 return;
1144 }
1145
1146 // The wall is angled. Bounce if the angle of approach is // phares
1147 // less than 45 degrees. // phares
1148
1149 side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
1150
1151 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
1152 if (side == 1)
1153 lineangle += ANG180;
1154 moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
1155
1156 // killough 3/2/98:
1157 // The moveangle+=10 breaks v1.9 demo compatibility in
1158 // some demos, so it needs demo_compatibility switch.
1159
1160 if (!demo_compatibility)
1161 moveangle += 10; // prevents sudden path reversal due to // phares
1162 // rounding error // |
1163 deltaangle = moveangle-lineangle; // V
1164 movelen = P_AproxDistance (tmxmove, tmymove);
1165 if (icyfloor && (deltaangle > ANG45) && (deltaangle < ANG90+ANG45))
1166 {
1167 moveangle = lineangle - deltaangle;
1168 movelen /= 2; // absorb
1169 S_StartSound(slidemo,sfx_oof); // oooff!
1170 moveangle >>= ANGLETOFINESHIFT;
1171 tmxmove = FixedMul (movelen, finecosine[moveangle]);
1172 tmymove = FixedMul (movelen, finesine[moveangle]);
1173 } // ^
1174 else // |
1175 { // phares
1176 if (deltaangle > ANG180)
1177 deltaangle += ANG180;
1178
1179 // I_Error ("SlideLine: ang>ANG180");
1180
1181 lineangle >>= ANGLETOFINESHIFT;
1182 deltaangle >>= ANGLETOFINESHIFT;
1183 newlen = FixedMul (movelen, finecosine[deltaangle]);
1184 tmxmove = FixedMul (newlen, finecosine[lineangle]);
1185 tmymove = FixedMul (newlen, finesine[lineangle]);
1186 } // phares
1187 }
1188
1189
1190//
1191// PTR_SlideTraverse
1192//
1193
1194boolean PTR_SlideTraverse (intercept_t* in)
1195 {
1196 line_t* li;
1197
1198 if (!in->isaline)
1199 I_Error ("PTR_SlideTraverse: not a line?");
1200
1201 li = in->d.line;
1202
1203 if ( ! (li->flags & ML_TWOSIDED) )
1204 {
1205 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1206 return true; // don't hit the back side
1207 goto isblocking;
1208 }
1209
1210 // set openrange, opentop, openbottom.
1211 // These define a 'window' from one sector to another across a line
1212
1213 P_LineOpening (li);
1214
1215 if (openrange < slidemo->height)
1216 goto isblocking; // doesn't fit
1217
1218 if (opentop - slidemo->z < slidemo->height)
1219 goto isblocking; // mobj is too high
1220
1221 if (openbottom - slidemo->z > 24*FRACUNIT )
1222 goto isblocking; // too big a step up
1223
1224 // this line doesn't block movement
1225
1226 return true;
1227
1228 // the line does block movement,
1229 // see if it is closer than best so far
1230
1231isblocking:
1232
1233 if (in->frac < bestslidefrac)
1234 {
1235 bestslidefrac = in->frac;
1236 bestslideline = li;
1237 }
1238
1239 return false; // stop
1240 }
1241
1242
1243//
1244// P_SlideMove
1245// The momx / momy move is bad, so try to slide
1246// along a wall.
1247// Find the first line hit, move flush to it,
1248// and slide along it
1249//
1250// This is a kludgy mess.
1251//
1252// killough 11/98: reformatted
1253
1254void P_SlideMove(mobj_t *mo)
1255{
1256 int hitcount = 3;
1257
1258 slidemo = mo; // the object that's sliding
1259
1260 do
1261 {
1262 fixed_t leadx, leady, trailx, traily;
1263
1264 if (!--hitcount)
1265 goto stairstep; // don't loop forever
1266
1267 // trace along the three leading corners
1268
1269 if (mo->momx > 0)
1270 leadx = mo->x + mo->radius, trailx = mo->x - mo->radius;
1271 else
1272 leadx = mo->x - mo->radius, trailx = mo->x + mo->radius;
1273
1274 if (mo->momy > 0)
1275 leady = mo->y + mo->radius, traily = mo->y - mo->radius;
1276 else
1277 leady = mo->y - mo->radius, traily = mo->y + mo->radius;
1278
1279 bestslidefrac = FRACUNIT+1;
1280
1281 P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy,
1282 PT_ADDLINES, PTR_SlideTraverse);
1283 P_PathTraverse(trailx, leady, trailx+mo->momx, leady+mo->momy,
1284 PT_ADDLINES, PTR_SlideTraverse);
1285 P_PathTraverse(leadx, traily, leadx+mo->momx, traily+mo->momy,
1286 PT_ADDLINES, PTR_SlideTraverse);
1287
1288 // move up to the wall
1289
1290 if (bestslidefrac == FRACUNIT+1)
1291 {
1292 // the move must have hit the middle, so stairstep
1293
1294 stairstep:
1295
1296 /* killough 3/15/98: Allow objects to drop off ledges
1297 *
1298 * phares 5/4/98: kill momentum if you can't move at all
1299 * This eliminates player bobbing if pressed against a wall
1300 * while on ice.
1301 *
1302 * killough 10/98: keep buggy code around for old Boom demos
1303 *
1304 * cph 2000/09//23: buggy code was only in Boom v2.01
1305 */
1306
1307 if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true))
1308 if (!P_TryMove(mo, mo->x + mo->momx, mo->y, true))
1309 if (compatibility_level == boom_201_compatibility)
1310 mo->momx = mo->momy = 0;
1311
1312 break;
1313 }
1314
1315 // fudge a bit to make sure it doesn't hit
1316
1317 if ((bestslidefrac -= 0x800) > 0)
1318 {
1319 fixed_t newx = FixedMul(mo->momx, bestslidefrac);
1320 fixed_t newy = FixedMul(mo->momy, bestslidefrac);
1321
1322 // killough 3/15/98: Allow objects to drop off ledges
1323
1324 if (!P_TryMove(mo, mo->x+newx, mo->y+newy, true))
1325 goto stairstep;
1326 }
1327
1328 // Now continue along the wall.
1329 // First calculate remainder.
1330
1331 bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
1332
1333 if (bestslidefrac > FRACUNIT)
1334 bestslidefrac = FRACUNIT;
1335
1336 if (bestslidefrac <= 0)
1337 break;
1338
1339 tmxmove = FixedMul(mo->momx, bestslidefrac);
1340 tmymove = FixedMul(mo->momy, bestslidefrac);
1341
1342 P_HitSlideLine(bestslideline); // clip the moves
1343
1344 mo->momx = tmxmove;
1345 mo->momy = tmymove;
1346
1347 /* killough 10/98: affect the bobbing the same way (but not voodoo dolls)
1348 * cph - DEMOSYNC? */
1349 if (mo->player && mo->player->mo == mo)
1350 {
1351 if (D_abs(mo->player->momx) > D_abs(tmxmove))
1352 mo->player->momx = tmxmove;
1353 if (D_abs(mo->player->momy) > D_abs(tmymove))
1354 mo->player->momy = tmymove;
1355 }
1356 } // killough 3/15/98: Allow objects to drop off ledges:
1357 while (!P_TryMove(mo, mo->x+tmxmove, mo->y+tmymove, true));
1358}
1359
1360//
1361// P_LineAttack
1362//
1363mobj_t* linetarget; // who got hit (or NULL)
1364static mobj_t* shootthing;
1365
1366/* killough 8/2/98: for more intelligent autoaiming */
1367static uint_64_t aim_flags_mask;
1368
1369// Height if not aiming up or down
1370fixed_t shootz;
1371
1372int la_damage;
1373fixed_t attackrange;
1374
1375static fixed_t aimslope;
1376
1377// slopes to top and bottom of target
1378// killough 4/20/98: make static instead of using ones in p_sight.c
1379
1380static fixed_t topslope;
1381static fixed_t bottomslope;
1382
1383
1384//
1385// PTR_AimTraverse
1386// Sets linetaget and aimslope when a target is aimed at.
1387//
1388boolean PTR_AimTraverse (intercept_t* in)
1389 {
1390 line_t* li;
1391 mobj_t* th;
1392 fixed_t slope;
1393 fixed_t thingtopslope;
1394 fixed_t thingbottomslope;
1395 fixed_t dist;
1396
1397 if (in->isaline)
1398 {
1399 li = in->d.line;
1400
1401 if ( !(li->flags & ML_TWOSIDED) )
1402 return false; // stop
1403
1404 // Crosses a two sided line.
1405 // A two sided line will restrict
1406 // the possible target ranges.
1407
1408 P_LineOpening (li);
1409
1410 if (openbottom >= opentop)
1411 return false; // stop
1412
1413 dist = FixedMul (attackrange, in->frac);
1414
1415 if (li->frontsector->floorheight != li->backsector->floorheight)
1416 {
1417 slope = FixedDiv (openbottom - shootz , dist);
1418 if (slope > bottomslope)
1419 bottomslope = slope;
1420 }
1421
1422 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1423 {
1424 slope = FixedDiv (opentop - shootz , dist);
1425 if (slope < topslope)
1426 topslope = slope;
1427 }
1428
1429 if (topslope <= bottomslope)
1430 return false; // stop
1431
1432 return true; // shot continues
1433 }
1434
1435 // shoot a thing
1436
1437 th = in->d.thing;
1438 if (th == shootthing)
1439 return true; // can't shoot self
1440
1441 if (!(th->flags&MF_SHOOTABLE))
1442 return true; // corpse or something
1443
1444 /* killough 7/19/98, 8/2/98:
1445 * friends don't aim at friends (except players), at least not first
1446 */
1447 if (th->flags & shootthing->flags & aim_flags_mask && !th->player)
1448 return true;
1449
1450 // check angles to see if the thing can be aimed at
1451
1452 dist = FixedMul (attackrange, in->frac);
1453 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1454
1455 if (thingtopslope < bottomslope)
1456 return true; // shot over the thing
1457
1458 thingbottomslope = FixedDiv (th->z - shootz, dist);
1459
1460 if (thingbottomslope > topslope)
1461 return true; // shot under the thing
1462
1463 // this thing can be hit!
1464
1465 if (thingtopslope > topslope)
1466 thingtopslope = topslope;
1467
1468 if (thingbottomslope < bottomslope)
1469 thingbottomslope = bottomslope;
1470
1471 aimslope = (thingtopslope+thingbottomslope)/2;
1472 linetarget = th;
1473
1474 return false; // don't go any farther
1475 }
1476
1477
1478//
1479// PTR_ShootTraverse
1480//
1481boolean PTR_ShootTraverse (intercept_t* in)
1482 {
1483 fixed_t x;
1484 fixed_t y;
1485 fixed_t z;
1486 fixed_t frac;
1487
1488 mobj_t* th;
1489
1490 fixed_t slope;
1491 fixed_t dist;
1492 fixed_t thingtopslope;
1493 fixed_t thingbottomslope;
1494
1495 if (in->isaline)
1496 {
1497 line_t *li = in->d.line;
1498
1499 if (li->special)
1500 P_ShootSpecialLine (shootthing, li);
1501
1502 if (li->flags & ML_TWOSIDED)
1503 { // crosses a two sided (really 2s) line
1504 P_LineOpening (li);
1505 dist = FixedMul(attackrange, in->frac);
1506
1507 // killough 11/98: simplify
1508
1509 if ((li->frontsector->floorheight==li->backsector->floorheight ||
1510 (slope = FixedDiv(openbottom - shootz , dist)) <= aimslope) &&
1511 (li->frontsector->ceilingheight==li->backsector->ceilingheight ||
1512 (slope = FixedDiv (opentop - shootz , dist)) >= aimslope))
1513 return true; // shot continues
1514 }
1515
1516 // hit line
1517 // position a bit closer
1518
1519 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1520 x = trace.x + FixedMul (trace.dx, frac);
1521 y = trace.y + FixedMul (trace.dy, frac);
1522 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1523
1524 if (li->frontsector->ceilingpic == skyflatnum)
1525 {
1526 // don't shoot the sky!
1527
1528 if (z > li->frontsector->ceilingheight)
1529 return false;
1530
1531 // it's a sky hack wall
1532
1533 if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1534
1535 // fix bullet-eaters -- killough:
1536 // WARNING: Almost all demos will lose sync without this
1537 // demo_compatibility flag check!!! killough 1/18/98
1538 if (demo_compatibility || li->backsector->ceilingheight < z)
1539 return false;
1540 }
1541
1542 // Spawn bullet puffs.
1543
1544 P_SpawnPuff (x,y,z);
1545
1546 // don't go any farther
1547
1548 return false;
1549 }
1550
1551 // shoot a thing
1552
1553 th = in->d.thing;
1554 if (th == shootthing)
1555 return true; // can't shoot self
1556
1557 if (!(th->flags&MF_SHOOTABLE))
1558 return true; // corpse or something
1559
1560 // check angles to see if the thing can be aimed at
1561
1562 dist = FixedMul (attackrange, in->frac);
1563 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1564
1565 if (thingtopslope < aimslope)
1566 return true; // shot over the thing
1567
1568 thingbottomslope = FixedDiv (th->z - shootz, dist);
1569
1570 if (thingbottomslope > aimslope)
1571 return true; // shot under the thing
1572
1573 // hit thing
1574 // position a bit closer
1575
1576 frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1577
1578 x = trace.x + FixedMul (trace.dx, frac);
1579 y = trace.y + FixedMul (trace.dy, frac);
1580 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1581
1582 // Spawn bullet puffs or blod spots,
1583 // depending on target type.
1584 if (in->d.thing->flags & MF_NOBLOOD)
1585 P_SpawnPuff (x,y,z);
1586 else
1587 P_SpawnBlood (x,y,z, la_damage);
1588
1589 if (la_damage)
1590 P_DamageMobj (th, shootthing, shootthing, la_damage);
1591
1592 // don't go any farther
1593 return false;
1594 }
1595
1596
1597//
1598// P_AimLineAttack
1599//
1600fixed_t P_AimLineAttack(mobj_t* t1,angle_t angle,fixed_t distance, uint_64_t mask)
1601 {
1602 fixed_t x2;
1603 fixed_t y2;
1604
1605 angle >>= ANGLETOFINESHIFT;
1606 shootthing = t1;
1607
1608 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1609 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1610 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1611
1612 // can't shoot outside view angles
1613
1614 topslope = 100*FRACUNIT/160;
1615 bottomslope = -100*FRACUNIT/160;
1616
1617 attackrange = distance;
1618 linetarget = NULL;
1619
1620 /* killough 8/2/98: prevent friends from aiming at friends */
1621 aim_flags_mask = mask;
1622
1623 P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_AimTraverse);
1624
1625 if (linetarget)
1626 return aimslope;
1627
1628 return 0;
1629 }
1630
1631
1632//
1633// P_LineAttack
1634// If damage == 0, it is just a test trace
1635// that will leave linetarget set.
1636//
1637
1638void P_LineAttack
1639(mobj_t* t1,
1640 angle_t angle,
1641 fixed_t distance,
1642 fixed_t slope,
1643 int damage)
1644 {
1645 fixed_t x2;
1646 fixed_t y2;
1647
1648 angle >>= ANGLETOFINESHIFT;
1649 shootthing = t1;
1650 la_damage = damage;
1651 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1652 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1653 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1654 attackrange = distance;
1655 aimslope = slope;
1656
1657 P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_ShootTraverse);
1658 }
1659
1660
1661//
1662// USE LINES
1663//
1664
1665mobj_t* usething;
1666
1667boolean PTR_UseTraverse (intercept_t* in)
1668 {
1669 int side;
1670
1671 if (!in->d.line->special)
1672 {
1673 P_LineOpening (in->d.line);
1674 if (openrange <= 0)
1675 {
1676 S_StartSound (usething, sfx_noway);
1677
1678 // can't use through a wall
1679 return false;
1680 }
1681
1682 // not a special line, but keep checking
1683
1684 return true;
1685 }
1686
1687 side = 0;
1688 if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1689 side = 1;
1690
1691 // return false; // don't use back side
1692
1693 P_UseSpecialLine (usething, in->d.line, side);
1694
1695 //WAS can't use for than one special line in a row
1696 //jff 3/21/98 NOW multiple use allowed with enabling line flag
1697
1698 return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))?
1699 true : false;
1700}
1701
1702// Returns false if a "oof" sound should be made because of a blocking
1703// linedef. Makes 2s middles which are impassable, as well as 2s uppers
1704// and lowers which block the player, cause the sound effect when the
1705// player tries to activate them. Specials are excluded, although it is
1706// assumed that all special linedefs within reach have been considered
1707// and rejected already (see P_UseLines).
1708//
1709// by Lee Killough
1710//
1711
1712boolean PTR_NoWayTraverse(intercept_t* in)
1713 {
1714 line_t *ld = in->d.line;
1715 // This linedef
1716 return ld->special || !( // Ignore specials
1717 ld->flags & ML_BLOCKING || ( // Always blocking
1718 P_LineOpening(ld), // Find openings
1719 openrange <= 0 || // No opening
1720 openbottom > usething->z+24*FRACUNIT || // Too high it blocks
1721 opentop < usething->z+usething->height // Too low it blocks
1722 )
1723 );
1724 }
1725
1726//
1727// P_UseLines
1728// Looks for special lines in front of the player to activate.
1729//
1730void P_UseLines (player_t* player)
1731 {
1732 int angle;
1733 fixed_t x1;
1734 fixed_t y1;
1735 fixed_t x2;
1736 fixed_t y2;
1737
1738 usething = player->mo;
1739
1740 angle = player->mo->angle >> ANGLETOFINESHIFT;
1741
1742 x1 = player->mo->x;
1743 y1 = player->mo->y;
1744 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1745 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1746
1747 // old code:
1748 //
1749 // P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1750 //
1751 // This added test makes the "oof" sound work on 2s lines -- killough:
1752
1753 if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ))
1754 if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse ))
1755 S_StartSound (usething, sfx_noway);
1756 }
1757
1758
1759//
1760// RADIUS ATTACK
1761//
1762
1763static mobj_t *bombsource, *bombspot;
1764static int bombdamage;
1765
1766
1767//
1768// PIT_RadiusAttack
1769// "bombsource" is the creature
1770// that caused the explosion at "bombspot".
1771//
1772
1773boolean PIT_RadiusAttack (mobj_t* thing)
1774 {
1775 fixed_t dx;
1776 fixed_t dy;
1777 fixed_t dist;
1778
1779 /* killough 8/20/98: allow bouncers to take damage
1780 * (missile bouncers are already excluded with MF_NOBLOCKMAP)
1781 */
1782
1783 if (!(thing->flags & (MF_SHOOTABLE | MF_BOUNCES)))
1784 return true;
1785
1786 // Boss spider and cyborg
1787 // take no damage from concussion.
1788
1789 // killough 8/10/98: allow grenades to hurt anyone, unless
1790 // fired by Cyberdemons, in which case it won't hurt Cybers.
1791
1792 if (bombspot->flags & MF_BOUNCES ?
1793 thing->type == MT_CYBORG && bombsource->type == MT_CYBORG :
1794 thing->type == MT_CYBORG || thing->type == MT_SPIDER)
1795 return true;
1796
1797 dx = D_abs(thing->x - bombspot->x);
1798 dy = D_abs(thing->y - bombspot->y);
1799
1800 dist = dx>dy ? dx : dy;
1801 dist = (dist - thing->radius) >> FRACBITS;
1802
1803 if (dist < 0)
1804 dist = 0;
1805
1806 if (dist >= bombdamage)
1807 return true; // out of range
1808
1809 if ( P_CheckSight (thing, bombspot) )
1810 {
1811 // must be in direct path
1812 P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1813 }
1814
1815 return true;
1816 }
1817
1818
1819//
1820// P_RadiusAttack
1821// Source is the creature that caused the explosion at spot.
1822//
1823void P_RadiusAttack(mobj_t* spot,mobj_t* source,int damage)
1824 {
1825 int x;
1826 int y;
1827
1828 int xl;
1829 int xh;
1830 int yl;
1831 int yh;
1832
1833 fixed_t dist;
1834
1835 dist = (damage+MAXRADIUS)<<FRACBITS;
1836 yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1837 yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1838 xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1839 xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1840 bombspot = spot;
1841 bombsource = source;
1842 bombdamage = damage;
1843
1844 for (y=yl ; y<=yh ; y++)
1845 for (x=xl ; x<=xh ; x++)
1846 P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1847 }
1848
1849
1850
1851//
1852// SECTOR HEIGHT CHANGING
1853// After modifying a sectors floor or ceiling height,
1854// call this routine to adjust the positions
1855// of all things that touch the sector.
1856//
1857// If anything doesn't fit anymore, true will be returned.
1858// If crunch is true, they will take damage
1859// as they are being crushed.
1860// If Crunch is false, you should set the sector height back
1861// the way it was and call P_ChangeSector again
1862// to undo the changes.
1863//
1864
1865static boolean crushchange, nofit;
1866
1867//
1868// PIT_ChangeSector
1869//
1870
1871boolean PIT_ChangeSector (mobj_t* thing)
1872 {
1873 mobj_t* mo;
1874
1875 if (P_ThingHeightClip (thing))
1876 return true; // keep checking
1877
1878 // crunch bodies to giblets
1879
1880 if (thing->health <= 0)
1881 {
1882 P_SetMobjState (thing, S_GIBS);
1883
1884 thing->flags &= ~MF_SOLID;
1885 thing->height = 0;
1886 thing->radius = 0;
1887 return true; // keep checking
1888 }
1889
1890 // crunch dropped items
1891
1892 if (thing->flags & MF_DROPPED)
1893 {
1894 P_RemoveMobj (thing);
1895
1896 // keep checking
1897 return true;
1898 }
1899
1900 /* killough 11/98: kill touchy things immediately */
1901 if (thing->flags & MF_TOUCHY &&
1902 (thing->intflags & MIF_ARMED || sentient(thing)))
1903 {
1904 P_DamageMobj(thing, NULL, NULL, thing->health); // kill object
1905 return true; // keep checking
1906 }
1907
1908 if (! (thing->flags & MF_SHOOTABLE) )
1909 {
1910 // assume it is bloody gibs or something
1911 return true;
1912 }
1913
1914 nofit = true;
1915
1916 if (crushchange && !(leveltime&3)) {
1917 int t;
1918 P_DamageMobj(thing,NULL,NULL,10);
1919
1920 // spray blood in a random direction
1921 mo = P_SpawnMobj (thing->x,
1922 thing->y,
1923 thing->z + thing->height/2, MT_BLOOD);
1924
1925 /* killough 8/10/98: remove dependence on order of evaluation */
1926 t = P_Random(pr_crush);
1927 mo->momx = (t - P_Random (pr_crush))<<12;
1928 t = P_Random(pr_crush);
1929 mo->momy = (t - P_Random (pr_crush))<<12;
1930 }
1931
1932 // keep checking (crush other things)
1933 return true;
1934 }
1935
1936
1937//
1938// P_ChangeSector
1939//
1940boolean P_ChangeSector(sector_t* sector,boolean crunch)
1941 {
1942 int x;
1943 int y;
1944
1945 nofit = false;
1946 crushchange = crunch;
1947
1948 // ARRGGHHH!!!!
1949 // This is horrendously slow!!!
1950 // killough 3/14/98
1951
1952 // re-check heights for all things near the moving sector
1953
1954 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1955 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1956 P_BlockThingsIterator (x, y, PIT_ChangeSector);
1957
1958 return nofit;
1959 }
1960
1961//
1962// P_CheckSector
1963// jff 3/19/98 added to just check monsters on the periphery
1964// of a moving sector instead of all in bounding box of the
1965// sector. Both more accurate and faster.
1966//
1967
1968boolean P_CheckSector(sector_t* sector,boolean crunch)
1969 {
1970 msecnode_t *n;
1971
1972 if (comp[comp_floors]) /* use the old routine for old demos though */
1973 return P_ChangeSector(sector,crunch);
1974
1975 nofit = false;
1976 crushchange = crunch;
1977
1978 // killough 4/4/98: scan list front-to-back until empty or exhausted,
1979 // restarting from beginning after each thing is processed. Avoids
1980 // crashes, and is sure to examine all things in the sector, and only
1981 // the things which are in the sector, until a steady-state is reached.
1982 // Things can arbitrarily be inserted and removed and it won't mess up.
1983 //
1984 // killough 4/7/98: simplified to avoid using complicated counter
1985
1986 // Mark all things invalid
1987
1988 for (n=sector->touching_thinglist; n; n=n->m_snext)
1989 n->visited = false;
1990
1991 do
1992 for (n=sector->touching_thinglist; n; n=n->m_snext) // go through list
1993 if (!n->visited) // unprocessed thing found
1994 {
1995 n->visited = true; // mark thing as processed
1996 if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
1997 PIT_ChangeSector(n->m_thing); // process it
1998 break; // exit and start over
1999 }
2000 while (n); // repeat from scratch until all things left are marked valid
2001
2002 return nofit;
2003 }
2004
2005
2006// CPhipps -
2007// Use block memory allocator here
2008
2009#include "z_bmalloc.h"
2010
2011IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(secnodezone, sizeof(msecnode_t), PU_LEVEL, 32, "SecNodes");
2012
2013inline static msecnode_t* P_GetSecnode(void)
2014{
2015 return (msecnode_t*)Z_BMalloc(&secnodezone);
2016}
2017
2018// P_PutSecnode() returns a node to the freelist.
2019
2020inline static void P_PutSecnode(msecnode_t* node)
2021{
2022 Z_BFree(&secnodezone, node);
2023}
2024
2025// phares 3/16/98
2026//
2027// P_AddSecnode() searches the current list to see if this sector is
2028// already there. If not, it adds a sector node at the head of the list of
2029// sectors this object appears in. This is called when creating a list of
2030// nodes that will get linked in later. Returns a pointer to the new node.
2031
2032msecnode_t* P_AddSecnode(sector_t* s, mobj_t* thing, msecnode_t* nextnode)
2033 {
2034 msecnode_t* node;
2035
2036 node = nextnode;
2037 while (node)
2038 {
2039 if (node->m_sector == s) // Already have a node for this sector?
2040 {
2041 node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
2042 return(nextnode);
2043 }
2044 node = node->m_tnext;
2045 }
2046
2047 // Couldn't find an existing node for this sector. Add one at the head
2048 // of the list.
2049
2050 node = P_GetSecnode();
2051
2052 // killough 4/4/98, 4/7/98: mark new nodes unvisited.
2053 node->visited = 0;
2054
2055 node->m_sector = s; // sector
2056 node->m_thing = thing; // mobj
2057 node->m_tprev = NULL; // prev node on Thing thread
2058 node->m_tnext = nextnode; // next node on Thing thread
2059 if (nextnode)
2060 nextnode->m_tprev = node; // set back link on Thing
2061
2062 // Add new node at head of sector thread starting at s->touching_thinglist
2063
2064 node->m_sprev = NULL; // prev node on sector thread
2065 node->m_snext = s->touching_thinglist; // next node on sector thread
2066 if (s->touching_thinglist)
2067 node->m_snext->m_sprev = node;
2068 s->touching_thinglist = node;
2069 return(node);
2070 }
2071
2072
2073// P_DelSecnode() deletes a sector node from the list of
2074// sectors this object appears in. Returns a pointer to the next node
2075// on the linked list, or NULL.
2076
2077msecnode_t* P_DelSecnode(msecnode_t* node)
2078 {
2079 msecnode_t* tp; // prev node on thing thread
2080 msecnode_t* tn; // next node on thing thread
2081 msecnode_t* sp; // prev node on sector thread
2082 msecnode_t* sn; // next node on sector thread
2083
2084 if (node)
2085 {
2086
2087 // Unlink from the Thing thread. The Thing thread begins at
2088 // sector_list and not from mobj_t->touching_sectorlist.
2089
2090 tp = node->m_tprev;
2091 tn = node->m_tnext;
2092 if (tp)
2093 tp->m_tnext = tn;
2094 if (tn)
2095 tn->m_tprev = tp;
2096
2097 // Unlink from the sector thread. This thread begins at
2098 // sector_t->touching_thinglist.
2099
2100 sp = node->m_sprev;
2101 sn = node->m_snext;
2102 if (sp)
2103 sp->m_snext = sn;
2104 else
2105 node->m_sector->touching_thinglist = sn;
2106 if (sn)
2107 sn->m_sprev = sp;
2108
2109 // Return this node to the freelist
2110
2111 P_PutSecnode(node);
2112 return(tn);
2113 }
2114 return(NULL);
2115 } // phares 3/13/98
2116
2117// Delete an entire sector list
2118
2119void P_DelSeclist(msecnode_t* node)
2120
2121 {
2122 while (node)
2123 node = P_DelSecnode(node);
2124 }
2125
2126
2127// phares 3/14/98
2128//
2129// PIT_GetSectors
2130// Locates all the sectors the object is in by looking at the lines that
2131// cross through it. You have already decided that the object is allowed
2132// at this location, so don't bother with checking impassable or
2133// blocking lines.
2134
2135boolean PIT_GetSectors(line_t* ld)
2136 {
2137 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
2138 tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
2139 tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
2140 tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
2141 return true;
2142
2143 if (P_BoxOnLineSide(tmbbox, ld) != -1)
2144 return true;
2145
2146 // This line crosses through the object.
2147
2148 // Collect the sector(s) from the line and add to the
2149 // sector_list you're examining. If the Thing ends up being
2150 // allowed to move to this position, then the sector_list
2151 // will be attached to the Thing's mobj_t at touching_sectorlist.
2152
2153 sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list);
2154
2155 /* Don't assume all lines are 2-sided, since some Things
2156 * like MT_TFOG are allowed regardless of whether their radius takes
2157 * them beyond an impassable linedef.
2158 *
2159 * killough 3/27/98, 4/4/98:
2160 * Use sidedefs instead of 2s flag to determine two-sidedness.
2161 * killough 8/1/98: avoid duplicate if same sector on both sides
2162 * cph - DEMOSYNC? */
2163
2164 if (ld->backsector && ld->backsector != ld->frontsector)
2165 sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list);
2166
2167 return true;
2168 }
2169
2170
2171// phares 3/14/98
2172//
2173// P_CreateSecNodeList alters/creates the sector_list that shows what sectors
2174// the object resides in.
2175
2176void P_CreateSecNodeList(mobj_t* thing,fixed_t x,fixed_t y)
2177{
2178 int xl;
2179 int xh;
2180 int yl;
2181 int yh;
2182 int bx;
2183 int by;
2184 msecnode_t* node;
2185 mobj_t* saved_tmthing = tmthing; /* cph - see comment at func end */
2186 fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */
2187
2188 // First, clear out the existing m_thing fields. As each node is
2189 // added or verified as needed, m_thing will be set properly. When
2190 // finished, delete all nodes where m_thing is still NULL. These
2191 // represent the sectors the Thing has vacated.
2192
2193 node = sector_list;
2194 while (node)
2195 {
2196 node->m_thing = NULL;
2197 node = node->m_tnext;
2198 }
2199
2200 tmthing = thing;
2201
2202 tmx = x;
2203 tmy = y;
2204
2205 tmbbox[BOXTOP] = y + tmthing->radius;
2206 tmbbox[BOXBOTTOM] = y - tmthing->radius;
2207 tmbbox[BOXRIGHT] = x + tmthing->radius;
2208 tmbbox[BOXLEFT] = x - tmthing->radius;
2209
2210 validcount++; // used to make sure we only process a line once
2211
2212 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
2213 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
2214 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
2215 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
2216
2217 for (bx=xl ; bx<=xh ; bx++)
2218 for (by=yl ; by<=yh ; by++)
2219 P_BlockLinesIterator(bx,by,PIT_GetSectors);
2220
2221 // Add the sector of the (x,y) point to sector_list.
2222
2223 sector_list = P_AddSecnode(thing->subsector->sector,thing,sector_list);
2224
2225 // Now delete any nodes that won't be used. These are the ones where
2226 // m_thing is still NULL.
2227
2228 node = sector_list;
2229 while (node)
2230 {
2231 if (node->m_thing == NULL)
2232 {
2233 if (node == sector_list)
2234 sector_list = node->m_tnext;
2235 node = P_DelSecnode(node);
2236 }
2237 else
2238 node = node->m_tnext;
2239 }
2240
2241 /* cph -
2242 * This is the strife we get into for using global variables. tmthing
2243 * is being used by several different functions calling
2244 * P_BlockThingIterator, including functions that can be called *from*
2245 * P_BlockThingIterator. Using a global tmthing is not reentrant.
2246 * OTOH for Boom/MBF demos we have to preserve the buggy behavior.
2247 * Fun. We restore its previous value unless we're in a Boom/MBF demo.
2248 */
2249 if ((compatibility_level < boom_compatibility_compatibility) ||
2250 (compatibility_level >= prboom_3_compatibility))
2251 tmthing = saved_tmthing;
2252 /* And, duh, the same for tmx/y - cph 2002/09/22
2253 * And for tmbbox - cph 2003/08/10 */
2254 if ((compatibility_level < boom_compatibility_compatibility) /* ||
2255 (compatibility_level >= prboom_4_compatibility) */) {
2256 tmx = saved_tmx, tmy = saved_tmy;
2257 if (tmthing) {
2258 tmbbox[BOXTOP] = tmy + tmthing->radius;
2259 tmbbox[BOXBOTTOM] = tmy - tmthing->radius;
2260 tmbbox[BOXRIGHT] = tmx + tmthing->radius;
2261 tmbbox[BOXLEFT] = tmx - tmthing->radius;
2262 }
2263 }
2264}
2265
2266/* cphipps 2004/08/30 -
2267 * Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */
2268void P_MapStart(void) {
2269 if (tmthing) I_Error("P_MapStart: tmthing set!");
2270}
2271void P_MapEnd(void) {
2272 tmthing = NULL;
2273}
2274
2275// e6y
2276// Code to emulate the behavior of Vanilla Doom when encountering an overrun
2277// of the spechit array.
2278// No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc.
2279// http://www.doomworld.com/vb/showthread.php?s=&threadid=35214
2280static void SpechitOverrun(line_t *ld)
2281{
2282 //int addr = 0x01C09C98 + (ld - lines) * 0x3E;
2283 int addr = 0x00C09C98 + (ld - lines) * 0x3E;
2284
2285 if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility)
2286 {
2287 // e6y
2288 // There are no more desyncs in the following dosdoom demos:
2289 // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm
2290 // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip
2291 // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip
2292 // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip
2293
2294 switch(numspechit)
2295 {
2296 case 8: break; /* strange cph's code */
2297 case 9:
2298 tmfloorz = addr;
2299 break;
2300 case 10:
2301 tmceilingz = addr;
2302 break;
2303
2304 default:
2305 lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate"
2306 " an overrun where numspechit=%i\n",
2307 numspechit);
2308 break;
2309 }
2310 }
2311 else
2312 {
2313 switch(numspechit)
2314 {
2315 case 8: break; /* numspechit, not significant it seems - cph */
2316 case 9:
2317 case 10:
2318 case 11:
2319 case 12:
2320 tmbbox[numspechit-9] = addr;
2321 break;
2322 case 13:
2323 nofit = addr;
2324 break;
2325 case 14:
2326 crushchange = addr;
2327 break;
2328 default:
2329 lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate"
2330 " an overrun where numspechit=%i\n",
2331 numspechit);
2332 break;
2333 }
2334 }
2335}