summaryrefslogtreecommitdiff
path: root/src/p_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/p_map.c')
-rw-r--r--src/p_map.c1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/src/p_map.c b/src/p_map.c
new file mode 100644
index 00000000..4b437ab1
--- /dev/null
+++ b/src/p_map.c
@@ -0,0 +1,1342 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// $Id: p_map.c 4 2005-07-23 16:19:41Z fraggle $
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+//
+// This source is available for distribution and/or modification
+// only under the terms of the DOOM Source Code License as
+// published by id Software. All rights reserved.
+//
+// The source is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
+// for more details.
+//
+// $Log$
+// Revision 1.1 2005/07/23 16:20:39 fraggle
+// Initial revision
+//
+//
+// DESCRIPTION:
+// Movement, collision handling.
+// Shooting and aiming.
+//
+//-----------------------------------------------------------------------------
+
+static const char
+rcsid[] = "$Id: p_map.c 4 2005-07-23 16:19:41Z fraggle $";
+
+#include <stdlib.h>
+
+#include "m_bbox.h"
+#include "m_random.h"
+#include "i_system.h"
+
+#include "doomdef.h"
+#include "p_local.h"
+
+#include "s_sound.h"
+
+// State.
+#include "doomstat.h"
+#include "r_state.h"
+// Data.
+#include "sounds.h"
+
+
+fixed_t tmbbox[4];
+mobj_t* tmthing;
+int tmflags;
+fixed_t tmx;
+fixed_t tmy;
+
+
+// If "floatok" true, move would be ok
+// if within "tmfloorz - tmceilingz".
+boolean floatok;
+
+fixed_t tmfloorz;
+fixed_t tmceilingz;
+fixed_t tmdropoffz;
+
+// keep track of the line that lowers the ceiling,
+// so missiles don't explode against sky hack walls
+line_t* ceilingline;
+
+// keep track of special lines as they are hit,
+// but don't process them until the move is proven valid
+#define MAXSPECIALCROSS 8
+
+line_t* spechit[MAXSPECIALCROSS];
+int numspechit;
+
+
+
+//
+// TELEPORT MOVE
+//
+
+//
+// PIT_StompThing
+//
+boolean PIT_StompThing (mobj_t* thing)
+{
+ fixed_t blockdist;
+
+ if (!(thing->flags & MF_SHOOTABLE) )
+ return true;
+
+ blockdist = thing->radius + tmthing->radius;
+
+ if ( abs(thing->x - tmx) >= blockdist
+ || abs(thing->y - tmy) >= blockdist )
+ {
+ // didn't hit it
+ return true;
+ }
+
+ // don't clip against self
+ if (thing == tmthing)
+ return true;
+
+ // monsters don't stomp things except on boss level
+ if ( !tmthing->player && gamemap != 30)
+ return false;
+
+ P_DamageMobj (thing, tmthing, tmthing, 10000);
+
+ return true;
+}
+
+
+//
+// P_TeleportMove
+//
+boolean
+P_TeleportMove
+( mobj_t* thing,
+ fixed_t x,
+ fixed_t y )
+{
+ int xl;
+ int xh;
+ int yl;
+ int yh;
+ int bx;
+ int by;
+
+ subsector_t* newsubsec;
+
+ // kill anything occupying the position
+ tmthing = thing;
+ tmflags = thing->flags;
+
+ tmx = x;
+ tmy = y;
+
+ tmbbox[BOXTOP] = y + tmthing->radius;
+ tmbbox[BOXBOTTOM] = y - tmthing->radius;
+ tmbbox[BOXRIGHT] = x + tmthing->radius;
+ tmbbox[BOXLEFT] = x - tmthing->radius;
+
+ newsubsec = R_PointInSubsector (x,y);
+ ceilingline = NULL;
+
+ // The base floor/ceiling is from the subsector
+ // that contains the point.
+ // Any contacted lines the step closer together
+ // will adjust them.
+ tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
+ tmceilingz = newsubsec->sector->ceilingheight;
+
+ validcount++;
+ numspechit = 0;
+
+ // stomp on any things contacted
+ xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
+ xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
+ yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
+ yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
+
+ for (bx=xl ; bx<=xh ; bx++)
+ for (by=yl ; by<=yh ; by++)
+ if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
+ return false;
+
+ // the move is ok,
+ // so link the thing into its new position
+ P_UnsetThingPosition (thing);
+
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+ thing->x = x;
+ thing->y = y;
+
+ P_SetThingPosition (thing);
+
+ return true;
+}
+
+
+//
+// MOVEMENT ITERATOR FUNCTIONS
+//
+
+
+//
+// PIT_CheckLine
+// Adjusts tmfloorz and tmceilingz as lines are contacted
+//
+boolean PIT_CheckLine (line_t* ld)
+{
+ if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
+ || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
+ || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
+ || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
+ return true;
+
+ if (P_BoxOnLineSide (tmbbox, ld) != -1)
+ return true;
+
+ // A line has been hit
+
+ // The moving thing's destination position will cross
+ // the given line.
+ // If this should not be allowed, return false.
+ // If the line is special, keep track of it
+ // to process later if the move is proven ok.
+ // NOTE: specials are NOT sorted by order,
+ // so two special lines that are only 8 pixels apart
+ // could be crossed in either order.
+
+ if (!ld->backsector)
+ return false; // one sided line
+
+ if (!(tmthing->flags & MF_MISSILE) )
+ {
+ if ( ld->flags & ML_BLOCKING )
+ return false; // explicitly blocking everything
+
+ if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )
+ return false; // block monsters only
+ }
+
+ // set openrange, opentop, openbottom
+ P_LineOpening (ld);
+
+ // adjust floor / ceiling heights
+ if (opentop < tmceilingz)
+ {
+ tmceilingz = opentop;
+ ceilingline = ld;
+ }
+
+ if (openbottom > tmfloorz)
+ tmfloorz = openbottom;
+
+ if (lowfloor < tmdropoffz)
+ tmdropoffz = lowfloor;
+
+ // if contacted a special line, add it to the list
+ if (ld->special)
+ {
+ spechit[numspechit] = ld;
+ numspechit++;
+ }
+
+ return true;
+}
+
+//
+// PIT_CheckThing
+//
+boolean PIT_CheckThing (mobj_t* thing)
+{
+ fixed_t blockdist;
+ boolean solid;
+ int damage;
+
+ if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
+ return true;
+
+ blockdist = thing->radius + tmthing->radius;
+
+ if ( abs(thing->x - tmx) >= blockdist
+ || abs(thing->y - tmy) >= blockdist )
+ {
+ // didn't hit it
+ return true;
+ }
+
+ // don't clip against self
+ if (thing == tmthing)
+ return true;
+
+ // check for skulls slamming into things
+ if (tmthing->flags & MF_SKULLFLY)
+ {
+ damage = ((P_Random()%8)+1)*tmthing->info->damage;
+
+ P_DamageMobj (thing, tmthing, tmthing, damage);
+
+ tmthing->flags &= ~MF_SKULLFLY;
+ tmthing->momx = tmthing->momy = tmthing->momz = 0;
+
+ P_SetMobjState (tmthing, tmthing->info->spawnstate);
+
+ return false; // stop moving
+ }
+
+
+ // missiles can hit other things
+ if (tmthing->flags & MF_MISSILE)
+ {
+ // see if it went over / under
+ if (tmthing->z > thing->z + thing->height)
+ return true; // overhead
+ if (tmthing->z+tmthing->height < thing->z)
+ return true; // underneath
+
+ if (tmthing->target && (
+ tmthing->target->type == thing->type ||
+ (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
+ (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
+ {
+ // Don't hit same species as originator.
+ if (thing == tmthing->target)
+ return true;
+
+ if (thing->type != MT_PLAYER)
+ {
+ // Explode, but do no damage.
+ // Let players missile other players.
+ return false;
+ }
+ }
+
+ if (! (thing->flags & MF_SHOOTABLE) )
+ {
+ // didn't do any damage
+ return !(thing->flags & MF_SOLID);
+ }
+
+ // damage / explode
+ damage = ((P_Random()%8)+1)*tmthing->info->damage;
+ P_DamageMobj (thing, tmthing, tmthing->target, damage);
+
+ // don't traverse any more
+ return false;
+ }
+
+ // check for special pickup
+ if (thing->flags & MF_SPECIAL)
+ {
+ solid = thing->flags&MF_SOLID;
+ if (tmflags&MF_PICKUP)
+ {
+ // can remove thing
+ P_TouchSpecialThing (thing, tmthing);
+ }
+ return !solid;
+ }
+
+ return !(thing->flags & MF_SOLID);
+}
+
+
+//
+// MOVEMENT CLIPPING
+//
+
+//
+// P_CheckPosition
+// This is purely informative, nothing is modified
+// (except things picked up).
+//
+// in:
+// a mobj_t (can be valid or invalid)
+// a position to be checked
+// (doesn't need to be related to the mobj_t->x,y)
+//
+// during:
+// special things are touched if MF_PICKUP
+// early out on solid lines?
+//
+// out:
+// newsubsec
+// floorz
+// ceilingz
+// tmdropoffz
+// the lowest point contacted
+// (monsters won't move to a dropoff)
+// speciallines[]
+// numspeciallines
+//
+boolean
+P_CheckPosition
+( mobj_t* thing,
+ fixed_t x,
+ fixed_t y )
+{
+ int xl;
+ int xh;
+ int yl;
+ int yh;
+ int bx;
+ int by;
+ subsector_t* newsubsec;
+
+ tmthing = thing;
+ tmflags = thing->flags;
+
+ tmx = x;
+ tmy = y;
+
+ tmbbox[BOXTOP] = y + tmthing->radius;
+ tmbbox[BOXBOTTOM] = y - tmthing->radius;
+ tmbbox[BOXRIGHT] = x + tmthing->radius;
+ tmbbox[BOXLEFT] = x - tmthing->radius;
+
+ newsubsec = R_PointInSubsector (x,y);
+ ceilingline = NULL;
+
+ // The base floor / ceiling is from the subsector
+ // that contains the point.
+ // Any contacted lines the step closer together
+ // will adjust them.
+ tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
+ tmceilingz = newsubsec->sector->ceilingheight;
+
+ validcount++;
+ numspechit = 0;
+
+ if ( tmflags & MF_NOCLIP )
+ return true;
+
+ // Check things first, possibly picking things up.
+ // The bounding box is extended by MAXRADIUS
+ // because mobj_ts are grouped into mapblocks
+ // based on their origin point, and can overlap
+ // into adjacent blocks by up to MAXRADIUS units.
+ xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
+ xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
+ yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
+ yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
+
+ for (bx=xl ; bx<=xh ; bx++)
+ for (by=yl ; by<=yh ; by++)
+ if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
+ return false;
+
+ // check lines
+ xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
+ xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
+ yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
+ yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
+
+ for (bx=xl ; bx<=xh ; bx++)
+ for (by=yl ; by<=yh ; by++)
+ if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
+ return false;
+
+ return true;
+}
+
+
+//
+// P_TryMove
+// Attempt to move to a new position,
+// crossing special lines unless MF_TELEPORT is set.
+//
+boolean
+P_TryMove
+( mobj_t* thing,
+ fixed_t x,
+ fixed_t y )
+{
+ fixed_t oldx;
+ fixed_t oldy;
+ int side;
+ int oldside;
+ line_t* ld;
+
+ floatok = false;
+ if (!P_CheckPosition (thing, x, y))
+ return false; // solid wall or thing
+
+ if ( !(thing->flags & MF_NOCLIP) )
+ {
+ if (tmceilingz - tmfloorz < thing->height)
+ return false; // doesn't fit
+
+ floatok = true;
+
+ if ( !(thing->flags&MF_TELEPORT)
+ &&tmceilingz - thing->z < thing->height)
+ return false; // mobj must lower itself to fit
+
+ if ( !(thing->flags&MF_TELEPORT)
+ && tmfloorz - thing->z > 24*FRACUNIT )
+ return false; // too big a step up
+
+ if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
+ && tmfloorz - tmdropoffz > 24*FRACUNIT )
+ return false; // don't stand over a dropoff
+ }
+
+ // the move is ok,
+ // so link the thing into its new position
+ P_UnsetThingPosition (thing);
+
+ oldx = thing->x;
+ oldy = thing->y;
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+ thing->x = x;
+ thing->y = y;
+
+ P_SetThingPosition (thing);
+
+ // if any special lines were hit, do the effect
+ if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
+ {
+ while (numspechit--)
+ {
+ // see if the line was crossed
+ ld = spechit[numspechit];
+ side = P_PointOnLineSide (thing->x, thing->y, ld);
+ oldside = P_PointOnLineSide (oldx, oldy, ld);
+ if (side != oldside)
+ {
+ if (ld->special)
+ P_CrossSpecialLine (ld-lines, oldside, thing);
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//
+// P_ThingHeightClip
+// Takes a valid thing and adjusts the thing->floorz,
+// thing->ceilingz, and possibly thing->z.
+// This is called for all nearby monsters
+// whenever a sector changes height.
+// If the thing doesn't fit,
+// the z will be set to the lowest value
+// and false will be returned.
+//
+boolean P_ThingHeightClip (mobj_t* thing)
+{
+ boolean onfloor;
+
+ onfloor = (thing->z == thing->floorz);
+
+ P_CheckPosition (thing, thing->x, thing->y);
+ // what about stranding a monster partially off an edge?
+
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+
+ if (onfloor)
+ {
+ // walking monsters rise and fall with the floor
+ thing->z = thing->floorz;
+ }
+ else
+ {
+ // don't adjust a floating monster unless forced to
+ if (thing->z+thing->height > thing->ceilingz)
+ thing->z = thing->ceilingz - thing->height;
+ }
+
+ if (thing->ceilingz - thing->floorz < thing->height)
+ return false;
+
+ return true;
+}
+
+
+
+//
+// SLIDE MOVE
+// Allows the player to slide along any angled walls.
+//
+fixed_t bestslidefrac;
+fixed_t secondslidefrac;
+
+line_t* bestslideline;
+line_t* secondslideline;
+
+mobj_t* slidemo;
+
+fixed_t tmxmove;
+fixed_t tmymove;
+
+
+
+//
+// P_HitSlideLine
+// Adjusts the xmove / ymove
+// so that the next move will slide along the wall.
+//
+void P_HitSlideLine (line_t* ld)
+{
+ int side;
+
+ angle_t lineangle;
+ angle_t moveangle;
+ angle_t deltaangle;
+
+ fixed_t movelen;
+ fixed_t newlen;
+
+
+ if (ld->slopetype == ST_HORIZONTAL)
+ {
+ tmymove = 0;
+ return;
+ }
+
+ if (ld->slopetype == ST_VERTICAL)
+ {
+ tmxmove = 0;
+ return;
+ }
+
+ side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
+
+ lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
+
+ if (side == 1)
+ lineangle += ANG180;
+
+ moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
+ deltaangle = moveangle-lineangle;
+
+ if (deltaangle > ANG180)
+ deltaangle += ANG180;
+ // I_Error ("SlideLine: ang>ANG180");
+
+ lineangle >>= ANGLETOFINESHIFT;
+ deltaangle >>= ANGLETOFINESHIFT;
+
+ movelen = P_AproxDistance (tmxmove, tmymove);
+ newlen = FixedMul (movelen, finecosine[deltaangle]);
+
+ tmxmove = FixedMul (newlen, finecosine[lineangle]);
+ tmymove = FixedMul (newlen, finesine[lineangle]);
+}
+
+
+//
+// PTR_SlideTraverse
+//
+boolean PTR_SlideTraverse (intercept_t* in)
+{
+ line_t* li;
+
+ if (!in->isaline)
+ I_Error ("PTR_SlideTraverse: not a line?");
+
+ li = in->d.line;
+
+ if ( ! (li->flags & ML_TWOSIDED) )
+ {
+ if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
+ {
+ // don't hit the back side
+ return true;
+ }
+ goto isblocking;
+ }
+
+ // set openrange, opentop, openbottom
+ P_LineOpening (li);
+
+ if (openrange < slidemo->height)
+ goto isblocking; // doesn't fit
+
+ if (opentop - slidemo->z < slidemo->height)
+ goto isblocking; // mobj is too high
+
+ if (openbottom - slidemo->z > 24*FRACUNIT )
+ goto isblocking; // too big a step up
+
+ // this line doesn't block movement
+ return true;
+
+ // the line does block movement,
+ // see if it is closer than best so far
+ isblocking:
+ if (in->frac < bestslidefrac)
+ {
+ secondslidefrac = bestslidefrac;
+ secondslideline = bestslideline;
+ bestslidefrac = in->frac;
+ bestslideline = li;
+ }
+
+ return false; // stop
+}
+
+
+
+//
+// P_SlideMove
+// The momx / momy move is bad, so try to slide
+// along a wall.
+// Find the first line hit, move flush to it,
+// and slide along it
+//
+// This is a kludgy mess.
+//
+void P_SlideMove (mobj_t* mo)
+{
+ fixed_t leadx;
+ fixed_t leady;
+ fixed_t trailx;
+ fixed_t traily;
+ fixed_t newx;
+ fixed_t newy;
+ int hitcount;
+
+ slidemo = mo;
+ hitcount = 0;
+
+ retry:
+ if (++hitcount == 3)
+ goto stairstep; // don't loop forever
+
+
+ // trace along the three leading corners
+ if (mo->momx > 0)
+ {
+ leadx = mo->x + mo->radius;
+ trailx = mo->x - mo->radius;
+ }
+ else
+ {
+ leadx = mo->x - mo->radius;
+ trailx = mo->x + mo->radius;
+ }
+
+ if (mo->momy > 0)
+ {
+ leady = mo->y + mo->radius;
+ traily = mo->y - mo->radius;
+ }
+ else
+ {
+ leady = mo->y - mo->radius;
+ traily = mo->y + mo->radius;
+ }
+
+ bestslidefrac = FRACUNIT+1;
+
+ P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
+ PT_ADDLINES, PTR_SlideTraverse );
+ P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
+ PT_ADDLINES, PTR_SlideTraverse );
+ P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
+ PT_ADDLINES, PTR_SlideTraverse );
+
+ // move up to the wall
+ if (bestslidefrac == FRACUNIT+1)
+ {
+ // the move most have hit the middle, so stairstep
+ stairstep:
+ if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
+ P_TryMove (mo, mo->x + mo->momx, mo->y);
+ return;
+ }
+
+ // fudge a bit to make sure it doesn't hit
+ bestslidefrac -= 0x800;
+ if (bestslidefrac > 0)
+ {
+ newx = FixedMul (mo->momx, bestslidefrac);
+ newy = FixedMul (mo->momy, bestslidefrac);
+
+ if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
+ goto stairstep;
+ }
+
+ // Now continue along the wall.
+ // First calculate remainder.
+ bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
+
+ if (bestslidefrac > FRACUNIT)
+ bestslidefrac = FRACUNIT;
+
+ if (bestslidefrac <= 0)
+ return;
+
+ tmxmove = FixedMul (mo->momx, bestslidefrac);
+ tmymove = FixedMul (mo->momy, bestslidefrac);
+
+ P_HitSlideLine (bestslideline); // clip the moves
+
+ mo->momx = tmxmove;
+ mo->momy = tmymove;
+
+ if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
+ {
+ goto retry;
+ }
+}
+
+
+//
+// P_LineAttack
+//
+mobj_t* linetarget; // who got hit (or NULL)
+mobj_t* shootthing;
+
+// Height if not aiming up or down
+// ???: use slope for monsters?
+fixed_t shootz;
+
+int la_damage;
+fixed_t attackrange;
+
+fixed_t aimslope;
+
+// slopes to top and bottom of target
+extern fixed_t topslope;
+extern fixed_t bottomslope;
+
+
+//
+// PTR_AimTraverse
+// Sets linetaget and aimslope when a target is aimed at.
+//
+boolean
+PTR_AimTraverse (intercept_t* in)
+{
+ line_t* li;
+ mobj_t* th;
+ fixed_t slope;
+ fixed_t thingtopslope;
+ fixed_t thingbottomslope;
+ fixed_t dist;
+
+ if (in->isaline)
+ {
+ li = in->d.line;
+
+ if ( !(li->flags & ML_TWOSIDED) )
+ return false; // stop
+
+ // Crosses a two sided line.
+ // A two sided line will restrict
+ // the possible target ranges.
+ P_LineOpening (li);
+
+ if (openbottom >= opentop)
+ return false; // stop
+
+ dist = FixedMul (attackrange, in->frac);
+
+ if (li->frontsector->floorheight != li->backsector->floorheight)
+ {
+ slope = FixedDiv (openbottom - shootz , dist);
+ if (slope > bottomslope)
+ bottomslope = slope;
+ }
+
+ if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+ {
+ slope = FixedDiv (opentop - shootz , dist);
+ if (slope < topslope)
+ topslope = slope;
+ }
+
+ if (topslope <= bottomslope)
+ return false; // stop
+
+ return true; // shot continues
+ }
+
+ // shoot a thing
+ th = in->d.thing;
+ if (th == shootthing)
+ return true; // can't shoot self
+
+ if (!(th->flags&MF_SHOOTABLE))
+ return true; // corpse or something
+
+ // check angles to see if the thing can be aimed at
+ dist = FixedMul (attackrange, in->frac);
+ thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
+
+ if (thingtopslope < bottomslope)
+ return true; // shot over the thing
+
+ thingbottomslope = FixedDiv (th->z - shootz, dist);
+
+ if (thingbottomslope > topslope)
+ return true; // shot under the thing
+
+ // this thing can be hit!
+ if (thingtopslope > topslope)
+ thingtopslope = topslope;
+
+ if (thingbottomslope < bottomslope)
+ thingbottomslope = bottomslope;
+
+ aimslope = (thingtopslope+thingbottomslope)/2;
+ linetarget = th;
+
+ return false; // don't go any farther
+}
+
+
+//
+// PTR_ShootTraverse
+//
+boolean PTR_ShootTraverse (intercept_t* in)
+{
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+ fixed_t frac;
+
+ line_t* li;
+
+ mobj_t* th;
+
+ fixed_t slope;
+ fixed_t dist;
+ fixed_t thingtopslope;
+ fixed_t thingbottomslope;
+
+ if (in->isaline)
+ {
+ li = in->d.line;
+
+ if (li->special)
+ P_ShootSpecialLine (shootthing, li);
+
+ if ( !(li->flags & ML_TWOSIDED) )
+ goto hitline;
+
+ // crosses a two sided line
+ P_LineOpening (li);
+
+ dist = FixedMul (attackrange, in->frac);
+
+ if (li->frontsector->floorheight != li->backsector->floorheight)
+ {
+ slope = FixedDiv (openbottom - shootz , dist);
+ if (slope > aimslope)
+ goto hitline;
+ }
+
+ if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+ {
+ slope = FixedDiv (opentop - shootz , dist);
+ if (slope < aimslope)
+ goto hitline;
+ }
+
+ // shot continues
+ return true;
+
+
+ // hit line
+ hitline:
+ // position a bit closer
+ frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
+ x = trace.x + FixedMul (trace.dx, frac);
+ y = trace.y + FixedMul (trace.dy, frac);
+ z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
+
+ if (li->frontsector->ceilingpic == skyflatnum)
+ {
+ // don't shoot the sky!
+ if (z > li->frontsector->ceilingheight)
+ return false;
+
+ // it's a sky hack wall
+ if (li->backsector && li->backsector->ceilingpic == skyflatnum)
+ return false;
+ }
+
+ // Spawn bullet puffs.
+ P_SpawnPuff (x,y,z);
+
+ // don't go any farther
+ return false;
+ }
+
+ // shoot a thing
+ th = in->d.thing;
+ if (th == shootthing)
+ return true; // can't shoot self
+
+ if (!(th->flags&MF_SHOOTABLE))
+ return true; // corpse or something
+
+ // check angles to see if the thing can be aimed at
+ dist = FixedMul (attackrange, in->frac);
+ thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
+
+ if (thingtopslope < aimslope)
+ return true; // shot over the thing
+
+ thingbottomslope = FixedDiv (th->z - shootz, dist);
+
+ if (thingbottomslope > aimslope)
+ return true; // shot under the thing
+
+
+ // hit thing
+ // position a bit closer
+ frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
+
+ x = trace.x + FixedMul (trace.dx, frac);
+ y = trace.y + FixedMul (trace.dy, frac);
+ z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
+
+ // Spawn bullet puffs or blod spots,
+ // depending on target type.
+ if (in->d.thing->flags & MF_NOBLOOD)
+ P_SpawnPuff (x,y,z);
+ else
+ P_SpawnBlood (x,y,z, la_damage);
+
+ if (la_damage)
+ P_DamageMobj (th, shootthing, shootthing, la_damage);
+
+ // don't go any farther
+ return false;
+
+}
+
+
+//
+// P_AimLineAttack
+//
+fixed_t
+P_AimLineAttack
+( mobj_t* t1,
+ angle_t angle,
+ fixed_t distance )
+{
+ fixed_t x2;
+ fixed_t y2;
+
+ angle >>= ANGLETOFINESHIFT;
+ shootthing = t1;
+
+ x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
+ y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
+ shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
+
+ // can't shoot outside view angles
+ topslope = 100*FRACUNIT/160;
+ bottomslope = -100*FRACUNIT/160;
+
+ attackrange = distance;
+ linetarget = NULL;
+
+ P_PathTraverse ( t1->x, t1->y,
+ x2, y2,
+ PT_ADDLINES|PT_ADDTHINGS,
+ PTR_AimTraverse );
+
+ if (linetarget)
+ return aimslope;
+
+ return 0;
+}
+
+
+//
+// P_LineAttack
+// If damage == 0, it is just a test trace
+// that will leave linetarget set.
+//
+void
+P_LineAttack
+( mobj_t* t1,
+ angle_t angle,
+ fixed_t distance,
+ fixed_t slope,
+ int damage )
+{
+ fixed_t x2;
+ fixed_t y2;
+
+ angle >>= ANGLETOFINESHIFT;
+ shootthing = t1;
+ la_damage = damage;
+ x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
+ y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
+ shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
+ attackrange = distance;
+ aimslope = slope;
+
+ P_PathTraverse ( t1->x, t1->y,
+ x2, y2,
+ PT_ADDLINES|PT_ADDTHINGS,
+ PTR_ShootTraverse );
+}
+
+
+
+//
+// USE LINES
+//
+mobj_t* usething;
+
+boolean PTR_UseTraverse (intercept_t* in)
+{
+ int side;
+
+ if (!in->d.line->special)
+ {
+ P_LineOpening (in->d.line);
+ if (openrange <= 0)
+ {
+ S_StartSound (usething, sfx_noway);
+
+ // can't use through a wall
+ return false;
+ }
+ // not a special line, but keep checking
+ return true ;
+ }
+
+ side = 0;
+ if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
+ side = 1;
+
+ // return false; // don't use back side
+
+ P_UseSpecialLine (usething, in->d.line, side);
+
+ // can't use for than one special line in a row
+ return false;
+}
+
+
+//
+// P_UseLines
+// Looks for special lines in front of the player to activate.
+//
+void P_UseLines (player_t* player)
+{
+ int angle;
+ fixed_t x1;
+ fixed_t y1;
+ fixed_t x2;
+ fixed_t y2;
+
+ usething = player->mo;
+
+ angle = player->mo->angle >> ANGLETOFINESHIFT;
+
+ x1 = player->mo->x;
+ y1 = player->mo->y;
+ x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
+ y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
+
+ P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
+}
+
+
+//
+// RADIUS ATTACK
+//
+mobj_t* bombsource;
+mobj_t* bombspot;
+int bombdamage;
+
+
+//
+// PIT_RadiusAttack
+// "bombsource" is the creature
+// that caused the explosion at "bombspot".
+//
+boolean PIT_RadiusAttack (mobj_t* thing)
+{
+ fixed_t dx;
+ fixed_t dy;
+ fixed_t dist;
+
+ if (!(thing->flags & MF_SHOOTABLE) )
+ return true;
+
+ // Boss spider and cyborg
+ // take no damage from concussion.
+ if (thing->type == MT_CYBORG
+ || thing->type == MT_SPIDER)
+ return true;
+
+ dx = abs(thing->x - bombspot->x);
+ dy = abs(thing->y - bombspot->y);
+
+ dist = dx>dy ? dx : dy;
+ dist = (dist - thing->radius) >> FRACBITS;
+
+ if (dist < 0)
+ dist = 0;
+
+ if (dist >= bombdamage)
+ return true; // out of range
+
+ if ( P_CheckSight (thing, bombspot) )
+ {
+ // must be in direct path
+ P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
+ }
+
+ return true;
+}
+
+
+//
+// P_RadiusAttack
+// Source is the creature that caused the explosion at spot.
+//
+void
+P_RadiusAttack
+( mobj_t* spot,
+ mobj_t* source,
+ int damage )
+{
+ int x;
+ int y;
+
+ int xl;
+ int xh;
+ int yl;
+ int yh;
+
+ fixed_t dist;
+
+ dist = (damage+MAXRADIUS)<<FRACBITS;
+ yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
+ yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
+ xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
+ xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
+ bombspot = spot;
+ bombsource = source;
+ bombdamage = damage;
+
+ for (y=yl ; y<=yh ; y++)
+ for (x=xl ; x<=xh ; x++)
+ P_BlockThingsIterator (x, y, PIT_RadiusAttack );
+}
+
+
+
+//
+// SECTOR HEIGHT CHANGING
+// After modifying a sectors floor or ceiling height,
+// call this routine to adjust the positions
+// of all things that touch the sector.
+//
+// If anything doesn't fit anymore, true will be returned.
+// If crunch is true, they will take damage
+// as they are being crushed.
+// If Crunch is false, you should set the sector height back
+// the way it was and call P_ChangeSector again
+// to undo the changes.
+//
+boolean crushchange;
+boolean nofit;
+
+
+//
+// PIT_ChangeSector
+//
+boolean PIT_ChangeSector (mobj_t* thing)
+{
+ mobj_t* mo;
+
+ if (P_ThingHeightClip (thing))
+ {
+ // keep checking
+ return true;
+ }
+
+
+ // crunch bodies to giblets
+ if (thing->health <= 0)
+ {
+ P_SetMobjState (thing, S_GIBS);
+
+ thing->flags &= ~MF_SOLID;
+ thing->height = 0;
+ thing->radius = 0;
+
+ // keep checking
+ return true;
+ }
+
+ // crunch dropped items
+ if (thing->flags & MF_DROPPED)
+ {
+ P_RemoveMobj (thing);
+
+ // keep checking
+ return true;
+ }
+
+ if (! (thing->flags & MF_SHOOTABLE) )
+ {
+ // assume it is bloody gibs or something
+ return true;
+ }
+
+ nofit = true;
+
+ if (crushchange && !(leveltime&3) )
+ {
+ P_DamageMobj(thing,NULL,NULL,10);
+
+ // spray blood in a random direction
+ mo = P_SpawnMobj (thing->x,
+ thing->y,
+ thing->z + thing->height/2, MT_BLOOD);
+
+ mo->momx = (P_Random() - P_Random ())<<12;
+ mo->momy = (P_Random() - P_Random ())<<12;
+ }
+
+ // keep checking (crush other things)
+ return true;
+}
+
+
+
+//
+// P_ChangeSector
+//
+boolean
+P_ChangeSector
+( sector_t* sector,
+ boolean crunch )
+{
+ int x;
+ int y;
+
+ nofit = false;
+ crushchange = crunch;
+
+ // re-check heights for all things near the moving sector
+ for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
+ for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
+ P_BlockThingsIterator (x, y, PIT_ChangeSector);
+
+
+ return nofit;
+}
+