]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/p_map.c
games/doom: fix unterminated comment causing sound bugs (from qu7uux)
[plan9front.git] / sys / src / games / doom / p_map.c
1 // Emacs style mode select   -*- C++ -*- 
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //      Movement, collision handling.
21 //      Shooting and aiming.
22 //
23 //-----------------------------------------------------------------------------
24
25 static const char
26 rcsid[] = "$Id: p_map.c,v 1.5 1997/02/03 22:45:11 b1 Exp $";
27
28 #include "m_bbox.h"
29 #include "m_random.h"
30 #include "i_system.h"
31
32 #include "doomdef.h"
33 #include "p_local.h"
34
35 #include "s_sound.h"
36
37 // State.
38 #include "doomstat.h"
39 #include "r_state.h"
40 // Data.
41 #include "sounds.h"
42
43
44 fixed_t         tmbbox[4];
45 mobj_t*         tmthing;
46 int             tmflags;
47 fixed_t         tmx;
48 fixed_t         tmy;
49
50
51 // If "floatok" true, move would be ok
52 // if within "tmfloorz - tmceilingz".
53 boolean         floatok;
54
55 fixed_t         tmfloorz;
56 fixed_t         tmceilingz;
57 fixed_t         tmdropoffz;
58
59 // keep track of the line that lowers the ceiling,
60 // so missiles don't explode against sky hack walls
61 line_t*         ceilingline;
62
63 // keep track of special lines as they are hit,
64 // but don't process them until the move is proven valid
65 #define MAXSPECIALCROSS         8
66
67 line_t*         spechit[MAXSPECIALCROSS];
68 int             numspechit;
69
70
71
72 //
73 // TELEPORT MOVE
74 // 
75
76 //
77 // PIT_StompThing
78 //
79 boolean PIT_StompThing (mobj_t* thing)
80 {
81     fixed_t     blockdist;
82                 
83     if (!(thing->flags & MF_SHOOTABLE) )
84         return true;
85                 
86     blockdist = thing->radius + tmthing->radius;
87     
88     if ( abs(thing->x - tmx) >= blockdist
89          || abs(thing->y - tmy) >= blockdist )
90     {
91         // didn't hit it
92         return true;
93     }
94     
95     // don't clip against self
96     if (thing == tmthing)
97         return true;
98     
99     // monsters don't stomp things except on boss level
100     if ( !tmthing->player && gamemap != 30)
101         return false;   
102                 
103     P_DamageMobj (thing, tmthing, tmthing, 10000);
104         
105     return true;
106 }
107
108
109 //
110 // P_TeleportMove
111 //
112 boolean
113 P_TeleportMove
114 ( mobj_t*       thing,
115   fixed_t       x,
116   fixed_t       y )
117 {
118     int                 xl;
119     int                 xh;
120     int                 yl;
121     int                 yh;
122     int                 bx;
123     int                 by;
124     
125     subsector_t*        newsubsec;
126     
127     // kill anything occupying the position
128     tmthing = thing;
129     tmflags = thing->flags;
130         
131     tmx = x;
132     tmy = y;
133         
134     tmbbox[BOXTOP] = y + tmthing->radius;
135     tmbbox[BOXBOTTOM] = y - tmthing->radius;
136     tmbbox[BOXRIGHT] = x + tmthing->radius;
137     tmbbox[BOXLEFT] = x - tmthing->radius;
138
139     newsubsec = R_PointInSubsector (x,y);
140     ceilingline = NULL;
141     
142     // The base floor/ceiling is from the subsector
143     // that contains the point.
144     // Any contacted lines the step closer together
145     // will adjust them.
146     tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
147     tmceilingz = newsubsec->sector->ceilingheight;
148                         
149     validcount++;
150     numspechit = 0;
151     
152     // stomp on any things contacted
153     xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
154     xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
155     yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
156     yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
157
158     for (bx=xl ; bx<=xh ; bx++)
159         for (by=yl ; by<=yh ; by++)
160             if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
161                 return false;
162     
163     // the move is ok,
164     // so link the thing into its new position
165     P_UnsetThingPosition (thing);
166
167     thing->floorz = tmfloorz;
168     thing->ceilingz = tmceilingz;       
169     thing->x = x;
170     thing->y = y;
171
172     P_SetThingPosition (thing);
173         
174     return true;
175 }
176
177
178 //
179 // MOVEMENT ITERATOR FUNCTIONS
180 //
181
182
183 //
184 // PIT_CheckLine
185 // Adjusts tmfloorz and tmceilingz as lines are contacted
186 //
187 boolean PIT_CheckLine (line_t* ld)
188 {
189     if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
190         || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
191         || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
192         || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
193         return true;
194
195     if (P_BoxOnLineSide (tmbbox, ld) != -1)
196         return true;
197                 
198     // A line has been hit
199     
200     // The moving thing's destination position will cross
201     // the given line.
202     // If this should not be allowed, return false.
203     // If the line is special, keep track of it
204     // to process later if the move is proven ok.
205     // NOTE: specials are NOT sorted by order,
206     // so two special lines that are only 8 pixels apart
207     // could be crossed in either order.
208     
209     if (!ld->backsector)
210         return false;           // one sided line
211                 
212     if (!(tmthing->flags & MF_MISSILE) )
213     {
214         if ( ld->flags & ML_BLOCKING )
215             return false;       // explicitly blocking everything
216
217         if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )
218             return false;       // block monsters only
219     }
220
221     // set openrange, opentop, openbottom
222     P_LineOpening (ld); 
223         
224     // adjust floor / ceiling heights
225     if (opentop < tmceilingz)
226     {
227         tmceilingz = opentop;
228         ceilingline = ld;
229     }
230
231     if (openbottom > tmfloorz)
232         tmfloorz = openbottom;  
233
234     if (lowfloor < tmdropoffz)
235         tmdropoffz = lowfloor;
236                 
237     // if contacted a special line, add it to the list
238     if (ld->special)
239     {
240         spechit[numspechit] = ld;
241         numspechit++;
242     }
243
244     return true;
245 }
246
247 //
248 // PIT_CheckThing
249 //
250 boolean PIT_CheckThing (mobj_t* thing)
251 {
252     fixed_t             blockdist;
253     boolean             solid;
254     int                 damage;
255                 
256     if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
257         return true;
258     
259     blockdist = thing->radius + tmthing->radius;
260
261     if ( abs(thing->x - tmx) >= blockdist
262          || abs(thing->y - tmy) >= blockdist )
263     {
264         // didn't hit it
265         return true;    
266     }
267     
268     // don't clip against self
269     if (thing == tmthing)
270         return true;
271     
272     // check for skulls slamming into things
273     if (tmthing->flags & MF_SKULLFLY)
274     {
275         damage = ((P_Random()%8)+1)*tmthing->info->damage;
276         
277         P_DamageMobj (thing, tmthing, tmthing, damage);
278         
279         tmthing->flags &= ~MF_SKULLFLY;
280         tmthing->momx = tmthing->momy = tmthing->momz = 0;
281         
282         P_SetMobjState (tmthing, tmthing->info->spawnstate);
283         
284         return false;           // stop moving
285     }
286
287     
288     // missiles can hit other things
289     if (tmthing->flags & MF_MISSILE)
290     {
291         // see if it went over / under
292         if (tmthing->z > thing->z + thing->height)
293             return true;                // overhead
294         if (tmthing->z+tmthing->height < thing->z)
295             return true;                // underneath
296                 
297         if (tmthing->target && (
298             tmthing->target->type == thing->type || 
299             (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
300             (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
301         {
302             // Don't hit same species as originator.
303             if (thing == tmthing->target)
304                 return true;
305
306             if (thing->type != MT_PLAYER)
307             {
308                 // Explode, but do no damage.
309                 // Let players missile other players.
310                 return false;
311             }
312         }
313         
314         if (! (thing->flags & MF_SHOOTABLE) )
315         {
316             // didn't do any damage
317             return !(thing->flags & MF_SOLID);  
318         }
319         
320         // damage / explode
321         damage = ((P_Random()%8)+1)*tmthing->info->damage;
322         P_DamageMobj (thing, tmthing, tmthing->target, damage);
323
324         // don't traverse any more
325         return false;                           
326     }
327     
328     // check for special pickup
329     if (thing->flags & MF_SPECIAL)
330     {
331         solid = thing->flags&MF_SOLID;
332         if (tmflags&MF_PICKUP)
333         {
334             // can remove thing
335             P_TouchSpecialThing (thing, tmthing);
336         }
337         return !solid;
338     }
339         
340     return !(thing->flags & MF_SOLID);
341 }
342
343
344 //
345 // MOVEMENT CLIPPING
346 //
347
348 //
349 // P_CheckPosition
350 // This is purely informative, nothing is modified
351 // (except things picked up).
352 // 
353 // in:
354 //  a mobj_t (can be valid or invalid)
355 //  a position to be checked
356 //   (doesn't need to be related to the mobj_t->x,y)
357 //
358 // during:
359 //  special things are touched if MF_PICKUP
360 //  early out on solid lines?
361 //
362 // out:
363 //  newsubsec
364 //  floorz
365 //  ceilingz
366 //  tmdropoffz
367 //   the lowest point contacted
368 //   (monsters won't move to a dropoff)
369 //  speciallines[]
370 //  numspeciallines
371 //
372 boolean
373 P_CheckPosition
374 ( mobj_t*       thing,
375   fixed_t       x,
376   fixed_t       y )
377 {
378     int                 xl;
379     int                 xh;
380     int                 yl;
381     int                 yh;
382     int                 bx;
383     int                 by;
384     subsector_t*        newsubsec;
385
386     tmthing = thing;
387     tmflags = thing->flags;
388         
389     tmx = x;
390     tmy = y;
391         
392     tmbbox[BOXTOP] = y + tmthing->radius;
393     tmbbox[BOXBOTTOM] = y - tmthing->radius;
394     tmbbox[BOXRIGHT] = x + tmthing->radius;
395     tmbbox[BOXLEFT] = x - tmthing->radius;
396
397     newsubsec = R_PointInSubsector (x,y);
398     ceilingline = NULL;
399     
400     // The base floor / ceiling is from the subsector
401     // that contains the point.
402     // Any contacted lines the step closer together
403     // will adjust them.
404     tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
405     tmceilingz = newsubsec->sector->ceilingheight;
406                         
407     validcount++;
408     numspechit = 0;
409
410     if ( tmflags & MF_NOCLIP )
411         return true;
412     
413     // Check things first, possibly picking things up.
414     // The bounding box is extended by MAXRADIUS
415     // because mobj_ts are grouped into mapblocks
416     // based on their origin point, and can overlap
417     // into adjacent blocks by up to MAXRADIUS units.
418     xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
419     xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
420     yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
421     yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
422
423     for (bx=xl ; bx<=xh ; bx++)
424         for (by=yl ; by<=yh ; by++)
425             if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
426                 return false;
427     
428     // check lines
429     xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
430     xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
431     yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
432     yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
433
434     for (bx=xl ; bx<=xh ; bx++)
435         for (by=yl ; by<=yh ; by++)
436             if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
437                 return false;
438
439     return true;
440 }
441
442
443 //
444 // P_TryMove
445 // Attempt to move to a new position,
446 // crossing special lines unless MF_TELEPORT is set.
447 //
448 boolean
449 P_TryMove
450 ( mobj_t*       thing,
451   fixed_t       x,
452   fixed_t       y )
453 {
454     fixed_t     oldx;
455     fixed_t     oldy;
456     int         side;
457     int         oldside;
458     line_t*     ld;
459
460     floatok = false;
461     if (!P_CheckPosition (thing, x, y))
462         return false;           // solid wall or thing
463     
464     if ( !(thing->flags & MF_NOCLIP) )
465     {
466         if (tmceilingz - tmfloorz < thing->height)
467             return false;       // doesn't fit
468
469         floatok = true;
470         
471         if ( !(thing->flags&MF_TELEPORT) 
472              &&tmceilingz - thing->z < thing->height)
473             return false;       // mobj must lower itself to fit
474
475         if ( !(thing->flags&MF_TELEPORT)
476              && tmfloorz - thing->z > 24*FRACUNIT )
477             return false;       // too big a step up
478
479         if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
480              && tmfloorz - tmdropoffz > 24*FRACUNIT )
481             return false;       // don't stand over a dropoff
482     }
483     
484     // the move is ok,
485     // so link the thing into its new position
486     P_UnsetThingPosition (thing);
487
488     oldx = thing->x;
489     oldy = thing->y;
490     thing->floorz = tmfloorz;
491     thing->ceilingz = tmceilingz;       
492     thing->x = x;
493     thing->y = y;
494
495     P_SetThingPosition (thing);
496     
497     // if any special lines were hit, do the effect
498     if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
499     {
500         while (numspechit--)
501         {
502             // see if the line was crossed
503             ld = spechit[numspechit];
504             side = P_PointOnLineSide (thing->x, thing->y, ld);
505             oldside = P_PointOnLineSide (oldx, oldy, ld);
506             if (side != oldside)
507             {
508                 if (ld->special)
509                     P_CrossSpecialLine (ld-lines, oldside, thing);
510             }
511         }
512     }
513
514     return true;
515 }
516
517
518 //
519 // P_ThingHeightClip
520 // Takes a valid thing and adjusts the thing->floorz,
521 // thing->ceilingz, and possibly thing->z.
522 // This is called for all nearby monsters
523 // whenever a sector changes height.
524 // If the thing doesn't fit,
525 // the z will be set to the lowest value
526 // and false will be returned.
527 //
528 boolean P_ThingHeightClip (mobj_t* thing)
529 {
530     boolean             onfloor;
531         
532     onfloor = (thing->z == thing->floorz);
533         
534     P_CheckPosition (thing, thing->x, thing->y);        
535     // what about stranding a monster partially off an edge?
536         
537     thing->floorz = tmfloorz;
538     thing->ceilingz = tmceilingz;
539         
540     if (onfloor)
541     {
542         // walking monsters rise and fall with the floor
543         thing->z = thing->floorz;
544     }
545     else
546     {
547         // don't adjust a floating monster unless forced to
548         if (thing->z+thing->height > thing->ceilingz)
549             thing->z = thing->ceilingz - thing->height;
550     }
551         
552     if (thing->ceilingz - thing->floorz < thing->height)
553         return false;
554                 
555     return true;
556 }
557
558
559
560 //
561 // SLIDE MOVE
562 // Allows the player to slide along any angled walls.
563 //
564 fixed_t         bestslidefrac;
565 fixed_t         secondslidefrac;
566
567 line_t*         bestslideline;
568 line_t*         secondslideline;
569
570 mobj_t*         slidemo;
571
572 fixed_t         tmxmove;
573 fixed_t         tmymove;
574
575
576
577 //
578 // P_HitSlideLine
579 // Adjusts the xmove / ymove
580 // so that the next move will slide along the wall.
581 //
582 void P_HitSlideLine (line_t* ld)
583 {
584     int                 side;
585
586     angle_t             lineangle;
587     angle_t             moveangle;
588     angle_t             deltaangle;
589     
590     fixed_t             movelen;
591     fixed_t             newlen;
592         
593         
594     if (ld->slopetype == ST_HORIZONTAL)
595     {
596         tmymove = 0;
597         return;
598     }
599     
600     if (ld->slopetype == ST_VERTICAL)
601     {
602         tmxmove = 0;
603         return;
604     }
605         
606     side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
607         
608     lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
609
610     if (side == 1)
611         lineangle += ANG180;
612
613     moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
614     deltaangle = moveangle-lineangle;
615
616     if (deltaangle > ANG180)
617         deltaangle += ANG180;
618     //  I_Error ("SlideLine: ang>ANG180");
619
620     lineangle >>= ANGLETOFINESHIFT;
621     deltaangle >>= ANGLETOFINESHIFT;
622         
623     movelen = P_AproxDistance (tmxmove, tmymove);
624     newlen = FixedMul (movelen, finecosine[deltaangle]);
625
626     tmxmove = FixedMul (newlen, finecosine[lineangle]); 
627     tmymove = FixedMul (newlen, finesine[lineangle]);   
628 }
629
630
631 //
632 // PTR_SlideTraverse
633 //
634 boolean PTR_SlideTraverse (intercept_t* in)
635 {
636     line_t*     li;
637         
638     if (!in->isaline)
639         I_Error ("PTR_SlideTraverse: not a line?");
640                 
641     li = in->d.line;
642     
643     if ( ! (li->flags & ML_TWOSIDED) )
644     {
645         if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
646         {
647             // don't hit the back side
648             return true;                
649         }
650         goto isblocking;
651     }
652
653     // set openrange, opentop, openbottom
654     P_LineOpening (li);
655     
656     if (openrange < slidemo->height)
657         goto isblocking;                // doesn't fit
658                 
659     if (opentop - slidemo->z < slidemo->height)
660         goto isblocking;                // mobj is too high
661
662     if (openbottom - slidemo->z > 24*FRACUNIT )
663         goto isblocking;                // too big a step up
664
665     // this line doesn't block movement
666     return true;                
667         
668     // the line does block movement,
669     // see if it is closer than best so far
670   isblocking:           
671     if (in->frac < bestslidefrac)
672     {
673         secondslidefrac = bestslidefrac;
674         secondslideline = bestslideline;
675         bestslidefrac = in->frac;
676         bestslideline = li;
677     }
678         
679     return false;       // stop
680 }
681
682
683
684 //
685 // P_SlideMove
686 // The momx / momy move is bad, so try to slide
687 // along a wall.
688 // Find the first line hit, move flush to it,
689 // and slide along it
690 //
691 // This is a kludgy mess.
692 //
693 void P_SlideMove (mobj_t* mo)
694 {
695     fixed_t             leadx;
696     fixed_t             leady;
697     fixed_t             trailx;
698     fixed_t             traily;
699     fixed_t             newx;
700     fixed_t             newy;
701     int                 hitcount;
702                 
703     slidemo = mo;
704     hitcount = 0;
705     
706   retry:
707     if (++hitcount == 3)
708         goto stairstep;         // don't loop forever
709
710     
711     // trace along the three leading corners
712     if (mo->momx > 0)
713     {
714         leadx = mo->x + mo->radius;
715         trailx = mo->x - mo->radius;
716     }
717     else
718     {
719         leadx = mo->x - mo->radius;
720         trailx = mo->x + mo->radius;
721     }
722         
723     if (mo->momy > 0)
724     {
725         leady = mo->y + mo->radius;
726         traily = mo->y - mo->radius;
727     }
728     else
729     {
730         leady = mo->y - mo->radius;
731         traily = mo->y + mo->radius;
732     }
733                 
734     bestslidefrac = FRACUNIT+1;
735         
736     P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
737                      PT_ADDLINES, PTR_SlideTraverse );
738     P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
739                      PT_ADDLINES, PTR_SlideTraverse );
740     P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
741                      PT_ADDLINES, PTR_SlideTraverse );
742     
743     // move up to the wall
744     if (bestslidefrac == FRACUNIT+1)
745     {
746         // the move most have hit the middle, so stairstep
747       stairstep:
748         if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
749             P_TryMove (mo, mo->x + mo->momx, mo->y);
750         return;
751     }
752
753     // fudge a bit to make sure it doesn't hit
754     bestslidefrac -= 0x800;     
755     if (bestslidefrac > 0)
756     {
757         newx = FixedMul (mo->momx, bestslidefrac);
758         newy = FixedMul (mo->momy, bestslidefrac);
759         
760         if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
761             goto stairstep;
762     }
763     
764     // Now continue along the wall.
765     // First calculate remainder.
766     bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
767     
768     if (bestslidefrac > FRACUNIT)
769         bestslidefrac = FRACUNIT;
770     
771     if (bestslidefrac <= 0)
772         return;
773     
774     tmxmove = FixedMul (mo->momx, bestslidefrac);
775     tmymove = FixedMul (mo->momy, bestslidefrac);
776
777     P_HitSlideLine (bestslideline);     // clip the moves
778
779     mo->momx = tmxmove;
780     mo->momy = tmymove;
781                 
782     if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
783     {
784         goto retry;
785     }
786 }
787
788
789 //
790 // P_LineAttack
791 //
792 mobj_t*         linetarget;     // who got hit (or NULL)
793 mobj_t*         shootthing;
794
795 // Height if not aiming up or down
796 // ???: use slope for monsters?
797 fixed_t         shootz; 
798
799 int             la_damage;
800 fixed_t         attackrange;
801
802 fixed_t         aimslope;
803
804 // slopes to top and bottom of target
805 extern fixed_t  topslope;
806 extern fixed_t  bottomslope;    
807
808
809 //
810 // PTR_AimTraverse
811 // Sets linetaget and aimslope when a target is aimed at.
812 //
813 boolean
814 PTR_AimTraverse (intercept_t* in)
815 {
816     line_t*             li;
817     mobj_t*             th;
818     fixed_t             slope;
819     fixed_t             thingtopslope;
820     fixed_t             thingbottomslope;
821     fixed_t             dist;
822                 
823     if (in->isaline)
824     {
825         li = in->d.line;
826         
827         if ( !(li->flags & ML_TWOSIDED) )
828             return false;               // stop
829         
830         // Crosses a two sided line.
831         // A two sided line will restrict
832         // the possible target ranges.
833         P_LineOpening (li);
834         
835         if (openbottom >= opentop)
836             return false;               // stop
837         
838         dist = FixedMul (attackrange, in->frac);
839
840         if (li->frontsector->floorheight != li->backsector->floorheight)
841         {
842             slope = FixedDiv (openbottom - shootz , dist);
843             if (slope > bottomslope)
844                 bottomslope = slope;
845         }
846                 
847         if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
848         {
849             slope = FixedDiv (opentop - shootz , dist);
850             if (slope < topslope)
851                 topslope = slope;
852         }
853                 
854         if (topslope <= bottomslope)
855             return false;               // stop
856                         
857         return true;                    // shot continues
858     }
859     
860     // shoot a thing
861     th = in->d.thing;
862     if (th == shootthing)
863         return true;                    // can't shoot self
864     
865     if (!(th->flags&MF_SHOOTABLE))
866         return true;                    // corpse or something
867
868     // check angles to see if the thing can be aimed at
869     dist = FixedMul (attackrange, in->frac);
870     thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
871
872     if (thingtopslope < bottomslope)
873         return true;                    // shot over the thing
874
875     thingbottomslope = FixedDiv (th->z - shootz, dist);
876
877     if (thingbottomslope > topslope)
878         return true;                    // shot under the thing
879     
880     // this thing can be hit!
881     if (thingtopslope > topslope)
882         thingtopslope = topslope;
883     
884     if (thingbottomslope < bottomslope)
885         thingbottomslope = bottomslope;
886
887     aimslope = (thingtopslope+thingbottomslope)/2;
888     linetarget = th;
889
890     return false;                       // don't go any farther
891 }
892
893
894 //
895 // PTR_ShootTraverse
896 //
897 boolean PTR_ShootTraverse (intercept_t* in)
898 {
899     fixed_t             x;
900     fixed_t             y;
901     fixed_t             z;
902     fixed_t             frac;
903     
904     line_t*             li;
905     
906     mobj_t*             th;
907
908     fixed_t             slope;
909     fixed_t             dist;
910     fixed_t             thingtopslope;
911     fixed_t             thingbottomslope;
912                 
913     if (in->isaline)
914     {
915         li = in->d.line;
916         
917         if (li->special)
918             P_ShootSpecialLine (shootthing, li);
919
920         if ( !(li->flags & ML_TWOSIDED) )
921             goto hitline;
922         
923         // crosses a two sided line
924         P_LineOpening (li);
925                 
926         dist = FixedMul (attackrange, in->frac);
927
928         if (li->frontsector->floorheight != li->backsector->floorheight)
929         {
930             slope = FixedDiv (openbottom - shootz , dist);
931             if (slope > aimslope)
932                 goto hitline;
933         }
934                 
935         if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
936         {
937             slope = FixedDiv (opentop - shootz , dist);
938             if (slope < aimslope)
939                 goto hitline;
940         }
941
942         // shot continues
943         return true;
944         
945         
946         // hit line
947       hitline:
948         // position a bit closer
949         frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
950         x = trace.x + FixedMul (trace.dx, frac);
951         y = trace.y + FixedMul (trace.dy, frac);
952         z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
953
954         if (li->frontsector->ceilingpic == skyflatnum)
955         {
956             // don't shoot the sky!
957             if (z > li->frontsector->ceilingheight)
958                 return false;
959             
960             // it's a sky hack wall
961             if  (li->backsector && li->backsector->ceilingpic == skyflatnum)
962                 return false;           
963         }
964
965         // Spawn bullet puffs.
966         P_SpawnPuff (x,y,z);
967         
968         // don't go any farther
969         return false;   
970     }
971     
972     // shoot a thing
973     th = in->d.thing;
974     if (th == shootthing)
975         return true;            // can't shoot self
976     
977     if (!(th->flags&MF_SHOOTABLE))
978         return true;            // corpse or something
979                 
980     // check angles to see if the thing can be aimed at
981     dist = FixedMul (attackrange, in->frac);
982     thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
983
984     if (thingtopslope < aimslope)
985         return true;            // shot over the thing
986
987     thingbottomslope = FixedDiv (th->z - shootz, dist);
988
989     if (thingbottomslope > aimslope)
990         return true;            // shot under the thing
991
992     
993     // hit thing
994     // position a bit closer
995     frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
996
997     x = trace.x + FixedMul (trace.dx, frac);
998     y = trace.y + FixedMul (trace.dy, frac);
999     z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1000
1001     // Spawn bullet puffs or blod spots,
1002     // depending on target type.
1003     if (in->d.thing->flags & MF_NOBLOOD)
1004         P_SpawnPuff (x,y,z);
1005     else
1006         P_SpawnBlood (x,y,z, la_damage);
1007
1008     if (la_damage)
1009         P_DamageMobj (th, shootthing, shootthing, la_damage);
1010
1011     // don't go any farther
1012     return false;
1013         
1014 }
1015
1016
1017 //
1018 // P_AimLineAttack
1019 //
1020 fixed_t
1021 P_AimLineAttack
1022 ( mobj_t*       t1,
1023   angle_t       angle,
1024   fixed_t       distance )
1025 {
1026     fixed_t     x2;
1027     fixed_t     y2;
1028         
1029     angle >>= ANGLETOFINESHIFT;
1030     shootthing = t1;
1031     
1032     x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1033     y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1034     shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1035
1036     // can't shoot outside view angles
1037     topslope = 100*FRACUNIT/160;        
1038     bottomslope = -100*FRACUNIT/160;
1039     
1040     attackrange = distance;
1041     linetarget = NULL;
1042         
1043     P_PathTraverse ( t1->x, t1->y,
1044                      x2, y2,
1045                      PT_ADDLINES|PT_ADDTHINGS,
1046                      PTR_AimTraverse );
1047                 
1048     if (linetarget)
1049         return aimslope;
1050
1051     return 0;
1052 }
1053  
1054
1055 //
1056 // P_LineAttack
1057 // If damage == 0, it is just a test trace
1058 // that will leave linetarget set.
1059 //
1060 void
1061 P_LineAttack
1062 ( mobj_t*       t1,
1063   angle_t       angle,
1064   fixed_t       distance,
1065   fixed_t       slope,
1066   int           damage )
1067 {
1068     fixed_t     x2;
1069     fixed_t     y2;
1070         
1071     angle >>= ANGLETOFINESHIFT;
1072     shootthing = t1;
1073     la_damage = damage;
1074     x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1075     y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1076     shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1077     attackrange = distance;
1078     aimslope = slope;
1079                 
1080     P_PathTraverse ( t1->x, t1->y,
1081                      x2, y2,
1082                      PT_ADDLINES|PT_ADDTHINGS,
1083                      PTR_ShootTraverse );
1084 }
1085  
1086
1087
1088 //
1089 // USE LINES
1090 //
1091 mobj_t*         usething;
1092
1093 boolean PTR_UseTraverse (intercept_t* in)
1094 {
1095     int         side;
1096         
1097     if (!in->d.line->special)
1098     {
1099         P_LineOpening (in->d.line);
1100         if (openrange <= 0)
1101         {
1102             S_StartSound (usething, sfx_noway);
1103             
1104             // can't use through a wall
1105             return false;       
1106         }
1107         // not a special line, but keep checking
1108         return true ;           
1109     }
1110         
1111     side = 0;
1112     if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1113         side = 1;
1114     
1115     //  return false;           // don't use back side
1116         
1117     P_UseSpecialLine (usething, in->d.line, side);
1118
1119     // can't use for than one special line in a row
1120     return false;
1121 }
1122
1123
1124 //
1125 // P_UseLines
1126 // Looks for special lines in front of the player to activate.
1127 //
1128 void P_UseLines (player_t*      player) 
1129 {
1130     int         angle;
1131     fixed_t     x1;
1132     fixed_t     y1;
1133     fixed_t     x2;
1134     fixed_t     y2;
1135         
1136     usething = player->mo;
1137                 
1138     angle = player->mo->angle >> ANGLETOFINESHIFT;
1139
1140     x1 = player->mo->x;
1141     y1 = player->mo->y;
1142     x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1143     y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1144         
1145     P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1146 }
1147
1148
1149 //
1150 // RADIUS ATTACK
1151 //
1152 mobj_t*         bombsource;
1153 mobj_t*         bombspot;
1154 int             bombdamage;
1155
1156
1157 //
1158 // PIT_RadiusAttack
1159 // "bombsource" is the creature
1160 // that caused the explosion at "bombspot".
1161 //
1162 boolean PIT_RadiusAttack (mobj_t* thing)
1163 {
1164     fixed_t     dx;
1165     fixed_t     dy;
1166     fixed_t     dist;
1167         
1168     if (!(thing->flags & MF_SHOOTABLE) )
1169         return true;
1170
1171     // Boss spider and cyborg
1172     // take no damage from concussion.
1173     if (thing->type == MT_CYBORG
1174         || thing->type == MT_SPIDER)
1175         return true;    
1176                 
1177     dx = abs(thing->x - bombspot->x);
1178     dy = abs(thing->y - bombspot->y);
1179     
1180     dist = dx>dy ? dx : dy;
1181     dist = (dist - thing->radius) >> FRACBITS;
1182
1183     if (dist < 0)
1184         dist = 0;
1185
1186     if (dist >= bombdamage)
1187         return true;    // out of range
1188
1189     if ( P_CheckSight (thing, bombspot) )
1190     {
1191         // must be in direct path
1192         P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1193     }
1194     
1195     return true;
1196 }
1197
1198
1199 //
1200 // P_RadiusAttack
1201 // Source is the creature that caused the explosion at spot.
1202 //
1203 void
1204 P_RadiusAttack
1205 ( mobj_t*       spot,
1206   mobj_t*       source,
1207   int           damage )
1208 {
1209     int         x;
1210     int         y;
1211     
1212     int         xl;
1213     int         xh;
1214     int         yl;
1215     int         yh;
1216     
1217     fixed_t     dist;
1218         
1219     dist = (damage+MAXRADIUS)<<FRACBITS;
1220     yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1221     yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1222     xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1223     xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1224     bombspot = spot;
1225     bombsource = source;
1226     bombdamage = damage;
1227         
1228     for (y=yl ; y<=yh ; y++)
1229         for (x=xl ; x<=xh ; x++)
1230             P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1231 }
1232
1233
1234
1235 //
1236 // SECTOR HEIGHT CHANGING
1237 // After modifying a sectors floor or ceiling height,
1238 // call this routine to adjust the positions
1239 // of all things that touch the sector.
1240 //
1241 // If anything doesn't fit anymore, true will be returned.
1242 // If crunch is true, they will take damage
1243 //  as they are being crushed.
1244 // If Crunch is false, you should set the sector height back
1245 //  the way it was and call P_ChangeSector again
1246 //  to undo the changes.
1247 //
1248 boolean         crushchange;
1249 boolean         nofit;
1250
1251
1252 //
1253 // PIT_ChangeSector
1254 //
1255 boolean PIT_ChangeSector (mobj_t*       thing)
1256 {
1257     mobj_t*     mo;
1258         
1259     if (P_ThingHeightClip (thing))
1260     {
1261         // keep checking
1262         return true;
1263     }
1264     
1265
1266     // crunch bodies to giblets
1267     if (thing->health <= 0)
1268     {
1269         P_SetMobjState (thing, S_GIBS);
1270
1271         thing->flags &= ~MF_SOLID;
1272         thing->height = 0;
1273         thing->radius = 0;
1274
1275         // keep checking
1276         return true;            
1277     }
1278
1279     // crunch dropped items
1280     if (thing->flags & MF_DROPPED)
1281     {
1282         P_RemoveMobj (thing);
1283         
1284         // keep checking
1285         return true;            
1286     }
1287
1288     if (! (thing->flags & MF_SHOOTABLE) )
1289     {
1290         // assume it is bloody gibs or something
1291         return true;                    
1292     }
1293     
1294     nofit = true;
1295
1296     if (crushchange && !(leveltime&3) )
1297     {
1298         P_DamageMobj(thing,NULL,NULL,10);
1299
1300         // spray blood in a random direction
1301         mo = P_SpawnMobj (thing->x,
1302                           thing->y,
1303                           thing->z + thing->height/2, MT_BLOOD);
1304         
1305         mo->momx = P_Random2()<<12;
1306         mo->momy = P_Random2()<<12;
1307     }
1308
1309     // keep checking (crush other things)       
1310     return true;        
1311 }
1312
1313
1314
1315 //
1316 // P_ChangeSector
1317 //
1318 boolean
1319 P_ChangeSector
1320 ( sector_t*     sector,
1321   boolean       crunch )
1322 {
1323     int         x;
1324     int         y;
1325         
1326     nofit = false;
1327     crushchange = crunch;
1328         
1329     // re-check heights for all things near the moving sector
1330     for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1331         for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1332             P_BlockThingsIterator (x, y, PIT_ChangeSector);
1333         
1334         
1335     return nofit;
1336 }
1337