]> icculus.org git repositories - theoddone33/hhexen.git/blob - base/p_map.c
Round 2: Added --help and --version, as well as HHEXEN_DATA environment variable...
[theoddone33/hhexen.git] / base / p_map.c
1
2 //**************************************************************************
3 //**
4 //** p_map.c : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile$
7 //** $Revision$
8 //** $Date$
9 //** $Author$
10 //**
11 //**************************************************************************
12
13 #include "h2def.h"
14 #include "p_local.h"
15 #include "soundst.h"
16
17 static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj);
18
19 /*
20 ===============================================================================
21
22 NOTES:
23
24
25 ===============================================================================
26 */
27
28 /*
29 ===============================================================================
30
31 mobj_t NOTES
32
33 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.
34
35 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.
36
37 The sound code uses the x,y, and subsector fields to do stereo positioning of any sound effited by the mobj_t.
38
39 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.
40
41
42 Every mobj_t is linked into a single sector based on it's origin coordinates.
43 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.
44
45 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.
46
47 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.
48
49
50 ===============================================================================
51 */
52
53 fixed_t         tmbbox[4];
54 mobj_t          *tmthing;
55 mobj_t                   *tsthing;
56 int                     tmflags;
57 fixed_t         tmx, tmy;
58
59 boolean         floatok;        // if true, move would be ok if
60                                                         // within tmfloorz - tmceilingz
61
62 fixed_t tmfloorz, tmceilingz, tmdropoffz;
63 int tmfloorpic;
64
65 // keep track of the line that lowers the ceiling, so missiles don't explode
66 // against sky hack walls
67 line_t *ceilingline;
68
69 // keep track of special lines as they are hit, but don't process them
70 // until the move is proven valid
71 #define MAXSPECIALCROSS 8
72 line_t *spechit[MAXSPECIALCROSS];
73 int numspechit;
74
75 mobj_t *onmobj; // generic global onmobj...used for landing on pods/players
76 mobj_t *BlockingMobj;
77
78 /*
79 ===============================================================================
80
81                                         TELEPORT MOVE
82
83 ===============================================================================
84 */
85
86 /*
87 ==================
88 =
89 = PIT_StompThing
90 =
91 ==================
92 */
93
94 boolean PIT_StompThing (mobj_t *thing)
95 {
96         fixed_t         blockdist;
97
98         if (!(thing->flags & MF_SHOOTABLE) )
99                 return true;
100
101         blockdist = thing->radius + tmthing->radius;
102         if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist )
103                 return true;            // didn't hit it
104
105         if (thing == tmthing)
106                 return true;            // don't clip against self
107
108         if(!(tmthing->flags2&MF2_TELESTOMP))
109         { // Not allowed to stomp things
110                 return(false);
111         }
112
113         P_DamageMobj (thing, tmthing, tmthing, 10000);
114
115         return true;
116 }
117
118
119 /*
120 ===================
121 =
122 = P_TeleportMove
123 =
124 ===================
125 */
126
127 boolean P_TeleportMove (mobj_t *thing, fixed_t x, fixed_t y)
128 {
129         int                     xl,xh,yl,yh,bx,by;
130         subsector_t             *newsubsec;
131
132 //
133 // kill anything occupying the position
134 //
135
136         tmthing = thing;
137         tmflags = thing->flags;
138
139         tmx = x;
140         tmy = y;
141
142         tmbbox[BOXTOP] = y + tmthing->radius;
143         tmbbox[BOXBOTTOM] = y - tmthing->radius;
144         tmbbox[BOXRIGHT] = x + tmthing->radius;
145         tmbbox[BOXLEFT] = x - tmthing->radius;
146
147         newsubsec = R_PointInSubsector (x,y);
148         ceilingline = NULL;
149
150 //
151 // the base floor / ceiling is from the subsector that contains the
152 // point.  Any contacted lines the step closer together will adjust them
153 //
154         tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
155         tmceilingz = newsubsec->sector->ceilingheight;
156         tmfloorpic = newsubsec->sector->floorpic;
157
158         validcount++;
159         numspechit = 0;
160
161 //
162 // stomp on any things contacted
163 //
164         xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
165         xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
166         yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
167         yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
168
169         for (bx=xl ; bx<=xh ; bx++)
170                 for (by=yl ; by<=yh ; by++)
171                         if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
172                                 return false;
173
174 //
175 // the move is ok, so link the thing into its new position
176 //
177         P_UnsetThingPosition (thing);
178
179         thing->floorz = tmfloorz;
180         thing->ceilingz = tmceilingz;
181         thing->x = x;
182         thing->y = y;
183
184         P_SetThingPosition (thing);
185
186         return true;
187 }
188
189
190 boolean PIT_ThrustStompThing (mobj_t *thing)
191 {
192         fixed_t         blockdist;
193
194         if (!(thing->flags & MF_SHOOTABLE) )
195                 return true;
196
197         blockdist = thing->radius + tsthing->radius;
198         if ( abs(thing->x - tsthing->x) >= blockdist || 
199                   abs(thing->y - tsthing->y) >= blockdist ||
200                         (thing->z > tsthing->z+tsthing->height) )
201                 return true;            // didn't hit it
202
203         if (thing == tsthing)
204                 return true;            // don't clip against self
205
206         P_DamageMobj (thing, tsthing, tsthing, 10001);
207         tsthing->args[1] = 1;   // Mark thrust thing as bloody
208
209         return true;
210 }
211
212
213
214 void PIT_ThrustSpike(mobj_t *actor)
215 {
216         int xl,xh,yl,yh,bx,by;
217         int x0,x2,y0,y2;
218
219         tsthing = actor;
220
221         x0 = actor->x - actor->info->radius;
222         x2 = actor->x + actor->info->radius;
223         y0 = actor->y - actor->info->radius;
224         y2 = actor->y + actor->info->radius;
225
226         xl = (x0 - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
227         xh = (x2 - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
228         yl = (y0 - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
229         yh = (y2 - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
230
231         // stomp on any things contacted
232         for (bx=xl ; bx<=xh ; bx++)
233                 for (by=yl ; by<=yh ; by++)
234                         P_BlockThingsIterator(bx,by,PIT_ThrustStompThing);
235 }
236
237
238
239 /*
240 ===============================================================================
241
242                                         MOVEMENT ITERATOR FUNCTIONS
243
244 ===============================================================================
245 */
246
247 /*
248 ==================
249 =
250 = PIT_CheckLine
251 =
252 = Adjusts tmfloorz and tmceilingz as lines are contacted
253 ==================
254 */
255
256 boolean PIT_CheckLine(line_t *ld)
257 {
258         if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
259                 ||      tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
260                 ||      tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
261                 ||      tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
262         {
263                 return(true);
264         }
265         if(P_BoxOnLineSide(tmbbox, ld) != -1)
266         {
267                 return(true);
268         }
269
270 // a line has been hit
271 /*
272 =
273 = The moving thing's destination position will cross the given line.
274 = If this should not be allowed, return false.
275 = If the line is special, keep track of it to process later if the move
276 =       is proven ok.  NOTE: specials are NOT sorted by order, so two special lines
277 =       that are only 8 pixels apart could be crossed in either order.
278 */
279
280         if(!ld->backsector)
281         { // One sided line
282                 if (tmthing->flags2&MF2_BLASTED)
283                 {
284                         P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
285                 }
286                 CheckForPushSpecial(ld, 0, tmthing);
287                 return(false);
288         }
289         if(!(tmthing->flags&MF_MISSILE))
290         {
291                 if(ld->flags&ML_BLOCKING)
292                 { // Explicitly blocking everything
293                         if (tmthing->flags2&MF2_BLASTED)
294                         {
295                                 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
296                         }
297                         CheckForPushSpecial(ld, 0, tmthing);
298                         return(false);
299                 }
300                 if(!tmthing->player && ld->flags&ML_BLOCKMONSTERS)
301                 { // Block monsters only
302                         if (tmthing->flags2&MF2_BLASTED)
303                         {
304                                 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
305                         }
306                         return(false);
307                 }
308         }
309         P_LineOpening(ld);              // set openrange, opentop, openbottom
310         // adjust floor / ceiling heights
311         if(opentop < tmceilingz)
312         {
313                 tmceilingz = opentop;
314                 ceilingline = ld;
315         }
316         if(openbottom > tmfloorz)
317         {
318                 tmfloorz = openbottom;
319         }
320         if(lowfloor < tmdropoffz)
321         {
322                 tmdropoffz = lowfloor;
323         }
324         if(ld->special)
325         { // Contacted a special line, add it to the list
326                 spechit[numspechit] = ld;
327                 numspechit++;
328         }
329         return(true);
330 }
331
332 //---------------------------------------------------------------------------
333 //
334 // FUNC PIT_CheckThing
335 //
336 //---------------------------------------------------------------------------
337
338 boolean PIT_CheckThing(mobj_t *thing)
339 {
340         fixed_t blockdist;
341         boolean solid;
342         int damage;
343
344         if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE)))
345         { // Can't hit thing
346                 return(true);
347         }
348         blockdist = thing->radius+tmthing->radius;
349         if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist)
350         { // Didn't hit thing
351                 return(true);
352         }
353         if(thing == tmthing)
354         { // Don't clip against self
355                 return(true);
356         }
357         BlockingMobj = thing;
358         if(tmthing->flags2&MF2_PASSMOBJ)
359         { // check if a mobj passed over/under another object
360                 if(tmthing->type == MT_BISHOP && thing->type == MT_BISHOP)
361                 { // don't let bishops fly over other bishops
362                         return false;
363                 }
364                 if(tmthing->z >= thing->z+thing->height
365                         && !(thing->flags&MF_SPECIAL))
366                 {
367                         return(true);
368                 }
369                 else if(tmthing->z+tmthing->height < thing->z
370                         && !(thing->flags&MF_SPECIAL))
371                 { // under thing
372                         return(true);
373                 }
374         }
375         // Check for skulls slamming into things
376         if(tmthing->flags&MF_SKULLFLY)
377         {
378                 if(tmthing->type == MT_MINOTAUR)
379                 {
380                         // Slamming minotaurs shouldn't move non-creatures
381                         if (!(thing->flags&MF_COUNTKILL))
382                         {
383                                 return(false);
384                         }
385                 }
386                 else if(tmthing->type == MT_HOLY_FX)
387                 {
388                         if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
389                         {
390                                 if(netgame && !deathmatch && thing->player)
391                                 { // don't attack other co-op players
392                                         return true;
393                                 }
394                                 if(thing->flags2&MF2_REFLECTIVE
395                                         && (thing->player || thing->flags2&MF2_BOSS))
396                                 {
397                                         tmthing->special1 = (int)tmthing->target;
398                                         tmthing->target = thing;
399                                         return true;
400                                 }
401                                 if(thing->flags&MF_COUNTKILL || thing->player)
402                                 {
403                                         tmthing->special1 = (int)thing;
404                                 }
405                                 if(P_Random() < 96)
406                                 {
407                                         damage = 12;
408                                         if(thing->player || thing->flags2&MF2_BOSS)
409                                         {
410                                                 damage = 3;
411                                                 // ghost burns out faster when attacking players/bosses
412                                                 tmthing->health -= 6;
413                                         }
414                                         P_DamageMobj(thing, tmthing, tmthing->target, damage);
415                                         if(P_Random() < 128)
416                                         {
417                                                 P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z,
418                                                         MT_HOLY_PUFF);
419                                                 S_StartSound(tmthing, SFX_SPIRIT_ATTACK);
420                                                 if(thing->flags&MF_COUNTKILL && P_Random() < 128
421                                                 && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT))
422                                                 {
423                                                         if ((thing->type == MT_CENTAUR) ||
424                                                                 (thing->type == MT_CENTAURLEADER) ||
425                                                                 (thing->type == MT_ETTIN))
426                                                         {
427                                                                 S_StartSound(thing, SFX_PUPPYBEAT);
428                                                         }
429                                                 }
430                                         }
431                                 }
432                                 if(thing->health <= 0)
433                                 {
434                                         tmthing->special1 = 0;
435                                 }
436                         }
437                         return true;
438                 }
439                 damage = ((P_Random()%8)+1)*tmthing->damage;
440                 P_DamageMobj(thing, tmthing, tmthing, damage);
441                 tmthing->flags &= ~MF_SKULLFLY;
442                 tmthing->momx = tmthing->momy = tmthing->momz = 0;
443                 P_SetMobjState(tmthing, tmthing->info->seestate);
444                 return(false);
445         }
446         // Check for blasted thing running into another
447         if(tmthing->flags2&MF2_BLASTED && thing->flags&MF_SHOOTABLE)
448         {
449                 if (!(thing->flags2&MF2_BOSS) &&
450                         (thing->flags&MF_COUNTKILL))
451                 {
452                         thing->momx += tmthing->momx;
453                         thing->momy += tmthing->momy;
454                         if ((thing->momx + thing->momy) > 3*FRACUNIT)
455                         {
456                                 damage = (tmthing->info->mass/100)+1;
457                                 P_DamageMobj(thing, tmthing, tmthing, damage);
458                                 damage = (thing->info->mass/100)+1;
459                                 P_DamageMobj(tmthing, thing, thing, damage>>2);
460                         }
461                         return(false);
462                 }
463         }
464         // Check for missile
465         if(tmthing->flags&MF_MISSILE)
466         {
467                 // Check for a non-shootable mobj
468                 if(thing->flags2&MF2_NONSHOOTABLE)
469                 {
470                         return true;
471                 }
472                 // Check if it went over / under
473                 if(tmthing->z > thing->z+thing->height)
474                 { // Over thing
475                         return(true);
476                 }
477                 if(tmthing->z+tmthing->height < thing->z)
478                 { // Under thing
479                         return(true);
480                 }
481                 if(tmthing->flags2&MF2_FLOORBOUNCE)
482                 {
483                         if(tmthing->target == thing || !(thing->flags&MF_SOLID))
484                         {
485                                 return true;
486                         }
487                         else
488                         {
489                                 return false;
490                         }
491                 }
492                 if(tmthing->type == MT_LIGHTNING_FLOOR
493                         || tmthing->type == MT_LIGHTNING_CEILING)
494                 {
495                         if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
496                         {
497                                 if(thing->info->mass != MAXINT)
498                                 {
499                                         thing->momx += tmthing->momx>>4;
500                                         thing->momy += tmthing->momy>>4;
501                                 }
502                                 if((!thing->player && !(thing->flags2&MF2_BOSS))
503                                         || !(leveltime&1))
504                                 {
505                                         if(thing->type == MT_CENTAUR 
506                                         || thing->type == MT_CENTAURLEADER)
507                                         { // Lightning does more damage to centaurs
508                                                 P_DamageMobj(thing, tmthing, tmthing->target, 9);
509                                         }
510                                         else
511                                         {
512                                                 P_DamageMobj(thing, tmthing, tmthing->target, 3);
513                                         }
514                                         if(!(S_GetSoundPlayingInfo(tmthing, 
515                                                 SFX_MAGE_LIGHTNING_ZAP)))
516                                         {
517                                                 S_StartSound(tmthing, SFX_MAGE_LIGHTNING_ZAP);
518                                         }
519                                         if(thing->flags&MF_COUNTKILL && P_Random() < 64 
520                                         && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT))
521                                         {
522                                                 if ((thing->type == MT_CENTAUR) ||
523                                                         (thing->type == MT_CENTAURLEADER) ||
524                                                         (thing->type == MT_ETTIN))
525                                                 {
526                                                         S_StartSound(thing, SFX_PUPPYBEAT);
527                                                 }
528                                         }
529                                 }
530                                 tmthing->health--;
531                                 if(tmthing->health <= 0 || thing->health <= 0)
532                                 {
533                                         return false;
534                                 }
535                                 if(tmthing->type == MT_LIGHTNING_FLOOR)
536                                 {
537                                         if(tmthing->special2 
538                                                 && !((mobj_t *)tmthing->special2)->special1)
539                                         {
540                                                 ((mobj_t *)tmthing->special2)->special1 = 
541                                                         (int)thing;
542                                         }
543                                 }
544                                 else if(!tmthing->special1)
545                                 {
546                                         tmthing->special1 = (int)thing;
547                                 }
548                         }
549                         return true; // lightning zaps through all sprites
550                 }
551                 else if(tmthing->type == MT_LIGHTNING_ZAP)
552                 {
553                         mobj_t *lmo;
554
555                         if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
556                         {                       
557                                 lmo = (mobj_t *)tmthing->special2;
558                                 if(lmo)
559                                 {
560                                         if(lmo->type == MT_LIGHTNING_FLOOR)
561                                         {
562                                                 if(lmo->special2 
563                                                         && !((mobj_t *)lmo->special2)->special1)
564                                                 {
565                                                         ((mobj_t *)lmo->special2)->special1 = (int)thing;
566                                                 }
567                                         }
568                                         else if(!lmo->special1)
569                                         {
570                                                 lmo->special1 = (int)thing;
571                                         }
572                                         if(!(leveltime&3))
573                                         {
574                                                 lmo->health--;
575                                         }
576                                 }
577                         }
578                 }
579                 else if(tmthing->type == MT_MSTAFF_FX2 && thing != tmthing->target)
580                 {
581                         if(!thing->player && !(thing->flags2&MF2_BOSS))
582                         {
583                                 switch(thing->type)
584                                 {
585                                         case MT_FIGHTER_BOSS:   // these not flagged boss
586                                         case MT_CLERIC_BOSS:    // so they can be blasted
587                                         case MT_MAGE_BOSS:
588                                                 break;
589                                         default:
590                                                 P_DamageMobj(thing, tmthing, tmthing->target, 10);
591                                                 return true;
592                                                 break;
593                                 }
594                         }
595                 }
596                 if(tmthing->target && tmthing->target->type == thing->type)
597                 { // Don't hit same species as originator
598                         if(thing == tmthing->target)
599                         { // Don't missile self
600                                 return(true);
601                         }
602                         if(!thing->player)
603                         { // Hit same species as originator, explode, no damage
604                                 return(false);
605                         }
606                 }
607                 if(!(thing->flags&MF_SHOOTABLE))
608                 { // Didn't do any damage
609                         return!(thing->flags&MF_SOLID);
610                 }
611                 if(tmthing->flags2&MF2_RIP)
612                 {
613                         if (!(thing->flags&MF_NOBLOOD) &&
614                                 !(thing->flags2&MF2_REFLECTIVE) &&
615                                 !(thing->flags2&MF2_INVULNERABLE))
616                         { // Ok to spawn some blood
617                                 P_RipperBlood(tmthing);
618                         }
619                         //S_StartSound(tmthing, sfx_ripslop);
620                         damage = ((P_Random()&3)+2)*tmthing->damage;
621                         P_DamageMobj(thing, tmthing, tmthing->target, damage);
622                         if(thing->flags2&MF2_PUSHABLE
623                                 && !(tmthing->flags2&MF2_CANNOTPUSH))
624                         { // Push thing
625                                 thing->momx += tmthing->momx>>2;
626                                 thing->momy += tmthing->momy>>2;
627                         }
628                         numspechit = 0;
629                         return(true);
630                 }
631                 // Do damage
632                 damage = ((P_Random()%8)+1)*tmthing->damage;
633                 if(damage)
634                 {
635                         if (!(thing->flags&MF_NOBLOOD) && 
636                                 !(thing->flags2&MF2_REFLECTIVE) &&
637                                 !(thing->flags2&MF2_INVULNERABLE) &&
638                                 !(tmthing->type == MT_TELOTHER_FX1) &&
639                                 !(tmthing->type == MT_TELOTHER_FX2) &&
640                                 !(tmthing->type == MT_TELOTHER_FX3) &&
641                                 !(tmthing->type == MT_TELOTHER_FX4) &&
642                                 !(tmthing->type == MT_TELOTHER_FX5) &&
643                                 (P_Random() < 192))
644                         {
645                                 P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing);
646                         }
647                         P_DamageMobj(thing, tmthing, tmthing->target, damage);
648                 }
649                 return(false);
650         }
651         if(thing->flags2&MF2_PUSHABLE && !(tmthing->flags2&MF2_CANNOTPUSH))
652         { // Push thing
653                 thing->momx += tmthing->momx>>2;
654                 thing->momy += tmthing->momy>>2;
655         }
656         // Check for special thing
657         if(thing->flags&MF_SPECIAL)
658         {
659                 solid = thing->flags&MF_SOLID;
660                 if(tmflags&MF_PICKUP)
661                 { // Can be picked up by tmthing
662                         P_TouchSpecialThing(thing, tmthing); // Can remove thing
663                 }
664                 return(!solid);
665         }
666         return(!(thing->flags&MF_SOLID));
667 }
668
669 //---------------------------------------------------------------------------
670 //
671 // PIT_CheckOnmobjZ
672 //
673 //---------------------------------------------------------------------------
674
675 boolean PIT_CheckOnmobjZ(mobj_t *thing)
676 {
677         fixed_t blockdist;
678
679         if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE)))
680         { // Can't hit thing
681                 return(true);
682         }
683         blockdist = thing->radius+tmthing->radius;
684         if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist)
685         { // Didn't hit thing
686                 return(true);
687         }
688         if(thing == tmthing)
689         { // Don't clip against self
690                 return(true);
691         }
692         if(tmthing->z > thing->z+thing->height)
693         {
694                 return(true);
695         }
696         else if(tmthing->z+tmthing->height < thing->z)
697         { // under thing
698                 return(true);
699         }
700         if(thing->flags&MF_SOLID)
701         {
702                 onmobj = thing;
703         }
704         return(!(thing->flags&MF_SOLID));
705 }
706
707 /*
708 ===============================================================================
709
710                                                 MOVEMENT CLIPPING
711
712 ===============================================================================
713 */
714
715 //----------------------------------------------------------------------------
716 //
717 // FUNC P_TestMobjLocation
718 //
719 // Returns true if the mobj is not blocked by anything at its current
720 // location, otherwise returns false.
721 //
722 //----------------------------------------------------------------------------
723
724 boolean P_TestMobjLocation(mobj_t *mobj)
725 {
726         int flags;
727
728         flags = mobj->flags;
729         mobj->flags &= ~MF_PICKUP;
730         if(P_CheckPosition(mobj, mobj->x, mobj->y))
731         { // XY is ok, now check Z
732                 mobj->flags = flags;
733                 if((mobj->z < mobj->floorz)
734                         || (mobj->z+mobj->height > mobj->ceilingz))
735                 { // Bad Z
736                         return(false);
737                 }
738                 return(true);
739         }
740         mobj->flags = flags;
741         return(false);
742 }
743
744 /*
745 ==================
746 =
747 = P_CheckPosition
748 =
749 = This is purely informative, nothing is modified (except things picked up)
750
751 in:
752 a mobj_t (can be valid or invalid)
753 a position to be checked (doesn't need to be related to the mobj_t->x,y)
754
755 during:
756 special things are touched if MF_PICKUP
757 early out on solid lines?
758
759 out:
760 newsubsec
761 floorz
762 ceilingz
763 tmdropoffz = the lowest point contacted (monsters won't move to a dropoff)
764 speciallines[]
765 numspeciallines
766 mobj_t *BlockingMobj = pointer to thing that blocked position (NULL if not
767 blocked, or blocked by a line).
768
769 ==================
770 */
771
772 boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y)
773 {
774         int                     xl,xh,yl,yh,bx,by;
775         subsector_t             *newsubsec;
776
777         tmthing = thing;
778         tmflags = thing->flags;
779
780         tmx = x;
781         tmy = y;
782
783         tmbbox[BOXTOP] = y + tmthing->radius;
784         tmbbox[BOXBOTTOM] = y - tmthing->radius;
785         tmbbox[BOXRIGHT] = x + tmthing->radius;
786         tmbbox[BOXLEFT] = x - tmthing->radius;
787
788         newsubsec = R_PointInSubsector (x,y);
789         ceilingline = NULL;
790
791 //
792 // the base floor / ceiling is from the subsector that contains the
793 // point.  Any contacted lines the step closer together will adjust them
794 //
795         tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
796         tmceilingz = newsubsec->sector->ceilingheight;
797         tmfloorpic = newsubsec->sector->floorpic;
798
799         validcount++;
800         numspechit = 0;
801
802         if(tmflags&MF_NOCLIP && !(tmflags&MF_SKULLFLY))
803         {
804                 return true;
805         }
806
807 //
808 // check things first, possibly picking things up
809 // the bounding box is extended by MAXRADIUS because mobj_ts are grouped
810 // into mapblocks based on their origin point, and can overlap into adjacent
811 // blocks by up to MAXRADIUS units
812 //
813         xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
814         xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
815         yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
816         yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
817
818         BlockingMobj = NULL;
819         for (bx=xl ; bx<=xh ; bx++)
820                 for (by=yl ; by<=yh ; by++)
821                         if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
822                                 return false;
823 //
824 // check lines
825 //
826         if(tmflags&MF_NOCLIP)
827         {
828                 return true;
829         }
830
831         BlockingMobj = NULL;
832         xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
833         xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
834         yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
835         yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
836
837         for (bx=xl ; bx<=xh ; bx++)
838                 for (by=yl ; by<=yh ; by++)
839                         if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
840                                 return false;
841         return true;
842 }
843
844 //=============================================================================
845 //
846 // P_CheckOnmobj(mobj_t *thing)
847 //
848 //              Checks if the new Z position is legal
849 //=============================================================================
850
851 mobj_t *P_CheckOnmobj(mobj_t *thing)
852 {
853         int                     xl,xh,yl,yh,bx,by;
854         subsector_t             *newsubsec;
855         fixed_t x;
856         fixed_t y;
857         mobj_t oldmo;
858
859         x = thing->x;
860         y = thing->y;
861         tmthing = thing;
862         tmflags = thing->flags;
863         oldmo = *thing; // save the old mobj before the fake zmovement
864         P_FakeZMovement(tmthing);
865
866         tmx = x;
867         tmy = y;
868
869         tmbbox[BOXTOP] = y + tmthing->radius;
870         tmbbox[BOXBOTTOM] = y - tmthing->radius;
871         tmbbox[BOXRIGHT] = x + tmthing->radius;
872         tmbbox[BOXLEFT] = x - tmthing->radius;
873
874         newsubsec = R_PointInSubsector (x,y);
875         ceilingline = NULL;
876
877 //
878 // the base floor / ceiling is from the subsector that contains the
879 // point.  Any contacted lines the step closer together will adjust them
880 //
881         tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
882         tmceilingz = newsubsec->sector->ceilingheight;
883         tmfloorpic = newsubsec->sector->floorpic;
884
885         validcount++;
886         numspechit = 0;
887
888         if ( tmflags & MF_NOCLIP )
889                 return NULL;
890
891 //
892 // check things first, possibly picking things up
893 // the bounding box is extended by MAXRADIUS because mobj_ts are grouped
894 // into mapblocks based on their origin point, and can overlap into adjacent
895 // blocks by up to MAXRADIUS units
896 //
897         xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
898         xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
899         yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
900         yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
901
902         for (bx=xl ; bx<=xh ; bx++)
903                 for (by=yl ; by<=yh ; by++)
904                         if (!P_BlockThingsIterator(bx,by,PIT_CheckOnmobjZ))
905                         {
906                                 *tmthing = oldmo;
907                                 return onmobj;
908                         }
909         *tmthing = oldmo;
910         return NULL;
911 }
912
913 //=============================================================================
914 //
915 // P_FakeZMovement
916 //
917 //              Fake the zmovement so that we can check if a move is legal
918 //=============================================================================
919
920 void P_FakeZMovement(mobj_t *mo)
921 {
922         int dist;
923         int delta;
924 //
925 // adjust height
926 //
927         mo->z += mo->momz;
928         if(mo->flags&MF_FLOAT && mo->target)
929         {       // float down towards target if too close
930                 if(!(mo->flags&MF_SKULLFLY) && !(mo->flags&MF_INFLOAT))
931                 {
932                         dist = P_AproxDistance(mo->x-mo->target->x, mo->y-mo->target->y);
933                         delta =( mo->target->z+(mo->height>>1))-mo->z;
934                         if (delta < 0 && dist < -(delta*3))
935                                 mo->z -= FLOATSPEED;
936                         else if (delta > 0 && dist < (delta*3))
937                                 mo->z += FLOATSPEED;
938                 }
939         }
940         if(mo->player && mo->flags2&MF2_FLY && !(mo->z <= mo->floorz)
941                 && leveltime&2)
942         {
943                 mo->z += finesine[(FINEANGLES/20*leveltime>>2)&FINEMASK];
944         }
945
946 //
947 // clip movement
948 //
949         if(mo->z <= mo->floorz)
950         { // Hit the floor
951                 mo->z = mo->floorz;
952                 if(mo->momz < 0)
953                 {
954                         mo->momz = 0;
955                 }
956                 if(mo->flags&MF_SKULLFLY)
957                 { // The skull slammed into something
958                         mo->momz = -mo->momz;
959                 }
960                 if(mo->info->crashstate && (mo->flags&MF_CORPSE))
961                 {
962                         return;
963                 }
964         }
965         else if(mo->flags2&MF2_LOGRAV)
966         {
967                 if(mo->momz == 0)
968                         mo->momz = -(GRAVITY>>3)*2;
969                 else
970                         mo->momz -= GRAVITY>>3;
971         }
972         else if (! (mo->flags & MF_NOGRAVITY) )
973         {
974                 if (mo->momz == 0)
975                         mo->momz = -GRAVITY*2;
976                 else
977                         mo->momz -= GRAVITY;
978         }
979
980         if (mo->z + mo->height > mo->ceilingz)
981         {       // hit the ceiling
982                 if (mo->momz > 0)
983                         mo->momz = 0;
984                 mo->z = mo->ceilingz - mo->height;
985                 if (mo->flags & MF_SKULLFLY)
986                 {       // the skull slammed into something
987                         mo->momz = -mo->momz;
988                 }
989         }
990 }
991
992 //===========================================================================
993 //
994 // CheckForPushSpecial
995 //
996 //===========================================================================
997
998 static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj)
999 {
1000         if (line->special)
1001         {
1002                 if(mobj->flags2&MF2_PUSHWALL)
1003                 {
1004                         P_ActivateLine(line, mobj, side, SPAC_PUSH);
1005                 }
1006                 else if(mobj->flags2&MF2_IMPACT)
1007                 {
1008                         P_ActivateLine(line, mobj, side, SPAC_IMPACT);
1009                 }       
1010         }
1011 }
1012
1013 /*
1014 ===================
1015 =
1016 = P_TryMove
1017 =
1018 = Attempt to move to a new position, crossing special lines unless MF_TELEPORT
1019 = is set
1020 =
1021 ===================
1022 */
1023
1024 boolean P_TryMove (mobj_t *thing, fixed_t x, fixed_t y)
1025 {
1026         fixed_t         oldx, oldy;
1027         int                     side, oldside;
1028         line_t          *ld;
1029
1030         floatok = false;
1031         if(!P_CheckPosition(thing, x, y))
1032         { // Solid wall or thing
1033                 if(!BlockingMobj || BlockingMobj->player 
1034                         || !thing->player)
1035                 { 
1036                         goto pushline;
1037                 }
1038                 else if (BlockingMobj->z+BlockingMobj->height-thing->z 
1039                         > 24*FRACUNIT 
1040                         || (BlockingMobj->subsector->sector->ceilingheight
1041                         -(BlockingMobj->z+BlockingMobj->height) < thing->height)
1042                         || (tmceilingz-(BlockingMobj->z+BlockingMobj->height) 
1043                         < thing->height))
1044                 {
1045                         goto pushline;
1046                 }
1047         }
1048         if(!(thing->flags&MF_NOCLIP))
1049         {
1050                 if(tmceilingz-tmfloorz < thing->height)
1051                 { // Doesn't fit
1052                         goto pushline;
1053                 }
1054                 floatok = true;
1055                 if(!(thing->flags&MF_TELEPORT)
1056                         && tmceilingz-thing->z < thing->height
1057                         && thing->type != MT_LIGHTNING_CEILING
1058                         && !(thing->flags2&MF2_FLY))
1059                 { // mobj must lower itself to fit
1060                         goto pushline;
1061                 }
1062                 if(thing->flags2&MF2_FLY)
1063                 {
1064                         if(thing->z+thing->height > tmceilingz)
1065                         {
1066                                 thing->momz = -8*FRACUNIT;
1067                                 goto pushline;
1068                         }
1069                         else if(thing->z < tmfloorz && tmfloorz-tmdropoffz > 24*FRACUNIT)
1070                         {
1071                                 thing->momz = 8*FRACUNIT;
1072                                 goto pushline;
1073                         }
1074                 }
1075                 if(!(thing->flags&MF_TELEPORT)
1076                         // The Minotaur floor fire (MT_MNTRFX2) can step up any amount
1077                         && thing->type != MT_MNTRFX2 && thing->type != MT_LIGHTNING_FLOOR
1078                         && tmfloorz-thing->z > 24*FRACUNIT)
1079                 {
1080                         goto pushline;
1081                 }
1082                 if (!(thing->flags&(MF_DROPOFF|MF_FLOAT)) && 
1083                         (tmfloorz-tmdropoffz > 24*FRACUNIT) &&
1084                         !(thing->flags2&MF2_BLASTED))
1085                 { // Can't move over a dropoff unless it's been blasted
1086                                 return(false);
1087                 }
1088                 if(thing->flags2&MF2_CANTLEAVEFLOORPIC 
1089                         && (tmfloorpic != thing->subsector->sector->floorpic
1090                                 || tmfloorz-thing->z != 0))
1091                 { // must stay within a sector of a certain floor type
1092                         return false;
1093                 }
1094         }
1095
1096 //
1097 // the move is ok, so link the thing into its new position
1098 //
1099         P_UnsetThingPosition (thing);
1100
1101         oldx = thing->x;
1102         oldy = thing->y;
1103         thing->floorz = tmfloorz;
1104         thing->ceilingz = tmceilingz;
1105         thing->floorpic = tmfloorpic;
1106         thing->x = x;
1107         thing->y = y;
1108
1109         P_SetThingPosition (thing);
1110
1111         if(thing->flags2&MF2_FLOORCLIP)
1112         {
1113                 if(thing->z == thing->subsector->sector->floorheight
1114                         && P_GetThingFloorType(thing) >= FLOOR_LIQUID)
1115                 {
1116                         thing->floorclip = 10*FRACUNIT;
1117                 }
1118                 else 
1119                 {
1120                         thing->floorclip = 0;
1121                 }
1122         }
1123
1124 //
1125 // if any special lines were hit, do the effect
1126 //
1127         if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
1128         {
1129                 while (numspechit > 0)
1130                 {
1131                         numspechit--;
1132                         // see if the line was crossed
1133                         ld = spechit[numspechit];
1134                         side = P_PointOnLineSide (thing->x, thing->y, ld);
1135                         oldside = P_PointOnLineSide (oldx, oldy, ld);
1136                         if (side != oldside)
1137                         {
1138                                 if (ld->special)
1139                                 {
1140                                         if(thing->player)
1141                                         {
1142                                                 P_ActivateLine(ld, thing, oldside, SPAC_CROSS);
1143                                         }
1144                                         else if(thing->flags2&MF2_MCROSS)
1145                                         {
1146                                                 P_ActivateLine(ld, thing, oldside, SPAC_MCROSS);
1147                                         }
1148                                         else if(thing->flags2&MF2_PCROSS)
1149                                         {
1150                                                 P_ActivateLine(ld, thing, oldside, SPAC_PCROSS);
1151                                         }
1152                                 }
1153                         }
1154                 }
1155         }
1156         return true;
1157
1158 pushline:
1159         if(!(thing->flags&(MF_TELEPORT|MF_NOCLIP)))
1160         {
1161                 int numSpecHitTemp;
1162
1163                 if (tmthing->flags2&MF2_BLASTED)
1164                 {
1165                         P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
1166                 }
1167                 numSpecHitTemp = numspechit;
1168                 while (numSpecHitTemp > 0)
1169                 {
1170                         numSpecHitTemp--;
1171                         // see if the line was crossed
1172                         ld = spechit[numSpecHitTemp];
1173                         side = P_PointOnLineSide (thing->x, thing->y, ld);
1174                         CheckForPushSpecial(ld, side, thing);
1175                 }
1176         }
1177         return false;
1178 }
1179
1180 /*
1181 ==================
1182 =
1183 = P_ThingHeightClip
1184 =
1185 = Takes a valid thing and adjusts the thing->floorz, thing->ceilingz,
1186 = anf possibly thing->z
1187 =
1188 = This is called for all nearby monsters whenever a sector changes height
1189 =
1190 = If the thing doesn't fit, the z will be set to the lowest value and
1191 = false will be returned
1192 ==================
1193 */
1194
1195 boolean P_ThingHeightClip (mobj_t *thing)
1196 {
1197         boolean         onfloor;
1198
1199         onfloor = (thing->z == thing->floorz);
1200
1201         P_CheckPosition (thing, thing->x, thing->y);
1202         // what about stranding a monster partially off an edge?
1203
1204         thing->floorz = tmfloorz;
1205         thing->ceilingz = tmceilingz;
1206         thing->floorpic = tmfloorpic;
1207
1208         if (onfloor)
1209         { // walking monsters rise and fall with the floor
1210                 if((thing->z-thing->floorz < 9*FRACUNIT) 
1211                         || (thing->flags&MF_NOGRAVITY))
1212                 { 
1213                         thing->z = thing->floorz;
1214                 }
1215         }
1216         else
1217         {       // don't adjust a floating monster unless forced to
1218                 if (thing->z+thing->height > thing->ceilingz)
1219                         thing->z = thing->ceilingz - thing->height;
1220         }
1221
1222         if (thing->ceilingz - thing->floorz < thing->height)
1223                 return false;
1224
1225         return true;
1226 }
1227
1228
1229 /*
1230 ==============================================================================
1231
1232                                                         SLIDE MOVE
1233
1234 Allows the player to slide along any angled walls
1235
1236 ==============================================================================
1237 */
1238
1239 fixed_t         bestslidefrac, secondslidefrac;
1240 line_t          *bestslideline, *secondslideline;
1241 mobj_t          *slidemo;
1242
1243 fixed_t         tmxmove, tmymove;
1244
1245 /*
1246 ==================
1247 =
1248 = P_HitSlideLine
1249 =
1250 = Adjusts the xmove / ymove so that the next move will slide along the wall
1251 ==================
1252 */
1253
1254 void P_HitSlideLine (line_t *ld)
1255 {
1256         int                     side;
1257         angle_t         lineangle, moveangle, deltaangle;
1258         fixed_t         movelen, newlen;
1259
1260
1261         if (ld->slopetype == ST_HORIZONTAL)
1262         {
1263                 tmymove = 0;
1264                 return;
1265         }
1266         if (ld->slopetype == ST_VERTICAL)
1267         {
1268                 tmxmove = 0;
1269                 return;
1270         }
1271
1272         side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
1273
1274         lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
1275         if (side == 1)
1276                 lineangle += ANG180;
1277         moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
1278         deltaangle = moveangle-lineangle;
1279         if (deltaangle > ANG180)
1280                 deltaangle += ANG180;
1281 //              I_Error ("SlideLine: ang>ANG180");
1282
1283         lineangle >>= ANGLETOFINESHIFT;
1284         deltaangle >>= ANGLETOFINESHIFT;
1285
1286         movelen = P_AproxDistance (tmxmove, tmymove);
1287         newlen = FixedMul (movelen, finecosine[deltaangle]);
1288         tmxmove = FixedMul (newlen, finecosine[lineangle]);
1289         tmymove = FixedMul (newlen, finesine[lineangle]);
1290 }
1291
1292 /*
1293 ==============
1294 =
1295 = PTR_SlideTraverse
1296 =
1297 ==============
1298 */
1299
1300 boolean         PTR_SlideTraverse (intercept_t *in)
1301 {
1302         line_t  *li;
1303
1304         if (!in->isaline)
1305                 I_Error ("PTR_SlideTraverse: not a line?");
1306
1307         li = in->d.line;
1308         if ( ! (li->flags & ML_TWOSIDED) )
1309         {
1310                 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1311                         return true;            // don't hit the back side
1312                 goto isblocking;
1313         }
1314
1315         P_LineOpening (li);                  // set openrange, opentop, openbottom
1316         if (openrange < slidemo->height)
1317                 goto isblocking;                // doesn't fit
1318
1319         if (opentop - slidemo->z < slidemo->height)
1320                 goto isblocking;                // mobj is too high
1321
1322         if (openbottom - slidemo->z > 24*FRACUNIT )
1323                 goto isblocking;                // too big a step up
1324
1325         return true;            // this line doesn't block movement
1326
1327 // the line does block movement, see if it is closer than best so far
1328 isblocking:
1329         if (in->frac < bestslidefrac)
1330         {
1331                 secondslidefrac = bestslidefrac;
1332                 secondslideline = bestslideline;
1333                 bestslidefrac = in->frac;
1334                 bestslideline = li;
1335         }
1336
1337         return false;   // stop
1338 }
1339
1340
1341 /*
1342 ==================
1343 =
1344 = P_SlideMove
1345 =
1346 = The momx / momy move is bad, so try to slide along a wall
1347 =
1348 = Find the first line hit, move flush to it, and slide along it
1349 =
1350 = This is a kludgy mess.
1351 ==================
1352 */
1353
1354 void P_SlideMove (mobj_t *mo)
1355 {
1356         fixed_t         leadx, leady;
1357         fixed_t         trailx, traily;
1358         fixed_t         newx, newy;
1359         int                     hitcount;
1360
1361         slidemo = mo;
1362         hitcount = 0;
1363 retry:
1364         if (++hitcount == 3)
1365                 goto stairstep;                 // don't loop forever
1366
1367 //
1368 // trace along the three leading corners
1369 //
1370         if (mo->momx > 0)
1371         {
1372                 leadx = mo->x + mo->radius;
1373                 trailx = mo->x - mo->radius;
1374         }
1375         else
1376         {
1377                 leadx = mo->x - mo->radius;
1378                 trailx = mo->x + mo->radius;
1379         }
1380
1381         if (mo->momy > 0)
1382         {
1383                 leady = mo->y + mo->radius;
1384                 traily = mo->y - mo->radius;
1385         }
1386         else
1387         {
1388                 leady = mo->y - mo->radius;
1389                 traily = mo->y + mo->radius;
1390         }
1391
1392         bestslidefrac = FRACUNIT+1;
1393
1394         P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
1395          PT_ADDLINES, PTR_SlideTraverse );
1396         P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
1397          PT_ADDLINES, PTR_SlideTraverse );
1398         P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
1399          PT_ADDLINES, PTR_SlideTraverse );
1400
1401 //
1402 // move up to the wall
1403 //
1404         if(bestslidefrac == FRACUNIT+1)
1405         { // the move must have hit the middle, so stairstep
1406 stairstep:
1407                 if(!P_TryMove(mo, mo->x, mo->y+mo->momy))
1408                 {
1409                         P_TryMove(mo, mo->x+mo->momx, mo->y);
1410                 }
1411                 return;
1412         }
1413
1414         bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit
1415         if (bestslidefrac > 0)
1416         {
1417                 newx = FixedMul (mo->momx, bestslidefrac);
1418                 newy = FixedMul (mo->momy, bestslidefrac);
1419                 if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
1420                         goto stairstep;
1421         }
1422
1423 //
1424 // now continue along the wall
1425 //
1426         bestslidefrac = FRACUNIT-(bestslidefrac+0x800); // remainder
1427         if (bestslidefrac > FRACUNIT)
1428                 bestslidefrac = FRACUNIT;
1429         if (bestslidefrac <= 0)
1430                 return;
1431
1432         tmxmove = FixedMul (mo->momx, bestslidefrac);
1433         tmymove = FixedMul (mo->momy, bestslidefrac);
1434
1435         P_HitSlideLine (bestslideline);                         // clip the moves
1436
1437         mo->momx = tmxmove;
1438         mo->momy = tmymove;
1439
1440         if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
1441         {
1442                 goto retry;
1443         }
1444 }
1445
1446 //============================================================================
1447 //
1448 // PTR_BounceTraverse
1449 //
1450 //============================================================================
1451
1452 boolean PTR_BounceTraverse(intercept_t *in)
1453 {
1454         line_t  *li;
1455
1456         if (!in->isaline)
1457                 I_Error ("PTR_BounceTraverse: not a line?");
1458
1459         li = in->d.line;
1460         if (!(li->flags&ML_TWOSIDED))
1461         {
1462                 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1463                         return true;            // don't hit the back side
1464                 goto bounceblocking;
1465         }
1466
1467         P_LineOpening (li);                  // set openrange, opentop, openbottom
1468         if (openrange < slidemo->height)
1469                 goto bounceblocking;                // doesn't fit
1470
1471         if (opentop - slidemo->z < slidemo->height)
1472                 goto bounceblocking;                // mobj is too high
1473         return true;            // this line doesn't block movement
1474
1475 // the line does block movement, see if it is closer than best so far
1476 bounceblocking:
1477         if (in->frac < bestslidefrac)
1478         {
1479                 secondslidefrac = bestslidefrac;
1480                 secondslideline = bestslideline;
1481                 bestslidefrac = in->frac;
1482                 bestslideline = li;
1483         }
1484         return false;   // stop
1485 }
1486
1487 //============================================================================
1488 //
1489 // P_BounceWall
1490 //
1491 //============================================================================
1492
1493 void P_BounceWall(mobj_t *mo)
1494 {
1495         fixed_t         leadx, leady;
1496         int             side;
1497         angle_t         lineangle, moveangle, deltaangle;
1498         fixed_t         movelen;
1499
1500         slidemo = mo;
1501
1502 //
1503 // trace along the three leading corners
1504 //
1505         if(mo->momx > 0)
1506         {
1507                 leadx = mo->x+mo->radius;
1508         }
1509         else
1510         {
1511                 leadx = mo->x-mo->radius;
1512         }
1513         if(mo->momy > 0)
1514         {
1515                 leady = mo->y+mo->radius;
1516         }
1517         else
1518         {
1519                 leady = mo->y-mo->radius;
1520         }
1521         bestslidefrac = FRACUNIT+1;
1522         P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy,
1523                 PT_ADDLINES, PTR_BounceTraverse);
1524
1525         side = P_PointOnLineSide(mo->x, mo->y, bestslideline);
1526         lineangle = R_PointToAngle2(0, 0, bestslideline->dx,
1527                 bestslideline->dy);
1528         if(side == 1)
1529                 lineangle += ANG180;
1530         moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy);
1531         deltaangle = (2*lineangle)-moveangle;
1532 //      if (deltaangle > ANG180)
1533 //              deltaangle += ANG180;
1534 //              I_Error ("SlideLine: ang>ANG180");
1535
1536         lineangle >>= ANGLETOFINESHIFT;
1537         deltaangle >>= ANGLETOFINESHIFT;
1538
1539         movelen = P_AproxDistance(mo->momx, mo->momy);
1540         movelen = FixedMul(movelen, 0.75*FRACUNIT); // friction
1541         if (movelen < FRACUNIT) movelen = 2*FRACUNIT;
1542         mo->momx = FixedMul(movelen, finecosine[deltaangle]);
1543         mo->momy = FixedMul(movelen, finesine[deltaangle]);
1544 }
1545
1546
1547 /*
1548 ==============================================================================
1549
1550                                                         P_LineAttack
1551
1552 ==============================================================================
1553 */
1554
1555
1556 mobj_t *PuffSpawned;
1557 mobj_t          *linetarget;                    // who got hit (or NULL)
1558 mobj_t          *shootthing;
1559 fixed_t         shootz;                                 // height if not aiming up or down
1560                                                                         // ???: use slope for monsters?
1561 int                     la_damage;
1562 fixed_t         attackrange;
1563
1564 fixed_t         aimslope;
1565
1566 extern  fixed_t         topslope, bottomslope;  // slopes to top and bottom of target
1567
1568 /*
1569 ===============================================================================
1570 =
1571 = PTR_AimTraverse
1572 =
1573 = Sets linetaget and aimslope when a target is aimed at
1574 ===============================================================================
1575 */
1576
1577 boolean         PTR_AimTraverse (intercept_t *in)
1578 {
1579         line_t          *li;
1580         mobj_t          *th;
1581         fixed_t         slope, thingtopslope, thingbottomslope;
1582         fixed_t         dist;
1583
1584         if (in->isaline)
1585         {
1586                 li = in->d.line;
1587                 if ( !(li->flags & ML_TWOSIDED) )
1588                         return false;           // stop
1589 //
1590 // crosses a two sided line
1591 // a two sided line will restrict the possible target ranges
1592                 P_LineOpening (li);
1593
1594                 if (openbottom >= opentop)
1595                         return false;           // stop
1596
1597                 dist = FixedMul (attackrange, in->frac);
1598
1599                 if (li->frontsector->floorheight != li->backsector->floorheight)
1600                 {
1601                         slope = FixedDiv (openbottom - shootz , dist);
1602                         if (slope > bottomslope)
1603                                 bottomslope = slope;
1604                 }
1605
1606                 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1607                 {
1608                         slope = FixedDiv (opentop - shootz , dist);
1609                         if (slope < topslope)
1610                                 topslope = slope;
1611                 }
1612
1613                 if (topslope <= bottomslope)
1614                         return false;           // stop
1615
1616                 return true;            // shot continues
1617         }
1618
1619 //
1620 // shoot a thing
1621 //
1622         th = in->d.thing;
1623         if (th == shootthing)
1624                 return true;            // can't shoot self
1625         if(!(th->flags&MF_SHOOTABLE))
1626         { // corpse or something
1627                 return true;
1628         }
1629         if(th->player && netgame && !deathmatch)
1630         { // don't aim at fellow co-op players
1631                 return true;
1632         }
1633
1634 // check angles to see if the thing can be aimed at
1635
1636         dist = FixedMul (attackrange, in->frac);
1637         thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1638         if (thingtopslope < bottomslope)
1639                 return true;            // shot over the thing
1640         thingbottomslope = FixedDiv (th->z - shootz, dist);
1641         if (thingbottomslope > topslope)
1642                 return true;            // shot under the thing
1643
1644 //
1645 // this thing can be hit!
1646 //
1647         if (thingtopslope > topslope)
1648                 thingtopslope = topslope;
1649         if (thingbottomslope < bottomslope)
1650                 thingbottomslope = bottomslope;
1651
1652         aimslope = (thingtopslope+thingbottomslope)/2;
1653         linetarget = th;
1654
1655         return false;                   // don't go any farther
1656 }
1657
1658
1659 /*
1660 ==============================================================================
1661 =
1662 = PTR_ShootTraverse
1663 =
1664 ==============================================================================
1665 */
1666
1667 boolean         PTR_ShootTraverse (intercept_t *in)
1668 {
1669         fixed_t         x,y,z;
1670         fixed_t         frac;
1671         line_t          *li;
1672         mobj_t          *th;
1673         fixed_t         slope;
1674         fixed_t         dist;
1675         fixed_t         thingtopslope, thingbottomslope;
1676
1677         extern mobj_t LavaInflictor;
1678
1679         if (in->isaline)
1680         {
1681                 li = in->d.line;
1682                 if (li->special)
1683                 {
1684                         P_ActivateLine(li, shootthing, 0, SPAC_IMPACT);
1685 //                      P_ShootSpecialLine (shootthing, li);
1686                 }
1687                 if ( !(li->flags & ML_TWOSIDED) )
1688                         goto hitline;
1689
1690 //
1691 // crosses a two sided line
1692 //
1693                 P_LineOpening (li);
1694
1695                 dist = FixedMul (attackrange, in->frac);
1696
1697                 if (li->frontsector->floorheight != li->backsector->floorheight)
1698                 {
1699                         slope = FixedDiv (openbottom - shootz , dist);
1700                         if (slope > aimslope)
1701                                 goto hitline;
1702                 }
1703
1704                 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1705                 {
1706                         slope = FixedDiv (opentop - shootz , dist);
1707                         if (slope < aimslope)
1708                                 goto hitline;
1709                 }
1710
1711                 return true;            // shot continues
1712 //
1713 // hit line
1714 //
1715 hitline:
1716                 // position a bit closer
1717                 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1718                 x = trace.x + FixedMul (trace.dx, frac);
1719                 y = trace.y + FixedMul (trace.dy, frac);
1720                 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1721
1722                 if (li->frontsector->ceilingpic == skyflatnum)
1723                 {
1724                         if (z > li->frontsector->ceilingheight)
1725                                 return false;           // don't shoot the sky!
1726                         if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1727                                 return false;           // it's a sky hack wall
1728                 }
1729
1730                 P_SpawnPuff (x,y,z);
1731                 return false;                   // don't go any farther
1732         }
1733
1734 //
1735 // shoot a thing
1736 //
1737         th = in->d.thing;
1738         if (th == shootthing)
1739                 return true;            // can't shoot self
1740         if (!(th->flags&MF_SHOOTABLE))
1741                 return true;            // corpse or something
1742
1743 //
1744 // check for physical attacks on a ghost
1745 //
1746 /*  FIX:  Impliment Heretic 2 weapons here
1747         if(th->flags&MF_SHADOW && shootthing->player->readyweapon == wp_staff)
1748         {
1749                 return(true);
1750         }
1751 */
1752
1753 // check angles to see if the thing can be aimed at
1754         dist = FixedMul (attackrange, in->frac);
1755         thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1756         if (thingtopslope < aimslope)
1757                 return true;            // shot over the thing
1758         thingbottomslope = FixedDiv (th->z - shootz, dist);
1759         if (thingbottomslope > aimslope)
1760                 return true;            // shot under the thing
1761
1762 //
1763 // hit thing
1764 //
1765         // position a bit closer
1766         frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1767         x = trace.x + FixedMul(trace.dx, frac);
1768         y = trace.y + FixedMul(trace.dy, frac);
1769         z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange));
1770         P_SpawnPuff(x, y, z);
1771         if(la_damage)
1772         {
1773                 if (!(in->d.thing->flags&MF_NOBLOOD) &&
1774                         !(in->d.thing->flags2&MF2_INVULNERABLE))
1775                 {
1776                         if(PuffType == MT_AXEPUFF || PuffType == MT_AXEPUFF_GLOW)
1777                         {
1778                                 P_BloodSplatter2(x, y, z, in->d.thing);
1779                         }
1780                         if(P_Random() < 192)
1781                         {
1782                                 P_BloodSplatter(x, y, z, in->d.thing);
1783                         }
1784                 }
1785                 if(PuffType == MT_FLAMEPUFF2)
1786                 { // Cleric FlameStrike does fire damage
1787                         P_DamageMobj(th, &LavaInflictor, shootthing, la_damage);
1788                 }
1789                 else
1790                 { 
1791                         P_DamageMobj(th, shootthing, shootthing, la_damage);
1792                 }
1793         }
1794         return(false); // don't go any farther
1795 }
1796
1797 /*
1798 =================
1799 =
1800 = P_AimLineAttack
1801 =
1802 =================
1803 */
1804
1805 fixed_t P_AimLineAttack (mobj_t *t1, angle_t angle, fixed_t distance)
1806 {
1807         fixed_t         x2, y2;
1808
1809         angle >>= ANGLETOFINESHIFT;
1810         shootthing = t1;
1811         x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1812         y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1813         shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1814         topslope = 100*FRACUNIT/160;    // can't shoot outside view angles
1815         bottomslope = -100*FRACUNIT/160;
1816         attackrange = distance;
1817         linetarget = NULL;
1818
1819         P_PathTraverse ( t1->x, t1->y, x2, y2
1820                 , PT_ADDLINES|PT_ADDTHINGS, PTR_AimTraverse );
1821
1822         if (linetarget)
1823                 return aimslope;
1824         return 0;
1825 }
1826
1827
1828
1829 /*
1830 =================
1831 =
1832 = P_LineAttack
1833 =
1834 = if damage == 0, it is just a test trace that will leave linetarget set
1835 =
1836 =================
1837 */
1838
1839 void P_LineAttack (mobj_t *t1, angle_t angle, fixed_t distance, fixed_t slope, int damage)
1840 {
1841         fixed_t         x2, y2;
1842
1843         angle >>= ANGLETOFINESHIFT;
1844         shootthing = t1;
1845         la_damage = damage;
1846         x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1847         y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1848         shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1849         shootz -= t1->floorclip;
1850         attackrange = distance;
1851         aimslope = slope;
1852
1853         if(P_PathTraverse(t1->x, t1->y, x2, y2, PT_ADDLINES|PT_ADDTHINGS,
1854                 PTR_ShootTraverse))
1855         {
1856                 switch(PuffType)
1857                 {
1858                         case MT_PUNCHPUFF:
1859                                 S_StartSound(t1, SFX_FIGHTER_PUNCH_MISS);
1860                                 break;
1861                         case MT_HAMMERPUFF:
1862                         case MT_AXEPUFF:
1863                         case MT_AXEPUFF_GLOW:
1864                                 S_StartSound(t1, SFX_FIGHTER_HAMMER_MISS);
1865                                 break;
1866                         case MT_FLAMEPUFF:
1867                                 P_SpawnPuff(x2, y2, shootz+FixedMul(slope, distance));
1868                                 break;
1869                         default:
1870                                 break;
1871                 }
1872         }
1873 }
1874
1875 /*
1876 ==============================================================================
1877
1878                                                         USE LINES
1879
1880 ==============================================================================
1881 */
1882
1883 mobj_t          *usething;
1884
1885 boolean         PTR_UseTraverse (intercept_t *in)
1886 {
1887         int sound;
1888         fixed_t pheight;
1889
1890         if (!in->d.line->special)
1891         {
1892                 P_LineOpening (in->d.line);
1893                 if (openrange <= 0)
1894                 {
1895                         if(usething->player)
1896                         {
1897                                 switch(usething->player->class)
1898                                 {
1899                                         case PCLASS_FIGHTER:
1900                                                 sound = SFX_PLAYER_FIGHTER_FAILED_USE;
1901                                                 break;
1902                                         case PCLASS_CLERIC:
1903                                                 sound = SFX_PLAYER_CLERIC_FAILED_USE;
1904                                                 break;
1905                                         case PCLASS_MAGE:
1906                                                 sound = SFX_PLAYER_MAGE_FAILED_USE;
1907                                                 break;
1908                                         case PCLASS_PIG:
1909                                                 sound = SFX_PIG_ACTIVE1;
1910                                                 break;
1911                                         default:
1912                                                 sound = SFX_NONE;
1913                                                 break;
1914                                 }
1915                                 S_StartSound(usething, sound);
1916                         }
1917                         return false;   // can't use through a wall
1918                 }
1919                 if(usething->player)
1920                 {
1921                         pheight = usething->z + (usething->height/2);
1922                         if ((opentop < pheight) || (openbottom > pheight))
1923                         {
1924                                 switch(usething->player->class)
1925                                 {
1926                                         case PCLASS_FIGHTER:
1927                                                 sound = SFX_PLAYER_FIGHTER_FAILED_USE;
1928                                                 break;
1929                                         case PCLASS_CLERIC:
1930                                                 sound = SFX_PLAYER_CLERIC_FAILED_USE;
1931                                                 break;
1932                                         case PCLASS_MAGE:
1933                                                 sound = SFX_PLAYER_MAGE_FAILED_USE;
1934                                                 break;
1935                                         case PCLASS_PIG:
1936                                                 sound = SFX_PIG_ACTIVE1;
1937                                                 break;
1938                                         default:
1939                                                 sound = SFX_NONE;
1940                                                 break;
1941                                 }
1942                                 S_StartSound(usething, sound);
1943                         }
1944                 }
1945                 return true ;           // not a special line, but keep checking
1946         }
1947
1948         if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1949                 return false;           // don't use back sides
1950
1951 //      P_UseSpecialLine (usething, in->d.line);
1952         P_ActivateLine(in->d.line, usething, 0, SPAC_USE);
1953
1954         return false;                   // can't use for than one special line in a row
1955 }
1956
1957
1958 /*
1959 ================
1960 =
1961 = P_UseLines
1962 =
1963 = Looks for special lines in front of the player to activate
1964 ================
1965 */
1966
1967 void P_UseLines (player_t *player)
1968 {
1969         int                     angle;
1970         fixed_t         x1, y1, x2, y2;
1971
1972         usething = player->mo;
1973
1974         angle = player->mo->angle >> ANGLETOFINESHIFT;
1975         x1 = player->mo->x;
1976         y1 = player->mo->y;
1977         x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1978         y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1979
1980         P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1981 }
1982
1983 //==========================================================================
1984 //
1985 // PTR_PuzzleItemTraverse
1986 //
1987 //==========================================================================
1988
1989 #define USE_PUZZLE_ITEM_SPECIAL 129
1990
1991 static mobj_t *PuzzleItemUser;
1992 static int PuzzleItemType;
1993 static boolean PuzzleActivated;
1994
1995 boolean PTR_PuzzleItemTraverse(intercept_t *in)
1996 {
1997         mobj_t *mobj;
1998         int sound;
1999
2000         if(in->isaline)
2001         { // Check line
2002                 if(in->d.line->special != USE_PUZZLE_ITEM_SPECIAL)
2003                 {
2004                         P_LineOpening(in->d.line);
2005                         if(openrange <= 0)
2006                         {
2007                                 sound = SFX_NONE;
2008                                 if(PuzzleItemUser->player)
2009                                 {
2010                                         switch(PuzzleItemUser->player->class)
2011                                         {
2012                                                 case PCLASS_FIGHTER:
2013                                                         sound = SFX_PUZZLE_FAIL_FIGHTER;
2014                                                         break;
2015                                                 case PCLASS_CLERIC:
2016                                                         sound = SFX_PUZZLE_FAIL_CLERIC;
2017                                                         break;
2018                                                 case PCLASS_MAGE:
2019                                                         sound = SFX_PUZZLE_FAIL_MAGE;
2020                                                         break;
2021                                                 default:
2022                                                         sound = SFX_NONE;
2023                                                         break;
2024                                         }
2025                                 }
2026                                 S_StartSound(PuzzleItemUser, sound);
2027                                 return false; // can't use through a wall
2028                         }
2029                         return true; // Continue searching
2030                 }
2031                 if(P_PointOnLineSide(PuzzleItemUser->x, PuzzleItemUser->y,
2032                         in->d.line) == 1)
2033                 { // Don't use back sides
2034                         return false;
2035                 }
2036                 if(PuzzleItemType != in->d.line->arg1)
2037                 { // Item type doesn't match
2038                         return false;
2039                 }
2040                 P_StartACS(in->d.line->arg2, 0, &in->d.line->arg3,
2041                         PuzzleItemUser, in->d.line, 0);
2042                 in->d.line->special = 0;
2043                 PuzzleActivated = true;
2044                 return false; // Stop searching
2045         }
2046         // Check thing
2047         mobj = in->d.thing;
2048         if(mobj->special != USE_PUZZLE_ITEM_SPECIAL)
2049         { // Wrong special
2050                 return true;
2051         }
2052         if(PuzzleItemType != mobj->args[0])
2053         { // Item type doesn't match
2054                 return true;
2055         }
2056         P_StartACS(mobj->args[1], 0, &mobj->args[2], PuzzleItemUser, NULL, 0);
2057         mobj->special = 0;
2058         PuzzleActivated = true;
2059         return false; // Stop searching
2060 }
2061
2062 //==========================================================================
2063 //
2064 // P_UsePuzzleItem
2065 //
2066 // Returns true if the puzzle item was used on a line or a thing.
2067 //
2068 //==========================================================================
2069
2070 boolean P_UsePuzzleItem(player_t *player, int itemType)
2071 {
2072         int angle;
2073         fixed_t x1, y1, x2, y2;
2074
2075         PuzzleItemType = itemType;
2076         PuzzleItemUser = player->mo;
2077         PuzzleActivated = false;
2078         angle = player->mo->angle>>ANGLETOFINESHIFT;
2079         x1 = player->mo->x;
2080         y1 = player->mo->y;
2081         x2 = x1+(USERANGE>>FRACBITS)*finecosine[angle];
2082         y2 = y1+(USERANGE>>FRACBITS)*finesine[angle];
2083         P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS,
2084                 PTR_PuzzleItemTraverse);
2085         return PuzzleActivated;
2086 }
2087
2088 /*
2089 ==============================================================================
2090
2091                                                         RADIUS ATTACK
2092
2093 ==============================================================================
2094 */
2095
2096 mobj_t *bombsource;
2097 mobj_t *bombspot;
2098 int bombdamage;
2099 int bombdistance;
2100 boolean DamageSource;
2101
2102 /*
2103 =================
2104 =
2105 = PIT_RadiusAttack
2106 =
2107 = Source is the creature that casued the explosion at spot
2108 =================
2109 */
2110
2111 boolean PIT_RadiusAttack (mobj_t *thing)
2112 {
2113         fixed_t dx, dy, dist;
2114         int damage;
2115
2116         if(!(thing->flags&MF_SHOOTABLE))
2117         {
2118                 return true;
2119         }
2120 //      if(thing->flags2&MF2_BOSS)
2121 //      {       // Bosses take no damage from PIT_RadiusAttack
2122 //              return(true);
2123 //      }
2124         if(!DamageSource && thing == bombsource)
2125         { // don't damage the source of the explosion
2126                 return true;
2127         }
2128         if(abs((thing->z-bombspot->z)>>FRACBITS) > 2*bombdistance)
2129         { // too high/low
2130                 return true;
2131         }
2132         dx = abs(thing->x-bombspot->x);
2133         dy = abs(thing->y-bombspot->y);
2134         dist = dx > dy ? dx : dy;
2135         dist = (dist-thing->radius)>>FRACBITS;
2136         if(dist < 0)
2137         {
2138                 dist = 0;
2139         }
2140         if(dist >= bombdistance)
2141         { // Out of range
2142                 return true;
2143         }
2144         if(P_CheckSight(thing, bombspot))
2145         { // OK to damage, target is in direct path
2146                 damage = (bombdamage*(bombdistance-dist)/bombdistance)+1;
2147                 if(thing->player)
2148                 {
2149                         damage >>= 2;
2150                 }
2151                 P_DamageMobj(thing, bombspot, bombsource, damage);
2152         }
2153         return(true);
2154 }
2155
2156 /*
2157 =================
2158 =
2159 = P_RadiusAttack
2160 =
2161 = Source is the creature that caused the explosion at spot
2162 =================
2163 */
2164
2165 void P_RadiusAttack (mobj_t *spot, mobj_t *source, int damage, int distance,
2166         boolean damageSource)
2167 {
2168         int                     x,y, xl, xh, yl, yh;
2169         fixed_t         dist;
2170
2171         dist = (distance+MAXRADIUS)<<FRACBITS;
2172         yh = (spot->y+dist-bmaporgy)>>MAPBLOCKSHIFT;
2173         yl = (spot->y-dist-bmaporgy)>>MAPBLOCKSHIFT;
2174         xh = (spot->x+dist-bmaporgx)>>MAPBLOCKSHIFT;
2175         xl = (spot->x-dist-bmaporgx)>>MAPBLOCKSHIFT;
2176         bombspot = spot;
2177         bombsource = source;
2178         bombdamage = damage;
2179         bombdistance = distance;
2180         DamageSource = damageSource;
2181         for (y = yl; y <= yh; y++)
2182         {
2183                 for (x = xl; x <= xh; x++)
2184                 {
2185                         P_BlockThingsIterator(x, y, PIT_RadiusAttack);
2186                 }
2187         }
2188 }
2189
2190 /*
2191 ==============================================================================
2192
2193                                                 SECTOR HEIGHT CHANGING
2194
2195 = After modifying a sectors floor or ceiling height, call this
2196 = routine to adjust the positions of all things that touch the
2197 = sector.
2198 =
2199 = If anything doesn't fit anymore, true will be returned.
2200 = If crunch is true, they will take damage as they are being crushed
2201 = If Crunch is false, you should set the sector height back the way it
2202 = was and call P_ChangeSector again to undo the changes
2203 ==============================================================================
2204 */
2205
2206 int         crushchange;
2207 boolean         nofit;
2208
2209 /*
2210 ===============
2211 =
2212 = PIT_ChangeSector
2213 =
2214 ===============
2215 */
2216
2217 boolean PIT_ChangeSector (mobj_t *thing)
2218 {
2219         mobj_t          *mo;
2220
2221         if (P_ThingHeightClip (thing))
2222                 return true;            // keep checking
2223
2224         // crunch bodies to giblets
2225         if ((thing->flags&MF_CORPSE) && (thing->health <= 0))
2226         {
2227                 if (thing->flags&MF_NOBLOOD)
2228                 {
2229                         P_RemoveMobj (thing);
2230                 }
2231                 else
2232                 {
2233                         if (thing->state != &states[S_GIBS1])
2234                         {
2235                                 P_SetMobjState (thing, S_GIBS1);
2236                                 thing->height = 0;
2237                                 thing->radius = 0;
2238                                 S_StartSound(thing, SFX_PLAYER_FALLING_SPLAT);
2239                         }
2240                 }
2241                 return true;            // keep checking
2242         }
2243
2244         // crunch dropped items
2245         if (thing->flags2&MF2_DROPPED)
2246         {
2247                 P_RemoveMobj (thing);
2248                 return true;            // keep checking
2249         }
2250
2251         if (! (thing->flags & MF_SHOOTABLE) )
2252                 return true;                            // assume it is bloody gibs or something
2253
2254         nofit = true;
2255         if (crushchange && !(leveltime&3))
2256         {
2257                 P_DamageMobj(thing, NULL, NULL, crushchange);
2258                 // spray blood in a random direction
2259                 if ((!(thing->flags&MF_NOBLOOD)) &&
2260                         (!(thing->flags2&MF2_INVULNERABLE)))
2261                 {
2262                         mo = P_SpawnMobj (thing->x, thing->y, thing->z + thing->height/2, 
2263                                 MT_BLOOD);
2264                         mo->momx = (P_Random() - P_Random ())<<12;
2265                         mo->momy = (P_Random() - P_Random ())<<12;
2266                 }
2267         }
2268
2269         return true;            // keep checking (crush other things)
2270 }
2271
2272 /*
2273 ===============
2274 =
2275 = P_ChangeSector
2276 =
2277 ===============
2278 */
2279
2280 boolean P_ChangeSector (sector_t *sector, int crunch)
2281 {
2282         int                     x,y;
2283
2284         nofit = false;
2285         crushchange = crunch;
2286
2287 // recheck heights for all things near the moving sector
2288
2289         for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
2290                 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
2291                         P_BlockThingsIterator (x, y, PIT_ChangeSector);
2292
2293
2294         return nofit;
2295 }
2296