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