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