Add weapon cycling bindings for mouse and joystick buttons. Add weapon cycling bindi...
[chocolate-doom.git] / src / p_map.c
blobb50fff2cdb4b391b4d991ca44bd06caa1de0c867
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard, Andrey Budko
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 // 02111-1307, USA.
22 // DESCRIPTION:
23 // Movement, collision handling.
24 // Shooting and aiming.
26 //-----------------------------------------------------------------------------
29 #include <stdlib.h>
31 #include "deh_misc.h"
33 #include "m_bbox.h"
34 #include "m_random.h"
35 #include "i_system.h"
37 #include "doomdef.h"
38 #include "m_argv.h"
39 #include "m_misc.h"
40 #include "p_local.h"
42 #include "s_sound.h"
44 // State.
45 #include "doomstat.h"
46 #include "r_state.h"
47 // Data.
48 #include "sounds.h"
50 // Spechit overrun magic value.
52 // This is the value used by PrBoom-plus. I think the value below is
53 // actually better and works with more demos. However, I think
54 // it's better for the spechits emulation to be compatible with
55 // PrBoom-plus, at least so that the big spechits emulation list
56 // on Doomworld can also be used with Chocolate Doom.
58 #define DEFAULT_SPECHIT_MAGIC 0x01C09C98
60 // This is from a post by myk on the Doomworld forums,
61 // outputted from entryway's spechit_magic generator for
62 // s205n546.lmp. The _exact_ value of this isn't too
63 // important; as long as it is in the right general
64 // range, it will usually work. Otherwise, we can use
65 // the generator (hacked doom2.exe) and provide it
66 // with -spechit.
68 //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8
71 fixed_t tmbbox[4];
72 mobj_t* tmthing;
73 int tmflags;
74 fixed_t tmx;
75 fixed_t tmy;
78 // If "floatok" true, move would be ok
79 // if within "tmfloorz - tmceilingz".
80 boolean floatok;
82 fixed_t tmfloorz;
83 fixed_t tmceilingz;
84 fixed_t tmdropoffz;
86 // keep track of the line that lowers the ceiling,
87 // so missiles don't explode against sky hack walls
88 line_t* ceilingline;
90 // keep track of special lines as they are hit,
91 // but don't process them until the move is proven valid
93 // fraggle: I have increased the size of this buffer. In the original Doom,
94 // overrunning past this limit caused other bits of memory to be overwritten,
95 // affecting demo playback. However, in doing so, the limit was still
96 // exceeded. So we have to support more than 8 specials.
98 // We keep the original limit, to detect what variables in memory were
99 // overwritten (see SpechitOverrun())
101 #define MAXSPECIALCROSS 20
102 #define MAXSPECIALCROSS_ORIGINAL 8
104 line_t* spechit[MAXSPECIALCROSS];
105 int numspechit;
110 // TELEPORT MOVE
114 // PIT_StompThing
116 boolean PIT_StompThing (mobj_t* thing)
118 fixed_t blockdist;
120 if (!(thing->flags & MF_SHOOTABLE) )
121 return true;
123 blockdist = thing->radius + tmthing->radius;
125 if ( abs(thing->x - tmx) >= blockdist
126 || abs(thing->y - tmy) >= blockdist )
128 // didn't hit it
129 return true;
132 // don't clip against self
133 if (thing == tmthing)
134 return true;
136 // monsters don't stomp things except on boss level
137 if ( !tmthing->player && gamemap != 30)
138 return false;
140 P_DamageMobj (thing, tmthing, tmthing, 10000);
142 return true;
147 // P_TeleportMove
149 boolean
150 P_TeleportMove
151 ( mobj_t* thing,
152 fixed_t x,
153 fixed_t y )
155 int xl;
156 int xh;
157 int yl;
158 int yh;
159 int bx;
160 int by;
162 subsector_t* newsubsec;
164 // kill anything occupying the position
165 tmthing = thing;
166 tmflags = thing->flags;
168 tmx = x;
169 tmy = y;
171 tmbbox[BOXTOP] = y + tmthing->radius;
172 tmbbox[BOXBOTTOM] = y - tmthing->radius;
173 tmbbox[BOXRIGHT] = x + tmthing->radius;
174 tmbbox[BOXLEFT] = x - tmthing->radius;
176 newsubsec = R_PointInSubsector (x,y);
177 ceilingline = NULL;
179 // The base floor/ceiling is from the subsector
180 // that contains the point.
181 // Any contacted lines the step closer together
182 // will adjust them.
183 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
184 tmceilingz = newsubsec->sector->ceilingheight;
186 validcount++;
187 numspechit = 0;
189 // stomp on any things contacted
190 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
191 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
192 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
193 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
195 for (bx=xl ; bx<=xh ; bx++)
196 for (by=yl ; by<=yh ; by++)
197 if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
198 return false;
200 // the move is ok,
201 // so link the thing into its new position
202 P_UnsetThingPosition (thing);
204 thing->floorz = tmfloorz;
205 thing->ceilingz = tmceilingz;
206 thing->x = x;
207 thing->y = y;
209 P_SetThingPosition (thing);
211 return true;
216 // MOVEMENT ITERATOR FUNCTIONS
219 static void SpechitOverrun(line_t *ld);
222 // PIT_CheckLine
223 // Adjusts tmfloorz and tmceilingz as lines are contacted
225 boolean PIT_CheckLine (line_t* ld)
227 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
228 || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
229 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
230 || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
231 return true;
233 if (P_BoxOnLineSide (tmbbox, ld) != -1)
234 return true;
236 // A line has been hit
238 // The moving thing's destination position will cross
239 // the given line.
240 // If this should not be allowed, return false.
241 // If the line is special, keep track of it
242 // to process later if the move is proven ok.
243 // NOTE: specials are NOT sorted by order,
244 // so two special lines that are only 8 pixels apart
245 // could be crossed in either order.
247 if (!ld->backsector)
248 return false; // one sided line
250 if (!(tmthing->flags & MF_MISSILE) )
252 if ( ld->flags & ML_BLOCKING )
253 return false; // explicitly blocking everything
255 if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )
256 return false; // block monsters only
259 // set openrange, opentop, openbottom
260 P_LineOpening (ld);
262 // adjust floor / ceiling heights
263 if (opentop < tmceilingz)
265 tmceilingz = opentop;
266 ceilingline = ld;
269 if (openbottom > tmfloorz)
270 tmfloorz = openbottom;
272 if (lowfloor < tmdropoffz)
273 tmdropoffz = lowfloor;
275 // if contacted a special line, add it to the list
276 if (ld->special)
278 spechit[numspechit] = ld;
279 numspechit++;
281 // fraggle: spechits overrun emulation code from prboom-plus
282 if (numspechit > MAXSPECIALCROSS_ORIGINAL)
284 SpechitOverrun(ld);
288 return true;
292 // PIT_CheckThing
294 boolean PIT_CheckThing (mobj_t* thing)
296 fixed_t blockdist;
297 boolean solid;
298 int damage;
300 if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
301 return true;
303 blockdist = thing->radius + tmthing->radius;
305 if ( abs(thing->x - tmx) >= blockdist
306 || abs(thing->y - tmy) >= blockdist )
308 // didn't hit it
309 return true;
312 // don't clip against self
313 if (thing == tmthing)
314 return true;
316 // check for skulls slamming into things
317 if (tmthing->flags & MF_SKULLFLY)
319 damage = ((P_Random()%8)+1)*tmthing->info->damage;
321 P_DamageMobj (thing, tmthing, tmthing, damage);
323 tmthing->flags &= ~MF_SKULLFLY;
324 tmthing->momx = tmthing->momy = tmthing->momz = 0;
326 P_SetMobjState (tmthing, tmthing->info->spawnstate);
328 return false; // stop moving
332 // missiles can hit other things
333 if (tmthing->flags & MF_MISSILE)
335 // see if it went over / under
336 if (tmthing->z > thing->z + thing->height)
337 return true; // overhead
338 if (tmthing->z+tmthing->height < thing->z)
339 return true; // underneath
341 if (tmthing->target
342 && (tmthing->target->type == thing->type ||
343 (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
344 (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
346 // Don't hit same species as originator.
347 if (thing == tmthing->target)
348 return true;
350 // sdh: Add deh_species_infighting here. We can override the
351 // "monsters of the same species cant hurt each other" behavior
352 // through dehacked patches
354 if (thing->type != MT_PLAYER && !deh_species_infighting)
356 // Explode, but do no damage.
357 // Let players missile other players.
358 return false;
362 if (! (thing->flags & MF_SHOOTABLE) )
364 // didn't do any damage
365 return !(thing->flags & MF_SOLID);
368 // damage / explode
369 damage = ((P_Random()%8)+1)*tmthing->info->damage;
370 P_DamageMobj (thing, tmthing, tmthing->target, damage);
372 // don't traverse any more
373 return false;
376 // check for special pickup
377 if (thing->flags & MF_SPECIAL)
379 solid = thing->flags&MF_SOLID;
380 if (tmflags&MF_PICKUP)
382 // can remove thing
383 P_TouchSpecialThing (thing, tmthing);
385 return !solid;
388 return !(thing->flags & MF_SOLID);
393 // MOVEMENT CLIPPING
397 // P_CheckPosition
398 // This is purely informative, nothing is modified
399 // (except things picked up).
401 // in:
402 // a mobj_t (can be valid or invalid)
403 // a position to be checked
404 // (doesn't need to be related to the mobj_t->x,y)
406 // during:
407 // special things are touched if MF_PICKUP
408 // early out on solid lines?
410 // out:
411 // newsubsec
412 // floorz
413 // ceilingz
414 // tmdropoffz
415 // the lowest point contacted
416 // (monsters won't move to a dropoff)
417 // speciallines[]
418 // numspeciallines
420 boolean
421 P_CheckPosition
422 ( mobj_t* thing,
423 fixed_t x,
424 fixed_t y )
426 int xl;
427 int xh;
428 int yl;
429 int yh;
430 int bx;
431 int by;
432 subsector_t* newsubsec;
434 tmthing = thing;
435 tmflags = thing->flags;
437 tmx = x;
438 tmy = y;
440 tmbbox[BOXTOP] = y + tmthing->radius;
441 tmbbox[BOXBOTTOM] = y - tmthing->radius;
442 tmbbox[BOXRIGHT] = x + tmthing->radius;
443 tmbbox[BOXLEFT] = x - tmthing->radius;
445 newsubsec = R_PointInSubsector (x,y);
446 ceilingline = NULL;
448 // The base floor / ceiling is from the subsector
449 // that contains the point.
450 // Any contacted lines the step closer together
451 // will adjust them.
452 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
453 tmceilingz = newsubsec->sector->ceilingheight;
455 validcount++;
456 numspechit = 0;
458 if ( tmflags & MF_NOCLIP )
459 return true;
461 // Check things first, possibly picking things up.
462 // The bounding box is extended by MAXRADIUS
463 // because mobj_ts are grouped into mapblocks
464 // based on their origin point, and can overlap
465 // into adjacent blocks by up to MAXRADIUS units.
466 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
467 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
468 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
469 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
471 for (bx=xl ; bx<=xh ; bx++)
472 for (by=yl ; by<=yh ; by++)
473 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
474 return false;
476 // check lines
477 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
478 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
479 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
480 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
482 for (bx=xl ; bx<=xh ; bx++)
483 for (by=yl ; by<=yh ; by++)
484 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
485 return false;
487 return true;
492 // P_TryMove
493 // Attempt to move to a new position,
494 // crossing special lines unless MF_TELEPORT is set.
496 boolean
497 P_TryMove
498 ( mobj_t* thing,
499 fixed_t x,
500 fixed_t y )
502 fixed_t oldx;
503 fixed_t oldy;
504 int side;
505 int oldside;
506 line_t* ld;
508 floatok = false;
509 if (!P_CheckPosition (thing, x, y))
510 return false; // solid wall or thing
512 if ( !(thing->flags & MF_NOCLIP) )
514 if (tmceilingz - tmfloorz < thing->height)
515 return false; // doesn't fit
517 floatok = true;
519 if ( !(thing->flags&MF_TELEPORT)
520 &&tmceilingz - thing->z < thing->height)
521 return false; // mobj must lower itself to fit
523 if ( !(thing->flags&MF_TELEPORT)
524 && tmfloorz - thing->z > 24*FRACUNIT )
525 return false; // too big a step up
527 if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
528 && tmfloorz - tmdropoffz > 24*FRACUNIT )
529 return false; // don't stand over a dropoff
532 // the move is ok,
533 // so link the thing into its new position
534 P_UnsetThingPosition (thing);
536 oldx = thing->x;
537 oldy = thing->y;
538 thing->floorz = tmfloorz;
539 thing->ceilingz = tmceilingz;
540 thing->x = x;
541 thing->y = y;
543 P_SetThingPosition (thing);
545 // if any special lines were hit, do the effect
546 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
548 while (numspechit--)
550 // see if the line was crossed
551 ld = spechit[numspechit];
552 side = P_PointOnLineSide (thing->x, thing->y, ld);
553 oldside = P_PointOnLineSide (oldx, oldy, ld);
554 if (side != oldside)
556 if (ld->special)
557 P_CrossSpecialLine (ld-lines, oldside, thing);
562 return true;
567 // P_ThingHeightClip
568 // Takes a valid thing and adjusts the thing->floorz,
569 // thing->ceilingz, and possibly thing->z.
570 // This is called for all nearby monsters
571 // whenever a sector changes height.
572 // If the thing doesn't fit,
573 // the z will be set to the lowest value
574 // and false will be returned.
576 boolean P_ThingHeightClip (mobj_t* thing)
578 boolean onfloor;
580 onfloor = (thing->z == thing->floorz);
582 P_CheckPosition (thing, thing->x, thing->y);
583 // what about stranding a monster partially off an edge?
585 thing->floorz = tmfloorz;
586 thing->ceilingz = tmceilingz;
588 if (onfloor)
590 // walking monsters rise and fall with the floor
591 thing->z = thing->floorz;
593 else
595 // don't adjust a floating monster unless forced to
596 if (thing->z+thing->height > thing->ceilingz)
597 thing->z = thing->ceilingz - thing->height;
600 if (thing->ceilingz - thing->floorz < thing->height)
601 return false;
603 return true;
609 // SLIDE MOVE
610 // Allows the player to slide along any angled walls.
612 fixed_t bestslidefrac;
613 fixed_t secondslidefrac;
615 line_t* bestslideline;
616 line_t* secondslideline;
618 mobj_t* slidemo;
620 fixed_t tmxmove;
621 fixed_t tmymove;
626 // P_HitSlideLine
627 // Adjusts the xmove / ymove
628 // so that the next move will slide along the wall.
630 void P_HitSlideLine (line_t* ld)
632 int side;
634 angle_t lineangle;
635 angle_t moveangle;
636 angle_t deltaangle;
638 fixed_t movelen;
639 fixed_t newlen;
642 if (ld->slopetype == ST_HORIZONTAL)
644 tmymove = 0;
645 return;
648 if (ld->slopetype == ST_VERTICAL)
650 tmxmove = 0;
651 return;
654 side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
656 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
658 if (side == 1)
659 lineangle += ANG180;
661 moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
662 deltaangle = moveangle-lineangle;
664 if (deltaangle > ANG180)
665 deltaangle += ANG180;
666 // I_Error ("SlideLine: ang>ANG180");
668 lineangle >>= ANGLETOFINESHIFT;
669 deltaangle >>= ANGLETOFINESHIFT;
671 movelen = P_AproxDistance (tmxmove, tmymove);
672 newlen = FixedMul (movelen, finecosine[deltaangle]);
674 tmxmove = FixedMul (newlen, finecosine[lineangle]);
675 tmymove = FixedMul (newlen, finesine[lineangle]);
680 // PTR_SlideTraverse
682 boolean PTR_SlideTraverse (intercept_t* in)
684 line_t* li;
686 if (!in->isaline)
687 I_Error ("PTR_SlideTraverse: not a line?");
689 li = in->d.line;
691 if ( ! (li->flags & ML_TWOSIDED) )
693 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
695 // don't hit the back side
696 return true;
698 goto isblocking;
701 // set openrange, opentop, openbottom
702 P_LineOpening (li);
704 if (openrange < slidemo->height)
705 goto isblocking; // doesn't fit
707 if (opentop - slidemo->z < slidemo->height)
708 goto isblocking; // mobj is too high
710 if (openbottom - slidemo->z > 24*FRACUNIT )
711 goto isblocking; // too big a step up
713 // this line doesn't block movement
714 return true;
716 // the line does block movement,
717 // see if it is closer than best so far
718 isblocking:
719 if (in->frac < bestslidefrac)
721 secondslidefrac = bestslidefrac;
722 secondslideline = bestslideline;
723 bestslidefrac = in->frac;
724 bestslideline = li;
727 return false; // stop
733 // P_SlideMove
734 // The momx / momy move is bad, so try to slide
735 // along a wall.
736 // Find the first line hit, move flush to it,
737 // and slide along it
739 // This is a kludgy mess.
741 void P_SlideMove (mobj_t* mo)
743 fixed_t leadx;
744 fixed_t leady;
745 fixed_t trailx;
746 fixed_t traily;
747 fixed_t newx;
748 fixed_t newy;
749 int hitcount;
751 slidemo = mo;
752 hitcount = 0;
754 retry:
755 if (++hitcount == 3)
756 goto stairstep; // don't loop forever
759 // trace along the three leading corners
760 if (mo->momx > 0)
762 leadx = mo->x + mo->radius;
763 trailx = mo->x - mo->radius;
765 else
767 leadx = mo->x - mo->radius;
768 trailx = mo->x + mo->radius;
771 if (mo->momy > 0)
773 leady = mo->y + mo->radius;
774 traily = mo->y - mo->radius;
776 else
778 leady = mo->y - mo->radius;
779 traily = mo->y + mo->radius;
782 bestslidefrac = FRACUNIT+1;
784 P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
785 PT_ADDLINES, PTR_SlideTraverse );
786 P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
787 PT_ADDLINES, PTR_SlideTraverse );
788 P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
789 PT_ADDLINES, PTR_SlideTraverse );
791 // move up to the wall
792 if (bestslidefrac == FRACUNIT+1)
794 // the move most have hit the middle, so stairstep
795 stairstep:
796 if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
797 P_TryMove (mo, mo->x + mo->momx, mo->y);
798 return;
801 // fudge a bit to make sure it doesn't hit
802 bestslidefrac -= 0x800;
803 if (bestslidefrac > 0)
805 newx = FixedMul (mo->momx, bestslidefrac);
806 newy = FixedMul (mo->momy, bestslidefrac);
808 if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
809 goto stairstep;
812 // Now continue along the wall.
813 // First calculate remainder.
814 bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
816 if (bestslidefrac > FRACUNIT)
817 bestslidefrac = FRACUNIT;
819 if (bestslidefrac <= 0)
820 return;
822 tmxmove = FixedMul (mo->momx, bestslidefrac);
823 tmymove = FixedMul (mo->momy, bestslidefrac);
825 P_HitSlideLine (bestslideline); // clip the moves
827 mo->momx = tmxmove;
828 mo->momy = tmymove;
830 if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
832 goto retry;
838 // P_LineAttack
840 mobj_t* linetarget; // who got hit (or NULL)
841 mobj_t* shootthing;
843 // Height if not aiming up or down
844 // ???: use slope for monsters?
845 fixed_t shootz;
847 int la_damage;
848 fixed_t attackrange;
850 fixed_t aimslope;
852 // slopes to top and bottom of target
853 extern fixed_t topslope;
854 extern fixed_t bottomslope;
858 // PTR_AimTraverse
859 // Sets linetaget and aimslope when a target is aimed at.
861 boolean
862 PTR_AimTraverse (intercept_t* in)
864 line_t* li;
865 mobj_t* th;
866 fixed_t slope;
867 fixed_t thingtopslope;
868 fixed_t thingbottomslope;
869 fixed_t dist;
871 if (in->isaline)
873 li = in->d.line;
875 if ( !(li->flags & ML_TWOSIDED) )
876 return false; // stop
878 // Crosses a two sided line.
879 // A two sided line will restrict
880 // the possible target ranges.
881 P_LineOpening (li);
883 if (openbottom >= opentop)
884 return false; // stop
886 dist = FixedMul (attackrange, in->frac);
888 // Return false if there is no back sector. This should never
889 // be the case if the line is two-sided; however, some WADs
890 // (eg. ottawau.wad) use this as an "impassible glass" trick
891 // and rely on Vanilla Doom's (unintentional) support for this.
893 if (li->backsector == NULL)
895 return false;
898 if (li->frontsector->floorheight != li->backsector->floorheight)
900 slope = FixedDiv (openbottom - shootz , dist);
901 if (slope > bottomslope)
902 bottomslope = slope;
905 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
907 slope = FixedDiv (opentop - shootz , dist);
908 if (slope < topslope)
909 topslope = slope;
912 if (topslope <= bottomslope)
913 return false; // stop
915 return true; // shot continues
918 // shoot a thing
919 th = in->d.thing;
920 if (th == shootthing)
921 return true; // can't shoot self
923 if (!(th->flags&MF_SHOOTABLE))
924 return true; // corpse or something
926 // check angles to see if the thing can be aimed at
927 dist = FixedMul (attackrange, in->frac);
928 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
930 if (thingtopslope < bottomslope)
931 return true; // shot over the thing
933 thingbottomslope = FixedDiv (th->z - shootz, dist);
935 if (thingbottomslope > topslope)
936 return true; // shot under the thing
938 // this thing can be hit!
939 if (thingtopslope > topslope)
940 thingtopslope = topslope;
942 if (thingbottomslope < bottomslope)
943 thingbottomslope = bottomslope;
945 aimslope = (thingtopslope+thingbottomslope)/2;
946 linetarget = th;
948 return false; // don't go any farther
953 // PTR_ShootTraverse
955 boolean PTR_ShootTraverse (intercept_t* in)
957 fixed_t x;
958 fixed_t y;
959 fixed_t z;
960 fixed_t frac;
962 line_t* li;
964 mobj_t* th;
966 fixed_t slope;
967 fixed_t dist;
968 fixed_t thingtopslope;
969 fixed_t thingbottomslope;
971 if (in->isaline)
973 li = in->d.line;
975 if (li->special)
976 P_ShootSpecialLine (shootthing, li);
978 if ( !(li->flags & ML_TWOSIDED) )
979 goto hitline;
981 // crosses a two sided line
982 P_LineOpening (li);
984 dist = FixedMul (attackrange, in->frac);
986 // Check if backsector is NULL. See comment in PTR_AimTraverse.
988 if (li->backsector == NULL)
990 goto hitline;
993 if (li->frontsector->floorheight != li->backsector->floorheight)
995 slope = FixedDiv (openbottom - shootz , dist);
996 if (slope > aimslope)
997 goto hitline;
1000 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1002 slope = FixedDiv (opentop - shootz , dist);
1003 if (slope < aimslope)
1004 goto hitline;
1007 // shot continues
1008 return true;
1011 // hit line
1012 hitline:
1013 // position a bit closer
1014 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1015 x = trace.x + FixedMul (trace.dx, frac);
1016 y = trace.y + FixedMul (trace.dy, frac);
1017 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1019 if (li->frontsector->ceilingpic == skyflatnum)
1021 // don't shoot the sky!
1022 if (z > li->frontsector->ceilingheight)
1023 return false;
1025 // it's a sky hack wall
1026 if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1027 return false;
1030 // Spawn bullet puffs.
1031 P_SpawnPuff (x,y,z);
1033 // don't go any farther
1034 return false;
1037 // shoot a thing
1038 th = in->d.thing;
1039 if (th == shootthing)
1040 return true; // can't shoot self
1042 if (!(th->flags&MF_SHOOTABLE))
1043 return true; // corpse or something
1045 // check angles to see if the thing can be aimed at
1046 dist = FixedMul (attackrange, in->frac);
1047 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1049 if (thingtopslope < aimslope)
1050 return true; // shot over the thing
1052 thingbottomslope = FixedDiv (th->z - shootz, dist);
1054 if (thingbottomslope > aimslope)
1055 return true; // shot under the thing
1058 // hit thing
1059 // position a bit closer
1060 frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1062 x = trace.x + FixedMul (trace.dx, frac);
1063 y = trace.y + FixedMul (trace.dy, frac);
1064 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1066 // Spawn bullet puffs or blod spots,
1067 // depending on target type.
1068 if (in->d.thing->flags & MF_NOBLOOD)
1069 P_SpawnPuff (x,y,z);
1070 else
1071 P_SpawnBlood (x,y,z, la_damage);
1073 if (la_damage)
1074 P_DamageMobj (th, shootthing, shootthing, la_damage);
1076 // don't go any farther
1077 return false;
1083 // P_AimLineAttack
1085 fixed_t
1086 P_AimLineAttack
1087 ( mobj_t* t1,
1088 angle_t angle,
1089 fixed_t distance )
1091 fixed_t x2;
1092 fixed_t y2;
1094 t1 = P_SubstNullMobj(t1);
1096 angle >>= ANGLETOFINESHIFT;
1097 shootthing = t1;
1099 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1100 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1101 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1103 // can't shoot outside view angles
1104 topslope = 100*FRACUNIT/160;
1105 bottomslope = -100*FRACUNIT/160;
1107 attackrange = distance;
1108 linetarget = NULL;
1110 P_PathTraverse ( t1->x, t1->y,
1111 x2, y2,
1112 PT_ADDLINES|PT_ADDTHINGS,
1113 PTR_AimTraverse );
1115 if (linetarget)
1116 return aimslope;
1118 return 0;
1123 // P_LineAttack
1124 // If damage == 0, it is just a test trace
1125 // that will leave linetarget set.
1127 void
1128 P_LineAttack
1129 ( mobj_t* t1,
1130 angle_t angle,
1131 fixed_t distance,
1132 fixed_t slope,
1133 int damage )
1135 fixed_t x2;
1136 fixed_t y2;
1138 angle >>= ANGLETOFINESHIFT;
1139 shootthing = t1;
1140 la_damage = damage;
1141 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1142 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1143 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1144 attackrange = distance;
1145 aimslope = slope;
1147 P_PathTraverse ( t1->x, t1->y,
1148 x2, y2,
1149 PT_ADDLINES|PT_ADDTHINGS,
1150 PTR_ShootTraverse );
1156 // USE LINES
1158 mobj_t* usething;
1160 boolean PTR_UseTraverse (intercept_t* in)
1162 int side;
1164 if (!in->d.line->special)
1166 P_LineOpening (in->d.line);
1167 if (openrange <= 0)
1169 S_StartSound (usething, sfx_noway);
1171 // can't use through a wall
1172 return false;
1174 // not a special line, but keep checking
1175 return true ;
1178 side = 0;
1179 if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1180 side = 1;
1182 // return false; // don't use back side
1184 P_UseSpecialLine (usething, in->d.line, side);
1186 // can't use for than one special line in a row
1187 return false;
1192 // P_UseLines
1193 // Looks for special lines in front of the player to activate.
1195 void P_UseLines (player_t* player)
1197 int angle;
1198 fixed_t x1;
1199 fixed_t y1;
1200 fixed_t x2;
1201 fixed_t y2;
1203 usething = player->mo;
1205 angle = player->mo->angle >> ANGLETOFINESHIFT;
1207 x1 = player->mo->x;
1208 y1 = player->mo->y;
1209 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1210 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1212 P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1217 // RADIUS ATTACK
1219 mobj_t* bombsource;
1220 mobj_t* bombspot;
1221 int bombdamage;
1225 // PIT_RadiusAttack
1226 // "bombsource" is the creature
1227 // that caused the explosion at "bombspot".
1229 boolean PIT_RadiusAttack (mobj_t* thing)
1231 fixed_t dx;
1232 fixed_t dy;
1233 fixed_t dist;
1235 if (!(thing->flags & MF_SHOOTABLE) )
1236 return true;
1238 // Boss spider and cyborg
1239 // take no damage from concussion.
1240 if (thing->type == MT_CYBORG
1241 || thing->type == MT_SPIDER)
1242 return true;
1244 dx = abs(thing->x - bombspot->x);
1245 dy = abs(thing->y - bombspot->y);
1247 dist = dx>dy ? dx : dy;
1248 dist = (dist - thing->radius) >> FRACBITS;
1250 if (dist < 0)
1251 dist = 0;
1253 if (dist >= bombdamage)
1254 return true; // out of range
1256 if ( P_CheckSight (thing, bombspot) )
1258 // must be in direct path
1259 P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1262 return true;
1267 // P_RadiusAttack
1268 // Source is the creature that caused the explosion at spot.
1270 void
1271 P_RadiusAttack
1272 ( mobj_t* spot,
1273 mobj_t* source,
1274 int damage )
1276 int x;
1277 int y;
1279 int xl;
1280 int xh;
1281 int yl;
1282 int yh;
1284 fixed_t dist;
1286 dist = (damage+MAXRADIUS)<<FRACBITS;
1287 yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1288 yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1289 xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1290 xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1291 bombspot = spot;
1292 bombsource = source;
1293 bombdamage = damage;
1295 for (y=yl ; y<=yh ; y++)
1296 for (x=xl ; x<=xh ; x++)
1297 P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1303 // SECTOR HEIGHT CHANGING
1304 // After modifying a sectors floor or ceiling height,
1305 // call this routine to adjust the positions
1306 // of all things that touch the sector.
1308 // If anything doesn't fit anymore, true will be returned.
1309 // If crunch is true, they will take damage
1310 // as they are being crushed.
1311 // If Crunch is false, you should set the sector height back
1312 // the way it was and call P_ChangeSector again
1313 // to undo the changes.
1315 boolean crushchange;
1316 boolean nofit;
1320 // PIT_ChangeSector
1322 boolean PIT_ChangeSector (mobj_t* thing)
1324 mobj_t* mo;
1326 if (P_ThingHeightClip (thing))
1328 // keep checking
1329 return true;
1333 // crunch bodies to giblets
1334 if (thing->health <= 0)
1336 P_SetMobjState (thing, S_GIBS);
1338 thing->flags &= ~MF_SOLID;
1339 thing->height = 0;
1340 thing->radius = 0;
1342 // keep checking
1343 return true;
1346 // crunch dropped items
1347 if (thing->flags & MF_DROPPED)
1349 P_RemoveMobj (thing);
1351 // keep checking
1352 return true;
1355 if (! (thing->flags & MF_SHOOTABLE) )
1357 // assume it is bloody gibs or something
1358 return true;
1361 nofit = true;
1363 if (crushchange && !(leveltime&3) )
1365 P_DamageMobj(thing,NULL,NULL,10);
1367 // spray blood in a random direction
1368 mo = P_SpawnMobj (thing->x,
1369 thing->y,
1370 thing->z + thing->height/2, MT_BLOOD);
1372 mo->momx = (P_Random() - P_Random ())<<12;
1373 mo->momy = (P_Random() - P_Random ())<<12;
1376 // keep checking (crush other things)
1377 return true;
1383 // P_ChangeSector
1385 boolean
1386 P_ChangeSector
1387 ( sector_t* sector,
1388 boolean crunch )
1390 int x;
1391 int y;
1393 nofit = false;
1394 crushchange = crunch;
1396 // re-check heights for all things near the moving sector
1397 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1398 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1399 P_BlockThingsIterator (x, y, PIT_ChangeSector);
1402 return nofit;
1405 // Code to emulate the behavior of Vanilla Doom when encountering an overrun
1406 // of the spechit array. This is by Andrey Budko (e6y) and comes from his
1407 // PrBoom plus port. A big thanks to Andrey for this.
1409 static void SpechitOverrun(line_t *ld)
1411 static unsigned int baseaddr = 0;
1412 unsigned int addr;
1414 if (baseaddr == 0)
1416 int p;
1418 // This is the first time we have had an overrun. Work out
1419 // what base address we are going to use.
1420 // Allow a spechit value to be specified on the command line.
1423 // @category compat
1424 // @arg <n>
1426 // Use the specified magic value when emulating spechit overruns.
1429 p = M_CheckParm("-spechit");
1431 if (p > 0)
1433 M_StrToInt(myargv[p+1], (int *) &baseaddr);
1435 else
1437 baseaddr = DEFAULT_SPECHIT_MAGIC;
1441 // Calculate address used in doom2.exe
1443 addr = baseaddr + (ld - lines) * 0x3E;
1445 switch(numspechit)
1447 case 9:
1448 case 10:
1449 case 11:
1450 case 12:
1451 tmbbox[numspechit-9] = addr;
1452 break;
1453 case 13:
1454 crushchange = addr;
1455 break;
1456 case 14:
1457 nofit = addr;
1458 break;
1459 default:
1460 fprintf(stderr, "SpechitOverrun: Warning: unable to emulate"
1461 "an overrun where numspechit=%i\n",
1462 numspechit);
1463 break;