FS#12076 - DB stats resurrection: If the filename was changed, require
[kugel-rb.git] / apps / plugins / doom / p_map.c
blob35194dec144385d0101834e3a6eba49fa79892ba
1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
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-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
27 * DESCRIPTION:
28 * Movement, collision handling.
29 * Shooting and aiming.
31 *-----------------------------------------------------------------------------*/
33 #include "doomstat.h"
34 #include "r_main.h"
35 #include "p_mobj.h"
36 #include "p_maputl.h"
37 #include "p_map.h"
38 #include "p_setup.h"
39 #include "p_spec.h"
40 #include "s_sound.h"
41 #include "sounds.h"
42 #include "p_inter.h"
43 #include "m_random.h"
44 #include "m_bbox.h"
45 #include "i_system.h"
46 #include "rockmacros.h"
48 static mobj_t *tmthing;
49 static fixed_t tmx;
50 static fixed_t tmy;
51 static int pe_x; // Pain Elemental position for Lost Soul checks // phares
52 static int pe_y; // Pain Elemental position for Lost Soul checks // phares
53 static int ls_x; // Lost Soul position for Lost Soul checks // phares
54 static int ls_y; // Lost Soul position for Lost Soul checks // phares
56 // If "floatok" true, move would be ok
57 // if within "tmfloorz - tmceilingz".
59 boolean floatok;
61 /* killough 11/98: if "felldown" true, object was pushed down ledge */
62 boolean felldown;
64 // The tm* items are used to hold information globally, usually for
65 // line or object intersection checking
67 fixed_t tmbbox[4]; // bounding box for line intersection checks
68 fixed_t tmfloorz; // floor you'd hit if free to fall
69 fixed_t tmceilingz; // ceiling of sector you're in
70 fixed_t tmdropoffz; // dropoff on other side of line you're crossing
72 // keep track of the line that lowers the ceiling,
73 // so missiles don't explode against sky hack walls
75 line_t *ceilingline;
76 line_t *blockline; /* killough 8/11/98: blocking linedef */
77 line_t *floorline; /* killough 8/1/98: Highest touched floor */
78 static int tmunstuck; /* killough 8/1/98: whether to allow unsticking */
80 // keep track of special lines as they are hit,
81 // but don't process them until the move is proven valid
83 // 1/11/98 killough: removed limit on special lines crossed
84 line_t **spechit; // new code -- killough
85 static int spechit_max; // killough
87 int numspechit;
89 // Temporary holder for thing_sectorlist threads
90 msecnode_t* sector_list = NULL; // phares 3/16/98
93 // TELEPORT MOVE
97 // PIT_StompThing
100 static boolean telefrag; /* killough 8/9/98: whether to telefrag at exit */
102 boolean PIT_StompThing (mobj_t* thing)
104 fixed_t blockdist;
106 // phares 9/10/98: moved this self-check to start of routine
108 // don't clip against self
110 if (thing == tmthing)
111 return true;
113 if (!(thing->flags & MF_SHOOTABLE)) // Can't shoot it? Can't stomp it!
114 return true;
116 blockdist = thing->radius + tmthing->radius;
118 if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist)
119 return true; // didn't hit it
121 // monsters don't stomp things except on boss level
122 if (!telefrag) // killough 8/9/98: make consistent across all levels
123 return false;
125 P_DamageMobj (thing, tmthing, tmthing, 10000); // Stomp!
127 return true;
132 * killough 8/28/98:
134 * P_GetFriction()
136 * Returns the friction associated with a particular mobj.
139 int P_GetFriction(const mobj_t *mo, int *frictionfactor)
141 int friction = ORIG_FRICTION;
142 int movefactor = ORIG_FRICTION_FACTOR;
143 const msecnode_t *m;
144 const sector_t *sec;
146 /* Assign the friction value to objects on the floor, non-floating,
147 * and clipped. Normally the object's friction value is kept at
148 * ORIG_FRICTION and this thinker changes it for icy or muddy floors.
150 * When the object is straddling sectors with the same
151 * floorheight that have different frictions, use the lowest
152 * friction value (muddy has precedence over icy).
155 if (!(mo->flags & (MF_NOCLIP|MF_NOGRAVITY))
156 && (mbf_features || (mo->player && !compatibility)) &&
157 variable_friction)
158 for (m = mo->touching_sectorlist; m; m = m->m_tnext)
159 if ((sec = m->m_sector)->special & FRICTION_MASK &&
160 (sec->friction < friction || friction == ORIG_FRICTION) &&
161 (mo->z <= sec->floorheight ||
162 (sec->heightsec != -1 &&
163 mo->z <= sectors[sec->heightsec].floorheight &&
164 mbf_features)))
165 friction = sec->friction, movefactor = sec->movefactor;
167 if (frictionfactor)
168 *frictionfactor = movefactor;
170 return friction;
173 /* phares 3/19/98
174 * P_GetMoveFactor() returns the value by which the x,y
175 * movements are multiplied to add to player movement.
177 * killough 8/28/98: rewritten
180 int P_GetMoveFactor(const mobj_t *mo, int *frictionp)
182 int movefactor, friction;
184 // If the floor is icy or muddy, it's harder to get moving. This is where
185 // the different friction factors are applied to 'trying to move'. In
186 // p_mobj.c, the friction factors are applied as you coast and slow down.
188 if ((friction = P_GetFriction(mo, &movefactor)) < ORIG_FRICTION)
190 // phares 3/11/98: you start off slowly, then increase as
191 // you get better footing
193 int momentum = P_AproxDistance(mo->momx,mo->momy);
195 if (momentum > MORE_FRICTION_MOMENTUM<<2)
196 movefactor <<= 3;
197 else if (momentum > MORE_FRICTION_MOMENTUM<<1)
198 movefactor <<= 2;
199 else if (momentum > MORE_FRICTION_MOMENTUM)
200 movefactor <<= 1;
203 if (frictionp)
204 *frictionp = friction;
206 return movefactor;
210 // P_TeleportMove
213 boolean P_TeleportMove (mobj_t* thing,fixed_t x,fixed_t y, boolean boss)
215 int xl;
216 int xh;
217 int yl;
218 int yh;
219 int bx;
220 int by;
222 subsector_t* newsubsec;
224 /* killough 8/9/98: make telefragging more consistent, preserve compatibility */
225 telefrag = thing->player ||
226 (!comp[comp_telefrag] ? boss : (gamemap==30));
228 // kill anything occupying the position
230 tmthing = thing;
232 tmx = x;
233 tmy = y;
235 tmbbox[BOXTOP] = y + tmthing->radius;
236 tmbbox[BOXBOTTOM] = y - tmthing->radius;
237 tmbbox[BOXRIGHT] = x + tmthing->radius;
238 tmbbox[BOXLEFT] = x - tmthing->radius;
240 newsubsec = R_PointInSubsector (x,y);
241 ceilingline = NULL;
243 // The base floor/ceiling is from the subsector
244 // that contains the point.
245 // Any contacted lines the step closer together
246 // will adjust them.
248 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
249 tmceilingz = newsubsec->sector->ceilingheight;
251 validcount++;
252 numspechit = 0;
254 // stomp on any things contacted
256 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
257 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
258 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
259 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
261 for (bx=xl ; bx<=xh ; bx++)
262 for (by=yl ; by<=yh ; by++)
263 if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
264 return false;
266 // the move is ok,
267 // so unlink from the old position & link into the new position
269 P_UnsetThingPosition (thing);
271 thing->floorz = tmfloorz;
272 thing->ceilingz = tmceilingz;
273 thing->dropoffz = tmdropoffz; // killough 11/98
275 thing->x = x;
276 thing->y = y;
278 P_SetThingPosition (thing);
280 return true;
285 // MOVEMENT ITERATOR FUNCTIONS
288 // // phares
289 // PIT_CrossLine // |
290 // Checks to see if a PE->LS trajectory line crosses a blocking // V
291 // line. Returns false if it does.
293 // tmbbox holds the bounding box of the trajectory. If that box
294 // does not touch the bounding box of the line in question,
295 // then the trajectory is not blocked. If the PE is on one side
296 // of the line and the LS is on the other side, then the
297 // trajectory is blocked.
299 // Currently this assumes an infinite line, which is not quite
300 // correct. A more correct solution would be to check for an
301 // intersection of the trajectory and the line, but that takes
302 // longer and probably really isn't worth the effort.
305 static // killough 3/26/98: make static
306 boolean PIT_CrossLine (line_t* ld)
308 if (!(ld->flags & ML_TWOSIDED) ||
309 (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS)))
310 if (!(tmbbox[BOXLEFT] > ld->bbox[BOXRIGHT] ||
311 tmbbox[BOXRIGHT] < ld->bbox[BOXLEFT] ||
312 tmbbox[BOXTOP] < ld->bbox[BOXBOTTOM] ||
313 tmbbox[BOXBOTTOM] > ld->bbox[BOXTOP]))
314 if (P_PointOnLineSide(pe_x,pe_y,ld) != P_PointOnLineSide(ls_x,ls_y,ld))
315 return(false); // line blocks trajectory // ^
316 return(true); // line doesn't block trajectory // |
317 } // phares
320 /* killough 8/1/98: used to test intersection between thing and line
321 * assuming NO movement occurs -- used to avoid sticky situations.
324 static int untouched(line_t *ld)
326 fixed_t x, y, tmbbox[4];
327 return
328 (tmbbox[BOXRIGHT] = (x=tmthing->x)+tmthing->radius) <= ld->bbox[BOXLEFT] ||
329 (tmbbox[BOXLEFT] = x-tmthing->radius) >= ld->bbox[BOXRIGHT] ||
330 (tmbbox[BOXTOP] = (y=tmthing->y)+tmthing->radius) <= ld->bbox[BOXBOTTOM] ||
331 (tmbbox[BOXBOTTOM] = y-tmthing->radius) >= ld->bbox[BOXTOP] ||
332 P_BoxOnLineSide(tmbbox, ld) != -1;
336 // PIT_CheckLine
337 // Adjusts tmfloorz and tmceilingz as lines are contacted
340 static // killough 3/26/98: make static
341 boolean PIT_CheckLine (line_t* ld)
343 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
344 || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
345 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
346 || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
347 return true; // didn't hit it
349 if (P_BoxOnLineSide(tmbbox, ld) != -1)
350 return true; // didn't hit it
352 // A line has been hit
354 // The moving thing's destination position will cross the given line.
355 // If this should not be allowed, return false.
356 // If the line is special, keep track of it
357 // to process later if the move is proven ok.
358 // NOTE: specials are NOT sorted by order,
359 // so two special lines that are only 8 pixels apart
360 // could be crossed in either order.
362 // killough 7/24/98: allow player to move out of 1s wall, to prevent sticking
363 if (!ld->backsector) // one sided line
365 blockline = ld;
366 return tmunstuck && !untouched(ld) &&
367 FixedMul(tmx-tmthing->x,ld->dy) > FixedMul(tmy-tmthing->y,ld->dx);
370 // killough 8/10/98: allow bouncing objects to pass through as missiles
371 if (!(tmthing->flags & (MF_MISSILE | MF_BOUNCES)))
373 if (ld->flags & ML_BLOCKING) // explicitly blocking everything
374 return tmunstuck && !untouched(ld); // killough 8/1/98: allow escape
376 // killough 8/9/98: monster-blockers don't affect friends
377 if (!(tmthing->flags & MF_FRIEND || tmthing->player)
378 && ld->flags & ML_BLOCKMONSTERS)
379 return false; // block monsters only
382 // set openrange, opentop, openbottom
383 // these define a 'window' from one sector to another across this line
385 P_LineOpening (ld);
387 // adjust floor & ceiling heights
389 if (opentop < tmceilingz)
391 tmceilingz = opentop;
392 ceilingline = ld;
393 blockline = ld;
396 if (openbottom > tmfloorz)
398 tmfloorz = openbottom;
399 floorline = ld; // killough 8/1/98: remember floor linedef
400 blockline = ld;
403 if (lowfloor < tmdropoffz)
404 tmdropoffz = lowfloor;
406 // if contacted a special line, add it to the list
408 if (ld->special)
410 // 1/11/98 killough: remove limit on lines hit, by array doubling
411 if (numspechit >= spechit_max)
413 spechit_max = spechit_max ? spechit_max*2 : 8;
414 spechit = realloc(spechit,sizeof *spechit*spechit_max); // killough
416 spechit[numspechit++] = ld;
419 return true;
423 // PIT_CheckThing
426 static boolean PIT_CheckThing(mobj_t *thing) // killough 3/26/98: make static
428 fixed_t blockdist;
429 int damage;
431 // killough 11/98: add touchy things
432 if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE|MF_TOUCHY)))
433 return true;
435 blockdist = thing->radius + tmthing->radius;
437 if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist)
438 return true; // didn't hit it
440 // killough 11/98:
442 // This test has less information content (it's almost always false), so it
443 // should not be moved up to first, as it adds more overhead than it removes.
445 // don't clip against self
447 if (thing == tmthing)
448 return true;
450 /* killough 11/98:
452 * TOUCHY flag, for mines or other objects which die on contact with solids.
453 * If a solid object of a different type comes in contact with a touchy
454 * thing, and the touchy thing is not the sole one moving relative to fixed
455 * surroundings such as walls, then the touchy thing dies immediately.
458 if (thing->flags & MF_TOUCHY && // touchy object
459 tmthing->flags & MF_SOLID && // solid object touches it
460 thing->health > 0 && // touchy object is alive
461 (thing->intflags & MIF_ARMED || // Thing is an armed mine
462 sentient(thing)) && // ... or a sentient thing
463 (thing->type != tmthing->type || // only different species
464 thing->type == MT_PLAYER) && // ... or different players
465 thing->z + thing->height >= tmthing->z && // touches vertically
466 tmthing->z + tmthing->height >= thing->z &&
467 (thing->type ^ MT_PAIN) | // PEs and lost souls
468 (tmthing->type ^ MT_SKULL) && // are considered same
469 (thing->type ^ MT_SKULL) | // (but Barons & Knights
470 (tmthing->type ^ MT_PAIN)) // are intentionally not)
472 P_DamageMobj(thing, NULL, NULL, thing->health); // kill object
473 return true;
476 // check for skulls slamming into things
478 if (tmthing->flags & MF_SKULLFLY)
480 // A flying skull is smacking something.
481 // Determine damage amount, and the skull comes to a dead stop.
483 int damage = ((P_Random(pr_skullfly)%8)+1)*tmthing->info->damage;
485 P_DamageMobj (thing, tmthing, tmthing, damage);
487 tmthing->flags &= ~MF_SKULLFLY;
488 tmthing->momx = tmthing->momy = tmthing->momz = 0;
490 P_SetMobjState (tmthing, tmthing->info->spawnstate);
492 return false; // stop moving
495 // missiles can hit other things
496 // killough 8/10/98: bouncing non-solid things can hit other things too
498 if (tmthing->flags & MF_MISSILE || (tmthing->flags & MF_BOUNCES &&
499 !(tmthing->flags & MF_SOLID)))
501 // see if it went over / under
503 if (tmthing->z > thing->z + thing->height)
504 return true; // overhead
506 if (tmthing->z+tmthing->height < thing->z)
507 return true; // underneath
509 if (tmthing->target && (tmthing->target->type == thing->type ||
510 (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
511 (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT)))
513 if (thing == tmthing->target)
514 return true; // Don't hit same species as originator.
515 else
516 if (thing->type != MT_PLAYER) // Explode, but do no damage.
517 return false; // Let players missile other players.
520 // killough 8/10/98: if moving thing is not a missile, no damage
521 // is inflicted, and momentum is reduced if object hit is solid.
523 if (!(tmthing->flags & MF_MISSILE)) {
524 if (!(thing->flags & MF_SOLID)) {
525 return true;
526 } else {
527 tmthing->momx = -tmthing->momx;
528 tmthing->momy = -tmthing->momy;
529 if (!(tmthing->flags & MF_NOGRAVITY))
531 tmthing->momx >>= 2;
532 tmthing->momy >>= 2;
534 return false;
538 if (!(thing->flags & MF_SHOOTABLE))
539 return !(thing->flags & MF_SOLID); // didn't do any damage
541 // damage / explode
543 damage = ((P_Random(pr_damage)%8)+1)*tmthing->info->damage;
544 P_DamageMobj (thing, tmthing, tmthing->target, damage);
546 // don't traverse any more
547 return false;
550 // check for special pickup
552 if (thing->flags & MF_SPECIAL)
554 uint_64_t solid = thing->flags & MF_SOLID;
555 if (tmthing->flags & MF_PICKUP)
556 P_TouchSpecialThing(thing, tmthing); // can remove thing
557 return !solid;
560 // killough 3/16/98: Allow non-solid moving objects to move through solid
561 // ones, by allowing the moving thing (tmthing) to move if it's non-solid,
562 // despite another solid thing being in the way.
563 // killough 4/11/98: Treat no-clipping things as not blocking
565 return !((thing->flags & MF_SOLID && !(thing->flags & MF_NOCLIP))
566 && (tmthing->flags & MF_SOLID || demo_compatibility));
568 // return !(thing->flags & MF_SOLID); // old code -- killough
571 // This routine checks for Lost Souls trying to be spawned // phares
572 // across 1-sided lines, impassible lines, or "monsters can't // |
573 // cross" lines. Draw an imaginary line between the PE // V
574 // and the new Lost Soul spawn spot. If that line crosses
575 // a 'blocking' line, then disallow the spawn. Only search
576 // lines in the blocks of the blockmap where the bounding box
577 // of the trajectory line resides. Then check bounding box
578 // of the trajectory vs. the bounding box of each blocking
579 // line to see if the trajectory and the blocking line cross.
580 // Then check the PE and LS to see if they're on different
581 // sides of the blocking line. If so, return true, otherwise
582 // false.
584 boolean Check_Sides(mobj_t* actor, int x, int y)
586 int bx,by,xl,xh,yl,yh;
588 pe_x = actor->x;
589 pe_y = actor->y;
590 ls_x = x;
591 ls_y = y;
593 // Here is the bounding box of the trajectory
595 tmbbox[BOXLEFT] = pe_x < x ? pe_x : x;
596 tmbbox[BOXRIGHT] = pe_x > x ? pe_x : x;
597 tmbbox[BOXTOP] = pe_y > y ? pe_y : y;
598 tmbbox[BOXBOTTOM] = pe_y < y ? pe_y : y;
600 // Determine which blocks to look in for blocking lines
602 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
603 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
604 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
605 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
607 // xl->xh, yl->yh determine the mapblock set to search
609 validcount++; // prevents checking same line twice
610 for (bx = xl ; bx <= xh ; bx++)
611 for (by = yl ; by <= yh ; by++)
612 if (!P_BlockLinesIterator(bx,by,PIT_CrossLine))
613 return true; // ^
614 return(false); // |
615 } // phares
618 // MOVEMENT CLIPPING
622 // P_CheckPosition
623 // This is purely informative, nothing is modified
624 // (except things picked up).
626 // in:
627 // a mobj_t (can be valid or invalid)
628 // a position to be checked
629 // (doesn't need to be related to the mobj_t->x,y)
631 // during:
632 // special things are touched if MF_PICKUP
633 // early out on solid lines?
635 // out:
636 // newsubsec
637 // floorz
638 // ceilingz
639 // tmdropoffz
640 // the lowest point contacted
641 // (monsters won't move to a dropoff)
642 // speciallines[]
643 // numspeciallines
646 boolean P_CheckPosition (mobj_t* thing,fixed_t x,fixed_t y)
648 int xl;
649 int xh;
650 int yl;
651 int yh;
652 int bx;
653 int by;
654 subsector_t* newsubsec;
656 tmthing = thing;
658 tmx = x;
659 tmy = y;
661 tmbbox[BOXTOP] = y + tmthing->radius;
662 tmbbox[BOXBOTTOM] = y - tmthing->radius;
663 tmbbox[BOXRIGHT] = x + tmthing->radius;
664 tmbbox[BOXLEFT] = x - tmthing->radius;
666 newsubsec = R_PointInSubsector (x,y);
667 floorline = blockline = ceilingline = NULL; // killough 8/1/98
669 // Whether object can get out of a sticky situation:
670 tmunstuck = thing->player && /* only players */
671 thing->player->mo == thing && /* not voodoo dolls */
672 mbf_features; /* not under old demos */
674 // The base floor / ceiling is from the subsector
675 // that contains the point.
676 // Any contacted lines the step closer together
677 // will adjust them.
679 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
680 tmceilingz = newsubsec->sector->ceilingheight;
681 validcount++;
682 numspechit = 0;
684 if ( tmthing->flags & MF_NOCLIP )
685 return true;
687 // Check things first, possibly picking things up.
688 // The bounding box is extended by MAXRADIUS
689 // because mobj_ts are grouped into mapblocks
690 // based on their origin point, and can overlap
691 // into adjacent blocks by up to MAXRADIUS units.
693 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
694 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
695 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
696 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
699 for (bx=xl ; bx<=xh ; bx++)
700 for (by=yl ; by<=yh ; by++)
701 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
702 return false;
704 // check lines
706 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
707 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
708 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
709 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
711 for (bx=xl ; bx<=xh ; bx++)
712 for (by=yl ; by<=yh ; by++)
713 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
714 return false; // doesn't fit
716 return true;
721 // P_TryMove
722 // Attempt to move to a new position,
723 // crossing special lines unless MF_TELEPORT is set.
725 boolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y,
726 boolean dropoff) // killough 3/15/98: allow dropoff as option
728 fixed_t oldx;
729 fixed_t oldy;
731 felldown = floatok = false; // killough 11/98
733 if (!P_CheckPosition (thing, x, y))
734 return false; // solid wall or thing
736 if ( !(thing->flags & MF_NOCLIP) )
738 // killough 7/26/98: reformatted slightly
739 // killough 8/1/98: Possibly allow escape if otherwise stuck
741 if (tmceilingz - tmfloorz < thing->height || // doesn't fit
742 // mobj must lower to fit
743 (floatok = true, !(thing->flags & MF_TELEPORT) &&
744 tmceilingz - thing->z < thing->height) ||
745 // too big a step up
746 (!(thing->flags & MF_TELEPORT) &&
747 tmfloorz - thing->z > 24*FRACUNIT))
748 return tmunstuck
749 && !(ceilingline && untouched(ceilingline))
750 && !( floorline && untouched( floorline));
752 /* killough 3/15/98: Allow certain objects to drop off
753 * killough 7/24/98, 8/1/98:
754 * Prevent monsters from getting stuck hanging off ledges
755 * killough 10/98: Allow dropoffs in controlled circumstances
756 * killough 11/98: Improve symmetry of clipping on stairs
759 if (!(thing->flags & (MF_DROPOFF|MF_FLOAT))) {
760 if (comp[comp_dropoff])
762 if ((compatibility || !dropoff) && (tmfloorz - tmdropoffz > 24*FRACUNIT))
763 return false; // don't stand over a dropoff
765 else
766 if (!dropoff || (dropoff==2 && // large jump down (e.g. dogs)
767 (tmfloorz-tmdropoffz > 128*FRACUNIT ||
768 !thing->target || thing->target->z >tmdropoffz)))
770 if (!monkeys || !mbf_features ?
771 tmfloorz - tmdropoffz > 24*FRACUNIT :
772 thing->floorz - tmfloorz > 24*FRACUNIT ||
773 thing->dropoffz - tmdropoffz > 24*FRACUNIT)
774 return false;
776 else { /* dropoff allowed -- check for whether it fell more than 24 */
777 felldown = !(thing->flags & MF_NOGRAVITY) &&
778 thing->z - tmfloorz > 24*FRACUNIT;
782 if (thing->flags & MF_BOUNCES && // killough 8/13/98
783 !(thing->flags & (MF_MISSILE|MF_NOGRAVITY)) &&
784 !sentient(thing) && tmfloorz - thing->z > 16*FRACUNIT)
785 return false; // too big a step up for bouncers under gravity
787 // killough 11/98: prevent falling objects from going up too many steps
788 if (thing->intflags & MIF_FALLING && tmfloorz - thing->z >
789 FixedMul(thing->momx,thing->momx)+FixedMul(thing->momy,thing->momy))
790 return false;
793 // the move is ok,
794 // so unlink from the old position and link into the new position
796 P_UnsetThingPosition (thing);
798 oldx = thing->x;
799 oldy = thing->y;
800 thing->floorz = tmfloorz;
801 thing->ceilingz = tmceilingz;
802 thing->dropoffz = tmdropoffz; // killough 11/98: keep track of dropoffs
803 thing->x = x;
804 thing->y = y;
806 P_SetThingPosition (thing);
808 // if any special lines were hit, do the effect
810 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
811 while (numspechit--)
812 if (spechit[numspechit]->special) // see if the line was crossed
814 int oldside;
815 if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) !=
816 P_PointOnLineSide(thing->x, thing->y, spechit[numspechit]))
817 P_CrossSpecialLine(spechit[numspechit], oldside, thing);
820 return true;
824 * killough 9/12/98:
826 * Apply "torque" to objects hanging off of ledges, so that they
827 * fall off. It's not really torque, since Doom has no concept of
828 * rotation, but it's a convincing effect which avoids anomalies
829 * such as lifeless objects hanging more than halfway off of ledges,
830 * and allows objects to roll off of the edges of moving lifts, or
831 * to slide up and then back down stairs, or to fall into a ditch.
832 * If more than one linedef is contacted, the effects are cumulative,
833 * so balancing is possible.
836 static boolean PIT_ApplyTorque(line_t *ld)
838 if (ld->backsector && // If thing touches two-sided pivot linedef
839 tmbbox[BOXRIGHT] > ld->bbox[BOXLEFT] &&
840 tmbbox[BOXLEFT] < ld->bbox[BOXRIGHT] &&
841 tmbbox[BOXTOP] > ld->bbox[BOXBOTTOM] &&
842 tmbbox[BOXBOTTOM] < ld->bbox[BOXTOP] &&
843 P_BoxOnLineSide(tmbbox, ld) == -1)
845 mobj_t *mo = tmthing;
847 fixed_t dist = // lever arm
848 + (ld->dx >> FRACBITS) * (mo->y >> FRACBITS)
849 - (ld->dy >> FRACBITS) * (mo->x >> FRACBITS)
850 - (ld->dx >> FRACBITS) * (ld->v1->y >> FRACBITS)
851 + (ld->dy >> FRACBITS) * (ld->v1->x >> FRACBITS);
853 if (dist < 0 ? // dropoff direction
854 ld->frontsector->floorheight < mo->z &&
855 ld->backsector->floorheight >= mo->z :
856 ld->backsector->floorheight < mo->z &&
857 ld->frontsector->floorheight >= mo->z)
859 /* At this point, we know that the object straddles a two-sided
860 * linedef, and that the object's center of mass is above-ground.
863 fixed_t x = D_abs(ld->dx), y = D_abs(ld->dy);
865 if (y > x)
867 fixed_t t = x;
868 x = y;
869 y = t;
872 y = finesine[(tantoangle[FixedDiv(y,x)>>DBITS] +
873 ANG90) >> ANGLETOFINESHIFT];
875 /* Momentum is proportional to distance between the
876 * object's center of mass and the pivot linedef.
878 * It is scaled by 2^(OVERDRIVE - gear). When gear is
879 * increased, the momentum gradually decreases to 0 for
880 * the same amount of pseudotorque, so that oscillations
881 * are prevented, yet it has a chance to reach equilibrium.
883 dist = FixedDiv(FixedMul(dist, (mo->gear < OVERDRIVE) ?
884 y << -(mo->gear - OVERDRIVE) :
885 y >> +(mo->gear - OVERDRIVE)), x);
887 /* Apply momentum away from the pivot linedef. */
889 x = FixedMul(ld->dy, dist);
890 y = FixedMul(ld->dx, dist);
892 /* Avoid moving too fast all of a sudden (step into "overdrive") */
894 dist = FixedMul(x,x) + FixedMul(y,y);
896 while (dist > FRACUNIT*4 && mo->gear < MAXGEAR)
897 ++mo->gear, x >>= 1, y >>= 1, dist >>= 1;
899 mo->momx -= x;
900 mo->momy += y;
903 return true;
907 * killough 9/12/98
909 * Applies "torque" to objects, based on all contacted linedefs
912 void P_ApplyTorque(mobj_t *mo)
914 int xl = ((tmbbox[BOXLEFT] =
915 mo->x - mo->radius) - bmaporgx) >> MAPBLOCKSHIFT;
916 int xh = ((tmbbox[BOXRIGHT] =
917 mo->x + mo->radius) - bmaporgx) >> MAPBLOCKSHIFT;
918 int yl = ((tmbbox[BOXBOTTOM] =
919 mo->y - mo->radius) - bmaporgy) >> MAPBLOCKSHIFT;
920 int yh = ((tmbbox[BOXTOP] =
921 mo->y + mo->radius) - bmaporgy) >> MAPBLOCKSHIFT;
922 int bx,by,flags = mo->intflags; //Remember the current state, for gear-change
924 tmthing = mo;
925 validcount++; /* prevents checking same line twice */
927 for (bx = xl ; bx <= xh ; bx++)
928 for (by = yl ; by <= yh ; by++)
929 P_BlockLinesIterator(bx, by, PIT_ApplyTorque);
931 /* If any momentum, mark object as 'falling' using engine-internal flags */
932 if (mo->momx | mo->momy)
933 mo->intflags |= MIF_FALLING;
934 else // Clear the engine-internal flag indicating falling object.
935 mo->intflags &= ~MIF_FALLING;
937 /* If the object has been moving, step up the gear.
938 * This helps reach equilibrium and avoid oscillations.
940 * Doom has no concept of potential energy, much less
941 * of rotation, so we have to creatively simulate these
942 * systems somehow :)
945 if (!((mo->intflags | flags) & MIF_FALLING)) // If not falling for a while,
946 mo->gear = 0; // Reset it to full strength
947 else
948 if (mo->gear < MAXGEAR) // Else if not at max gear,
949 mo->gear++; // move up a gear
953 // P_ThingHeightClip
954 // Takes a valid thing and adjusts the thing->floorz,
955 // thing->ceilingz, and possibly thing->z.
956 // This is called for all nearby monsters
957 // whenever a sector changes height.
958 // If the thing doesn't fit,
959 // the z will be set to the lowest value
960 // and false will be returned.
963 boolean P_ThingHeightClip (mobj_t* thing)
965 boolean onfloor;
967 onfloor = (thing->z == thing->floorz);
969 P_CheckPosition (thing, thing->x, thing->y);
971 /* what about stranding a monster partially off an edge?
972 * killough 11/98: Answer: see below (upset balance if hanging off ledge)
975 thing->floorz = tmfloorz;
976 thing->ceilingz = tmceilingz;
977 thing->dropoffz = tmdropoffz; /* killough 11/98: remember dropoffs */
979 if (onfloor)
982 // walking monsters rise and fall with the floor
984 thing->z = thing->floorz;
986 /* killough 11/98: Possibly upset balance of objects hanging off ledges */
987 if (thing->intflags & MIF_FALLING && thing->gear >= MAXGEAR)
988 thing->gear = 0;
990 else
993 // don't adjust a floating monster unless forced to
995 if (thing->z+thing->height > thing->ceilingz)
996 thing->z = thing->ceilingz - thing->height;
999 return thing->ceilingz - thing->floorz >= thing->height;
1004 // SLIDE MOVE
1005 // Allows the player to slide along any angled walls.
1008 /* killough 8/2/98: make variables static */
1009 static fixed_t bestslidefrac;
1010 static fixed_t secondslidefrac;
1011 static line_t* bestslideline;
1012 static line_t* secondslideline;
1013 static mobj_t* slidemo;
1014 static fixed_t tmxmove;
1015 static fixed_t tmymove;
1019 // P_HitSlideLine
1020 // Adjusts the xmove / ymove
1021 // so that the next move will slide along the wall.
1022 // If the floor is icy, then you can bounce off a wall. // phares
1025 void P_HitSlideLine (line_t* ld)
1027 int side;
1028 angle_t lineangle;
1029 angle_t moveangle;
1030 angle_t deltaangle;
1031 fixed_t movelen;
1032 fixed_t newlen;
1033 boolean icyfloor; // is floor icy? // phares
1034 // |
1035 // Under icy conditions, if the angle of approach to the wall // V
1036 // is more than 45 degrees, then you'll bounce and lose half
1037 // your momentum. If less than 45 degrees, you'll slide along
1038 // the wall. 45 is arbitrary and is believable.
1040 // Check for the special cases of horz or vert walls.
1042 /* killough 10/98: only bounce if hit hard (prevents wobbling)
1043 * cph - DEMOSYNC - should only affect players in Boom demos? */
1044 icyfloor =
1045 (mbf_features ?
1046 P_AproxDistance(tmxmove, tmymove) > 4*FRACUNIT : !compatibility) &&
1047 variable_friction && // killough 8/28/98: calc friction on demand
1048 slidemo->z <= slidemo->floorz &&
1049 P_GetFriction(slidemo, NULL) > ORIG_FRICTION;
1051 if (ld->slopetype == ST_HORIZONTAL)
1053 if (icyfloor && (D_abs(tmymove) > D_abs(tmxmove)))
1055 tmxmove /= 2; // absorb half the momentum
1056 tmymove = -tmymove/2;
1057 S_StartSound(slidemo,sfx_oof); // oooff!
1059 else
1060 tmymove = 0; // no more movement in the Y direction
1061 return;
1064 if (ld->slopetype == ST_VERTICAL)
1066 if (icyfloor && (D_abs(tmxmove) > D_abs(tmymove)))
1068 tmxmove = -tmxmove/2; // absorb half the momentum
1069 tmymove /= 2;
1070 S_StartSound(slidemo,sfx_oof); // oooff! // ^
1071 } // |
1072 else // phares
1073 tmxmove = 0; // no more movement in the X direction
1074 return;
1077 // The wall is angled. Bounce if the angle of approach is // phares
1078 // less than 45 degrees. // phares
1080 side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
1082 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
1083 if (side == 1)
1084 lineangle += ANG180;
1085 moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
1087 // killough 3/2/98:
1088 // The moveangle+=10 breaks v1.9 demo compatibility in
1089 // some demos, so it needs demo_compatibility switch.
1091 if (!demo_compatibility)
1092 moveangle += 10; // prevents sudden path reversal due to // phares
1093 // rounding error // |
1094 deltaangle = moveangle-lineangle; // V
1095 movelen = P_AproxDistance (tmxmove, tmymove);
1096 if (icyfloor && (deltaangle > ANG45) && (deltaangle < ANG90+ANG45))
1098 moveangle = lineangle - deltaangle;
1099 movelen /= 2; // absorb
1100 S_StartSound(slidemo,sfx_oof); // oooff!
1101 moveangle >>= ANGLETOFINESHIFT;
1102 tmxmove = FixedMul (movelen, finecosine[moveangle]);
1103 tmymove = FixedMul (movelen, finesine[moveangle]);
1104 } // ^
1105 else // |
1106 { // phares
1107 if (deltaangle > ANG180)
1108 deltaangle += ANG180;
1110 // I_Error ("SlideLine: ang>ANG180");
1112 lineangle >>= ANGLETOFINESHIFT;
1113 deltaangle >>= ANGLETOFINESHIFT;
1114 newlen = FixedMul (movelen, finecosine[deltaangle]);
1115 tmxmove = FixedMul (newlen, finecosine[lineangle]);
1116 tmymove = FixedMul (newlen, finesine[lineangle]);
1117 } // phares
1122 // PTR_SlideTraverse
1125 boolean PTR_SlideTraverse (intercept_t* in)
1127 line_t* li;
1129 if (!in->isaline)
1130 I_Error ("PTR_SlideTraverse: not a line?");
1132 li = in->d.line;
1134 if ( ! (li->flags & ML_TWOSIDED) )
1136 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1137 return true; // don't hit the back side
1138 goto isblocking;
1141 // set openrange, opentop, openbottom.
1142 // These define a 'window' from one sector to another across a line
1144 P_LineOpening (li);
1146 if (openrange < slidemo->height)
1147 goto isblocking; // doesn't fit
1149 if (opentop - slidemo->z < slidemo->height)
1150 goto isblocking; // mobj is too high
1152 if (openbottom - slidemo->z > 24*FRACUNIT )
1153 goto isblocking; // too big a step up
1155 // this line doesn't block movement
1157 return true;
1159 // the line does block movement,
1160 // see if it is closer than best so far
1162 isblocking:
1164 if (in->frac < bestslidefrac)
1166 secondslidefrac = bestslidefrac;
1167 secondslideline = bestslideline;
1168 bestslidefrac = in->frac;
1169 bestslideline = li;
1172 return false; // stop
1177 // P_SlideMove
1178 // The momx / momy move is bad, so try to slide
1179 // along a wall.
1180 // Find the first line hit, move flush to it,
1181 // and slide along it
1183 // This is a kludgy mess.
1185 // killough 11/98: reformatted
1187 void P_SlideMove(mobj_t *mo)
1189 int hitcount = 3;
1191 slidemo = mo; // the object that's sliding
1195 fixed_t leadx, leady, trailx, traily;
1197 if (!--hitcount)
1198 goto stairstep; // don't loop forever
1200 // trace along the three leading corners
1202 if (mo->momx > 0)
1203 leadx = mo->x + mo->radius, trailx = mo->x - mo->radius;
1204 else
1205 leadx = mo->x - mo->radius, trailx = mo->x + mo->radius;
1207 if (mo->momy > 0)
1208 leady = mo->y + mo->radius, traily = mo->y - mo->radius;
1209 else
1210 leady = mo->y - mo->radius, traily = mo->y + mo->radius;
1212 bestslidefrac = FRACUNIT+1;
1214 P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy,
1215 PT_ADDLINES, PTR_SlideTraverse);
1216 P_PathTraverse(trailx, leady, trailx+mo->momx, leady+mo->momy,
1217 PT_ADDLINES, PTR_SlideTraverse);
1218 P_PathTraverse(leadx, traily, leadx+mo->momx, traily+mo->momy,
1219 PT_ADDLINES, PTR_SlideTraverse);
1221 // move up to the wall
1223 if (bestslidefrac == FRACUNIT+1)
1225 // the move must have hit the middle, so stairstep
1227 stairstep:
1229 /* killough 3/15/98: Allow objects to drop off ledges
1231 * phares 5/4/98: kill momentum if you can't move at all
1232 * This eliminates player bobbing if pressed against a wall
1233 * while on ice.
1235 * killough 10/98: keep buggy code around for old Boom demos
1237 * cph 2000/09//23: buggy code was only in Boom v2.01
1240 if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true))
1241 if (!P_TryMove(mo, mo->x + mo->momx, mo->y, true))
1242 if (compatibility_level == boom_201_compatibility)
1243 mo->momx = mo->momy = 0;
1245 break;
1248 // fudge a bit to make sure it doesn't hit
1250 if ((bestslidefrac -= 0x800) > 0)
1252 fixed_t newx = FixedMul(mo->momx, bestslidefrac);
1253 fixed_t newy = FixedMul(mo->momy, bestslidefrac);
1255 // killough 3/15/98: Allow objects to drop off ledges
1257 if (!P_TryMove(mo, mo->x+newx, mo->y+newy, true))
1258 goto stairstep;
1261 // Now continue along the wall.
1262 // First calculate remainder.
1264 bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
1266 if (bestslidefrac > FRACUNIT)
1267 bestslidefrac = FRACUNIT;
1269 if (bestslidefrac <= 0)
1270 break;
1272 tmxmove = FixedMul(mo->momx, bestslidefrac);
1273 tmymove = FixedMul(mo->momy, bestslidefrac);
1275 P_HitSlideLine(bestslideline); // clip the moves
1277 mo->momx = tmxmove;
1278 mo->momy = tmymove;
1280 /* killough 10/98: affect the bobbing the same way (but not voodoo dolls)
1281 * cph - DEMOSYNC? */
1282 if (mo->player && mo->player->mo == mo)
1284 if (D_abs(mo->player->momx) > D_abs(tmxmove))
1285 mo->player->momx = tmxmove;
1286 if (D_abs(mo->player->momy) > D_abs(tmymove))
1287 mo->player->momy = tmymove;
1289 } // killough 3/15/98: Allow objects to drop off ledges:
1290 while (!P_TryMove(mo, mo->x+tmxmove, mo->y+tmymove, true));
1294 // P_LineAttack
1296 mobj_t* linetarget; // who got hit (or NULL)
1297 static mobj_t* shootthing;
1299 /* killough 8/2/98: for more intelligent autoaiming */
1300 static uint_64_t aim_flags_mask;
1302 // Height if not aiming up or down
1303 fixed_t shootz;
1305 int la_damage;
1306 fixed_t attackrange;
1308 static fixed_t aimslope;
1310 // slopes to top and bottom of target
1311 // killough 4/20/98: make static instead of using ones in p_sight.c
1313 static fixed_t topslope;
1314 static fixed_t bottomslope;
1318 // PTR_AimTraverse
1319 // Sets linetaget and aimslope when a target is aimed at.
1321 boolean PTR_AimTraverse (intercept_t* in)
1323 line_t* li;
1324 mobj_t* th;
1325 fixed_t slope;
1326 fixed_t thingtopslope;
1327 fixed_t thingbottomslope;
1328 fixed_t dist;
1330 if (in->isaline)
1332 li = in->d.line;
1334 if ( !(li->flags & ML_TWOSIDED) )
1335 return false; // stop
1337 // Crosses a two sided line.
1338 // A two sided line will restrict
1339 // the possible target ranges.
1341 P_LineOpening (li);
1343 if (openbottom >= opentop)
1344 return false; // stop
1346 dist = FixedMul (attackrange, in->frac);
1348 if (li->frontsector->floorheight != li->backsector->floorheight)
1350 slope = FixedDiv (openbottom - shootz , dist);
1351 if (slope > bottomslope)
1352 bottomslope = slope;
1355 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1357 slope = FixedDiv (opentop - shootz , dist);
1358 if (slope < topslope)
1359 topslope = slope;
1362 if (topslope <= bottomslope)
1363 return false; // stop
1365 return true; // shot continues
1368 // shoot a thing
1370 th = in->d.thing;
1371 if (th == shootthing)
1372 return true; // can't shoot self
1374 if (!(th->flags&MF_SHOOTABLE))
1375 return true; // corpse or something
1377 /* killough 7/19/98, 8/2/98:
1378 * friends don't aim at friends (except players), at least not first
1380 if (th->flags & shootthing->flags & aim_flags_mask && !th->player)
1381 return true;
1383 // check angles to see if the thing can be aimed at
1385 dist = FixedMul (attackrange, in->frac);
1386 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1388 if (thingtopslope < bottomslope)
1389 return true; // shot over the thing
1391 thingbottomslope = FixedDiv (th->z - shootz, dist);
1393 if (thingbottomslope > topslope)
1394 return true; // shot under the thing
1396 // this thing can be hit!
1398 if (thingtopslope > topslope)
1399 thingtopslope = topslope;
1401 if (thingbottomslope < bottomslope)
1402 thingbottomslope = bottomslope;
1404 aimslope = (thingtopslope+thingbottomslope)/2;
1405 linetarget = th;
1407 return false; // don't go any farther
1412 // PTR_ShootTraverse
1414 boolean PTR_ShootTraverse (intercept_t* in)
1416 fixed_t x;
1417 fixed_t y;
1418 fixed_t z;
1419 fixed_t frac;
1421 mobj_t* th;
1423 fixed_t slope;
1424 fixed_t dist;
1425 fixed_t thingtopslope;
1426 fixed_t thingbottomslope;
1428 if (in->isaline)
1430 line_t *li = in->d.line;
1432 if (li->special)
1433 P_ShootSpecialLine (shootthing, li);
1435 if (li->flags & ML_TWOSIDED)
1436 { // crosses a two sided (really 2s) line
1437 P_LineOpening (li);
1438 dist = FixedMul(attackrange, in->frac);
1440 // killough 11/98: simplify
1442 if ((li->frontsector->floorheight==li->backsector->floorheight ||
1443 (slope = FixedDiv(openbottom - shootz , dist)) <= aimslope) &&
1444 (li->frontsector->ceilingheight==li->backsector->ceilingheight ||
1445 (slope = FixedDiv (opentop - shootz , dist)) >= aimslope))
1446 return true; // shot continues
1449 // hit line
1450 // position a bit closer
1452 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1453 x = trace.x + FixedMul (trace.dx, frac);
1454 y = trace.y + FixedMul (trace.dy, frac);
1455 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1457 if (li->frontsector->ceilingpic == skyflatnum)
1459 // don't shoot the sky!
1461 if (z > li->frontsector->ceilingheight)
1462 return false;
1464 // it's a sky hack wall
1466 if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1468 // fix bullet-eaters -- killough:
1469 // WARNING: Almost all demos will lose sync without this
1470 // demo_compatibility flag check!!! killough 1/18/98
1471 if (demo_compatibility || li->backsector->ceilingheight < z)
1472 return false;
1475 // Spawn bullet puffs.
1477 P_SpawnPuff (x,y,z);
1479 // don't go any farther
1481 return false;
1484 // shoot a thing
1486 th = in->d.thing;
1487 if (th == shootthing)
1488 return true; // can't shoot self
1490 if (!(th->flags&MF_SHOOTABLE))
1491 return true; // corpse or something
1493 // check angles to see if the thing can be aimed at
1495 dist = FixedMul (attackrange, in->frac);
1496 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1498 if (thingtopslope < aimslope)
1499 return true; // shot over the thing
1501 thingbottomslope = FixedDiv (th->z - shootz, dist);
1503 if (thingbottomslope > aimslope)
1504 return true; // shot under the thing
1506 // hit thing
1507 // position a bit closer
1509 frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1511 x = trace.x + FixedMul (trace.dx, frac);
1512 y = trace.y + FixedMul (trace.dy, frac);
1513 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1515 // Spawn bullet puffs or blod spots,
1516 // depending on target type.
1517 if (in->d.thing->flags & MF_NOBLOOD)
1518 P_SpawnPuff (x,y,z);
1519 else
1520 P_SpawnBlood (x,y,z, la_damage);
1522 if (la_damage)
1523 P_DamageMobj (th, shootthing, shootthing, la_damage);
1525 // don't go any farther
1526 return false;
1531 // P_AimLineAttack
1533 fixed_t P_AimLineAttack(mobj_t* t1,angle_t angle,fixed_t distance, uint_64_t mask)
1535 fixed_t x2;
1536 fixed_t y2;
1538 angle >>= ANGLETOFINESHIFT;
1539 shootthing = t1;
1541 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1542 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1543 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1545 // can't shoot outside view angles
1547 topslope = 100*FRACUNIT/160;
1548 bottomslope = -100*FRACUNIT/160;
1550 attackrange = distance;
1551 linetarget = NULL;
1553 /* killough 8/2/98: prevent friends from aiming at friends */
1554 aim_flags_mask = mask;
1556 P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_AimTraverse);
1558 if (linetarget)
1559 return aimslope;
1561 return 0;
1566 // P_LineAttack
1567 // If damage == 0, it is just a test trace
1568 // that will leave linetarget set.
1571 void P_LineAttack
1572 (mobj_t* t1,
1573 angle_t angle,
1574 fixed_t distance,
1575 fixed_t slope,
1576 int damage)
1578 fixed_t x2;
1579 fixed_t y2;
1581 angle >>= ANGLETOFINESHIFT;
1582 shootthing = t1;
1583 la_damage = damage;
1584 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1585 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1586 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1587 attackrange = distance;
1588 aimslope = slope;
1590 P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_ShootTraverse);
1595 // USE LINES
1598 mobj_t* usething;
1600 boolean PTR_UseTraverse (intercept_t* in)
1602 int side;
1604 if (!in->d.line->special)
1606 P_LineOpening (in->d.line);
1607 if (openrange <= 0)
1609 S_StartSound (usething, sfx_noway);
1611 // can't use through a wall
1612 return false;
1615 // not a special line, but keep checking
1617 return true;
1620 side = 0;
1621 if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1622 side = 1;
1624 // return false; // don't use back side
1626 P_UseSpecialLine (usething, in->d.line, side);
1628 //WAS can't use for than one special line in a row
1629 //jff 3/21/98 NOW multiple use allowed with enabling line flag
1631 return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))?
1632 true : false;
1635 // Returns false if a "oof" sound should be made because of a blocking
1636 // linedef. Makes 2s middles which are impassable, as well as 2s uppers
1637 // and lowers which block the player, cause the sound effect when the
1638 // player tries to activate them. Specials are excluded, although it is
1639 // assumed that all special linedefs within reach have been considered
1640 // and rejected already (see P_UseLines).
1642 // by Lee Killough
1645 boolean PTR_NoWayTraverse(intercept_t* in)
1647 line_t *ld = in->d.line;
1648 // This linedef
1649 return ld->special || !( // Ignore specials
1650 ld->flags & ML_BLOCKING || ( // Always blocking
1651 P_LineOpening(ld), // Find openings
1652 openrange <= 0 || // No opening
1653 openbottom > usething->z+24*FRACUNIT || // Too high it blocks
1654 opentop < usething->z+usething->height // Too low it blocks
1660 // P_UseLines
1661 // Looks for special lines in front of the player to activate.
1663 void P_UseLines (player_t* player)
1665 int angle;
1666 fixed_t x1;
1667 fixed_t y1;
1668 fixed_t x2;
1669 fixed_t y2;
1671 usething = player->mo;
1673 angle = player->mo->angle >> ANGLETOFINESHIFT;
1675 x1 = player->mo->x;
1676 y1 = player->mo->y;
1677 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1678 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1680 // old code:
1682 // P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1684 // This added test makes the "oof" sound work on 2s lines -- killough:
1686 if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ))
1687 if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse ))
1688 S_StartSound (usething, sfx_noway);
1693 // RADIUS ATTACK
1696 static mobj_t *bombsource, *bombspot;
1697 static int bombdamage;
1701 // PIT_RadiusAttack
1702 // "bombsource" is the creature
1703 // that caused the explosion at "bombspot".
1706 boolean PIT_RadiusAttack (mobj_t* thing)
1708 fixed_t dx;
1709 fixed_t dy;
1710 fixed_t dist;
1712 /* killough 8/20/98: allow bouncers to take damage
1713 * (missile bouncers are already excluded with MF_NOBLOCKMAP)
1716 if (!(thing->flags & (MF_SHOOTABLE | MF_BOUNCES)))
1717 return true;
1719 // Boss spider and cyborg
1720 // take no damage from concussion.
1722 // killough 8/10/98: allow grenades to hurt anyone, unless
1723 // fired by Cyberdemons, in which case it won't hurt Cybers.
1725 if (bombspot->flags & MF_BOUNCES ?
1726 thing->type == MT_CYBORG && bombsource->type == MT_CYBORG :
1727 thing->type == MT_CYBORG || thing->type == MT_SPIDER)
1728 return true;
1730 dx = D_abs(thing->x - bombspot->x);
1731 dy = D_abs(thing->y - bombspot->y);
1733 dist = dx>dy ? dx : dy;
1734 dist = (dist - thing->radius) >> FRACBITS;
1736 if (dist < 0)
1737 dist = 0;
1739 if (dist >= bombdamage)
1740 return true; // out of range
1742 if ( P_CheckSight (thing, bombspot) )
1744 // must be in direct path
1745 P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1748 return true;
1753 // P_RadiusAttack
1754 // Source is the creature that caused the explosion at spot.
1756 void P_RadiusAttack(mobj_t* spot,mobj_t* source,int damage)
1758 int x;
1759 int y;
1761 int xl;
1762 int xh;
1763 int yl;
1764 int yh;
1766 fixed_t dist;
1768 dist = (damage+MAXRADIUS)<<FRACBITS;
1769 yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1770 yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1771 xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1772 xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1773 bombspot = spot;
1774 bombsource = source;
1775 bombdamage = damage;
1777 for (y=yl ; y<=yh ; y++)
1778 for (x=xl ; x<=xh ; x++)
1779 P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1785 // SECTOR HEIGHT CHANGING
1786 // After modifying a sectors floor or ceiling height,
1787 // call this routine to adjust the positions
1788 // of all things that touch the sector.
1790 // If anything doesn't fit anymore, true will be returned.
1791 // If crunch is true, they will take damage
1792 // as they are being crushed.
1793 // If Crunch is false, you should set the sector height back
1794 // the way it was and call P_ChangeSector again
1795 // to undo the changes.
1798 static boolean crushchange, nofit;
1801 // PIT_ChangeSector
1804 boolean PIT_ChangeSector (mobj_t* thing)
1806 mobj_t* mo;
1808 if (P_ThingHeightClip (thing))
1809 return true; // keep checking
1811 // crunch bodies to giblets
1813 if (thing->health <= 0)
1815 P_SetMobjState (thing, S_GIBS);
1817 thing->flags &= ~MF_SOLID;
1818 thing->height = 0;
1819 thing->radius = 0;
1820 return true; // keep checking
1823 // crunch dropped items
1825 if (thing->flags & MF_DROPPED)
1827 P_RemoveMobj (thing);
1829 // keep checking
1830 return true;
1833 /* killough 11/98: kill touchy things immediately */
1834 if (thing->flags & MF_TOUCHY &&
1835 (thing->intflags & MIF_ARMED || sentient(thing)))
1837 P_DamageMobj(thing, NULL, NULL, thing->health); // kill object
1838 return true; // keep checking
1841 if (! (thing->flags & MF_SHOOTABLE) )
1843 // assume it is bloody gibs or something
1844 return true;
1847 nofit = true;
1849 if (crushchange && !(leveltime&3)) {
1850 int t;
1851 P_DamageMobj(thing,NULL,NULL,10);
1853 // spray blood in a random direction
1854 mo = P_SpawnMobj (thing->x,
1855 thing->y,
1856 thing->z + thing->height/2, MT_BLOOD);
1858 /* killough 8/10/98: remove dependence on order of evaluation */
1859 t = P_Random(pr_crush);
1860 mo->momx = (t - P_Random (pr_crush))<<12;
1861 t = P_Random(pr_crush);
1862 mo->momy = (t - P_Random (pr_crush))<<12;
1865 // keep checking (crush other things)
1866 return true;
1871 // P_ChangeSector
1873 boolean P_ChangeSector(sector_t* sector,boolean crunch)
1875 int x;
1876 int y;
1878 nofit = false;
1879 crushchange = crunch;
1881 // ARRGGHHH!!!!
1882 // This is horrendously slow!!!
1883 // killough 3/14/98
1885 // re-check heights for all things near the moving sector
1887 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1888 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1889 P_BlockThingsIterator (x, y, PIT_ChangeSector);
1891 return nofit;
1895 // P_CheckSector
1896 // jff 3/19/98 added to just check monsters on the periphery
1897 // of a moving sector instead of all in bounding box of the
1898 // sector. Both more accurate and faster.
1901 boolean P_CheckSector(sector_t* sector,boolean crunch)
1903 msecnode_t *n;
1905 if (comp[comp_floors]) /* use the old routine for old demos though */
1906 return P_ChangeSector(sector,crunch);
1908 nofit = false;
1909 crushchange = crunch;
1911 // killough 4/4/98: scan list front-to-back until empty or exhausted,
1912 // restarting from beginning after each thing is processed. Avoids
1913 // crashes, and is sure to examine all things in the sector, and only
1914 // the things which are in the sector, until a steady-state is reached.
1915 // Things can arbitrarily be inserted and removed and it won't mess up.
1917 // killough 4/7/98: simplified to avoid using complicated counter
1919 // Mark all things invalid
1921 for (n=sector->touching_thinglist; n; n=n->m_snext)
1922 n->visited = false;
1925 for (n=sector->touching_thinglist; n; n=n->m_snext) // go through list
1926 if (!n->visited) // unprocessed thing found
1928 n->visited = true; // mark thing as processed
1929 if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
1930 PIT_ChangeSector(n->m_thing); // process it
1931 break; // exit and start over
1933 while (n); // repeat from scratch until all things left are marked valid
1935 return nofit;
1939 // CPhipps -
1940 // Use block memory allocator here
1942 #include "z_bmalloc.h"
1944 IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(secnodezone, sizeof(msecnode_t), PU_LEVEL, 32, "SecNodes");
1946 inline static msecnode_t* P_GetSecnode(void)
1948 return (msecnode_t*)Z_BMalloc(&secnodezone);
1951 // P_PutSecnode() returns a node to the freelist.
1953 inline static void P_PutSecnode(msecnode_t* node)
1955 Z_BFree(&secnodezone, node);
1958 // phares 3/16/98
1960 // P_AddSecnode() searches the current list to see if this sector is
1961 // already there. If not, it adds a sector node at the head of the list of
1962 // sectors this object appears in. This is called when creating a list of
1963 // nodes that will get linked in later. Returns a pointer to the new node.
1965 msecnode_t* P_AddSecnode(sector_t* s, mobj_t* thing, msecnode_t* nextnode)
1967 msecnode_t* node;
1969 node = nextnode;
1970 while (node)
1972 if (node->m_sector == s) // Already have a node for this sector?
1974 node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
1975 return(nextnode);
1977 node = node->m_tnext;
1980 // Couldn't find an existing node for this sector. Add one at the head
1981 // of the list.
1983 node = P_GetSecnode();
1985 // killough 4/4/98, 4/7/98: mark new nodes unvisited.
1986 node->visited = 0;
1988 node->m_sector = s; // sector
1989 node->m_thing = thing; // mobj
1990 node->m_tprev = NULL; // prev node on Thing thread
1991 node->m_tnext = nextnode; // next node on Thing thread
1992 if (nextnode)
1993 nextnode->m_tprev = node; // set back link on Thing
1995 // Add new node at head of sector thread starting at s->touching_thinglist
1997 node->m_sprev = NULL; // prev node on sector thread
1998 node->m_snext = s->touching_thinglist; // next node on sector thread
1999 if (s->touching_thinglist)
2000 node->m_snext->m_sprev = node;
2001 s->touching_thinglist = node;
2002 return(node);
2006 // P_DelSecnode() deletes a sector node from the list of
2007 // sectors this object appears in. Returns a pointer to the next node
2008 // on the linked list, or NULL.
2010 msecnode_t* P_DelSecnode(msecnode_t* node)
2012 msecnode_t* tp; // prev node on thing thread
2013 msecnode_t* tn; // next node on thing thread
2014 msecnode_t* sp; // prev node on sector thread
2015 msecnode_t* sn; // next node on sector thread
2017 if (node)
2020 // Unlink from the Thing thread. The Thing thread begins at
2021 // sector_list and not from mobj_t->touching_sectorlist.
2023 tp = node->m_tprev;
2024 tn = node->m_tnext;
2025 if (tp)
2026 tp->m_tnext = tn;
2027 if (tn)
2028 tn->m_tprev = tp;
2030 // Unlink from the sector thread. This thread begins at
2031 // sector_t->touching_thinglist.
2033 sp = node->m_sprev;
2034 sn = node->m_snext;
2035 if (sp)
2036 sp->m_snext = sn;
2037 else
2038 node->m_sector->touching_thinglist = sn;
2039 if (sn)
2040 sn->m_sprev = sp;
2042 // Return this node to the freelist
2044 P_PutSecnode(node);
2045 return(tn);
2047 return(NULL);
2048 } // phares 3/13/98
2050 // Delete an entire sector list
2052 void P_DelSeclist(msecnode_t* node)
2055 while (node)
2056 node = P_DelSecnode(node);
2060 // phares 3/14/98
2062 // PIT_GetSectors
2063 // Locates all the sectors the object is in by looking at the lines that
2064 // cross through it. You have already decided that the object is allowed
2065 // at this location, so don't bother with checking impassable or
2066 // blocking lines.
2068 boolean PIT_GetSectors(line_t* ld)
2070 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
2071 tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
2072 tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
2073 tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
2074 return true;
2076 if (P_BoxOnLineSide(tmbbox, ld) != -1)
2077 return true;
2079 // This line crosses through the object.
2081 // Collect the sector(s) from the line and add to the
2082 // sector_list you're examining. If the Thing ends up being
2083 // allowed to move to this position, then the sector_list
2084 // will be attached to the Thing's mobj_t at touching_sectorlist.
2086 sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list);
2088 /* Don't assume all lines are 2-sided, since some Things
2089 * like MT_TFOG are allowed regardless of whether their radius takes
2090 * them beyond an impassable linedef.
2092 * killough 3/27/98, 4/4/98:
2093 * Use sidedefs instead of 2s flag to determine two-sidedness.
2094 * killough 8/1/98: avoid duplicate if same sector on both sides
2095 * cph - DEMOSYNC? */
2097 if (ld->backsector && ld->backsector != ld->frontsector)
2098 sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list);
2100 return true;
2104 // phares 3/14/98
2106 // P_CreateSecNodeList alters/creates the sector_list that shows what sectors
2107 // the object resides in.
2109 void P_CreateSecNodeList(mobj_t* thing,fixed_t x,fixed_t y)
2111 int xl;
2112 int xh;
2113 int yl;
2114 int yh;
2115 int bx;
2116 int by;
2117 msecnode_t* node;
2118 mobj_t* saved_tmthing = tmthing; /* cph - see comment at func end */
2119 fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */
2121 // First, clear out the existing m_thing fields. As each node is
2122 // added or verified as needed, m_thing will be set properly. When
2123 // finished, delete all nodes where m_thing is still NULL. These
2124 // represent the sectors the Thing has vacated.
2126 node = sector_list;
2127 while (node)
2129 node->m_thing = NULL;
2130 node = node->m_tnext;
2133 tmthing = thing;
2135 tmx = x;
2136 tmy = y;
2138 tmbbox[BOXTOP] = y + tmthing->radius;
2139 tmbbox[BOXBOTTOM] = y - tmthing->radius;
2140 tmbbox[BOXRIGHT] = x + tmthing->radius;
2141 tmbbox[BOXLEFT] = x - tmthing->radius;
2143 validcount++; // used to make sure we only process a line once
2145 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
2146 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
2147 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
2148 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
2150 for (bx=xl ; bx<=xh ; bx++)
2151 for (by=yl ; by<=yh ; by++)
2152 P_BlockLinesIterator(bx,by,PIT_GetSectors);
2154 // Add the sector of the (x,y) point to sector_list.
2156 sector_list = P_AddSecnode(thing->subsector->sector,thing,sector_list);
2158 // Now delete any nodes that won't be used. These are the ones where
2159 // m_thing is still NULL.
2161 node = sector_list;
2162 while (node)
2164 if (node->m_thing == NULL)
2166 if (node == sector_list)
2167 sector_list = node->m_tnext;
2168 node = P_DelSecnode(node);
2170 else
2171 node = node->m_tnext;
2174 /* cph -
2175 * This is the strife we get into for using global variables. tmthing
2176 * is being used by several different functions calling
2177 * P_BlockThingIterator, including functions that can be called *from*
2178 * P_BlockThingIterator. Using a global tmthing is not reentrant.
2179 * OTOH for Boom/MBF demos we have to preserve the buggy behavior.
2180 * Fun. We restore its previous value unless we're in a Boom/MBF demo.
2182 if ((compatibility_level < boom_compatibility_compatibility) ||
2183 (compatibility_level >= prboom_3_compatibility))
2184 tmthing = saved_tmthing;
2185 /* And, duh, the same for tmx/y - cph 2002/09/22
2186 * And for tmbbox - cph 2003/08/10 */
2187 if ((compatibility_level < boom_compatibility_compatibility) /* ||
2188 (compatibility_level >= prboom_4_compatibility) */) {
2189 tmx = saved_tmx, tmy = saved_tmy;
2190 if (tmthing) {
2191 tmbbox[BOXTOP] = tmy + tmthing->radius;
2192 tmbbox[BOXBOTTOM] = tmy - tmthing->radius;
2193 tmbbox[BOXRIGHT] = tmx + tmthing->radius;
2194 tmbbox[BOXLEFT] = tmx - tmthing->radius;
2199 /* cphipps 2004/08/30 -
2200 * 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. */
2201 void P_MapStart(void) {
2202 if (tmthing) I_Error("P_MapStart: tmthing set!");
2204 void P_MapEnd(void) {
2205 tmthing = NULL;