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