summaryrefslogtreecommitdiff
path: root/src/heretic/p_maputl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/heretic/p_maputl.c')
-rw-r--r--src/heretic/p_maputl.c784
1 files changed, 784 insertions, 0 deletions
diff --git a/src/heretic/p_maputl.c b/src/heretic/p_maputl.c
new file mode 100644
index 00000000..3d848af2
--- /dev/null
+++ b/src/heretic/p_maputl.c
@@ -0,0 +1,784 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+
+// P_maputl.c
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "m_bbox.h"
+#include "p_local.h"
+
+
+/*
+===================
+=
+= P_AproxDistance
+=
+= Gives an estimation of distance (not exact)
+=
+===================
+*/
+
+fixed_t P_AproxDistance(fixed_t dx, fixed_t dy)
+{
+ dx = abs(dx);
+ dy = abs(dy);
+ if (dx < dy)
+ return dx + dy - (dx >> 1);
+ return dx + dy - (dy >> 1);
+}
+
+
+/*
+==================
+=
+= P_PointOnLineSide
+=
+= Returns 0 or 1
+==================
+*/
+
+int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line)
+{
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ if (!line->dx)
+ {
+ if (x <= line->v1->x)
+ return line->dy > 0;
+ return line->dy < 0;
+ }
+ if (!line->dy)
+ {
+ if (y <= line->v1->y)
+ return line->dx < 0;
+ return line->dx > 0;
+ }
+
+ dx = (x - line->v1->x);
+ dy = (y - line->v1->y);
+
+ left = FixedMul(line->dy >> FRACBITS, dx);
+ right = FixedMul(dy, line->dx >> FRACBITS);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+/*
+=================
+=
+= P_BoxOnLineSide
+=
+= Considers the line to be infinite
+= Returns side 0 or 1, -1 if box crosses the line
+=================
+*/
+
+int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld)
+{
+ int p1 = 0, p2 = 0;
+
+ switch (ld->slopetype)
+ {
+ case ST_HORIZONTAL:
+ p1 = tmbox[BOXTOP] > ld->v1->y;
+ p2 = tmbox[BOXBOTTOM] > ld->v1->y;
+ if (ld->dx < 0)
+ {
+ p1 ^= 1;
+ p2 ^= 1;
+ }
+ break;
+ case ST_VERTICAL:
+ p1 = tmbox[BOXRIGHT] < ld->v1->x;
+ p2 = tmbox[BOXLEFT] < ld->v1->x;
+ if (ld->dy < 0)
+ {
+ p1 ^= 1;
+ p2 ^= 1;
+ }
+ break;
+ case ST_POSITIVE:
+ p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld);
+ p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);
+ break;
+ case ST_NEGATIVE:
+ p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld);
+ p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);
+ break;
+ }
+
+ if (p1 == p2)
+ return p1;
+ return -1;
+}
+
+/*
+==================
+=
+= P_PointOnDivlineSide
+=
+= Returns 0 or 1
+==================
+*/
+
+int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line)
+{
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ if (!line->dx)
+ {
+ if (x <= line->x)
+ return line->dy > 0;
+ return line->dy < 0;
+ }
+ if (!line->dy)
+ {
+ if (y <= line->y)
+ return line->dx < 0;
+ return line->dx > 0;
+ }
+
+ dx = (x - line->x);
+ dy = (y - line->y);
+
+// try to quickly decide by looking at sign bits
+ if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000)
+ {
+ if ((line->dy ^ dx) & 0x80000000)
+ return 1; // (left is negative)
+ return 0;
+ }
+
+ left = FixedMul(line->dy >> 8, dx >> 8);
+ right = FixedMul(dy >> 8, line->dx >> 8);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+
+/*
+==============
+=
+= P_MakeDivline
+=
+==============
+*/
+
+void P_MakeDivline(line_t * li, divline_t * dl)
+{
+ dl->x = li->v1->x;
+ dl->y = li->v1->y;
+ dl->dx = li->dx;
+ dl->dy = li->dy;
+}
+
+
+/*
+===============
+=
+= P_InterceptVector
+=
+= Returns the fractional intercept point along the first divline
+=
+= This is only called by the addthings and addlines traversers
+===============
+*/
+
+fixed_t P_InterceptVector(divline_t * v2, divline_t * v1)
+{
+#if 1
+ fixed_t frac, num, den;
+
+ den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy);
+ if (den == 0)
+ return 0;
+// I_Error ("P_InterceptVector: parallel");
+ num = FixedMul((v1->x - v2->x) >> 8, v1->dy) +
+ FixedMul((v2->y - v1->y) >> 8, v1->dx);
+ frac = FixedDiv(num, den);
+
+ return frac;
+#else
+ float frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy;
+
+ v1x = (float) v1->x / FRACUNIT;
+ v1y = (float) v1->y / FRACUNIT;
+ v1dx = (float) v1->dx / FRACUNIT;
+ v1dy = (float) v1->dy / FRACUNIT;
+ v2x = (float) v2->x / FRACUNIT;
+ v2y = (float) v2->y / FRACUNIT;
+ v2dx = (float) v2->dx / FRACUNIT;
+ v2dy = (float) v2->dy / FRACUNIT;
+
+ den = v1dy * v2dx - v1dx * v2dy;
+ if (den == 0)
+ return 0; // parallel
+ num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx;
+ frac = num / den;
+
+ return frac * FRACUNIT;
+#endif
+}
+
+/*
+==================
+=
+= P_LineOpening
+=
+= Sets opentop and openbottom to the window through a two sided line
+= OPTIMIZE: keep this precalculated
+==================
+*/
+
+fixed_t opentop, openbottom, openrange;
+fixed_t lowfloor;
+
+void P_LineOpening(line_t * linedef)
+{
+ sector_t *front, *back;
+
+ if (linedef->sidenum[1] == -1)
+ { // single sided line
+ openrange = 0;
+ return;
+ }
+
+ front = linedef->frontsector;
+ back = linedef->backsector;
+
+ if (front->ceilingheight < back->ceilingheight)
+ opentop = front->ceilingheight;
+ else
+ opentop = back->ceilingheight;
+ if (front->floorheight > back->floorheight)
+ {
+ openbottom = front->floorheight;
+ lowfloor = back->floorheight;
+ }
+ else
+ {
+ openbottom = back->floorheight;
+ lowfloor = front->floorheight;
+ }
+
+ openrange = opentop - openbottom;
+}
+
+/*
+===============================================================================
+
+ THING POSITION SETTING
+
+===============================================================================
+*/
+
+/*
+===================
+=
+= P_UnsetThingPosition
+=
+= Unlinks a thing from block map and sectors
+=
+===================
+*/
+
+void P_UnsetThingPosition(mobj_t * thing)
+{
+ int blockx, blocky;
+
+ if (!(thing->flags & MF_NOSECTOR))
+ { // inert things don't need to be in blockmap
+// unlink from subsector
+ if (thing->snext)
+ thing->snext->sprev = thing->sprev;
+ if (thing->sprev)
+ thing->sprev->snext = thing->snext;
+ else
+ thing->subsector->sector->thinglist = thing->snext;
+ }
+
+ if (!(thing->flags & MF_NOBLOCKMAP))
+ { // inert things don't need to be in blockmap
+// unlink from block map
+ if (thing->bnext)
+ thing->bnext->bprev = thing->bprev;
+ if (thing->bprev)
+ thing->bprev->bnext = thing->bnext;
+ else
+ {
+ blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
+ blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
+ if (blockx >= 0 && blockx < bmapwidth
+ && blocky >= 0 && blocky < bmapheight)
+ blocklinks[blocky * bmapwidth + blockx] = thing->bnext;
+ }
+ }
+}
+
+
+/*
+===================
+=
+= P_SetThingPosition
+=
+= Links a thing into both a block and a subsector based on it's x y
+= Sets thing->subsector properly
+=
+===================
+*/
+
+void P_SetThingPosition(mobj_t * thing)
+{
+ subsector_t *ss;
+ sector_t *sec;
+ int blockx, blocky;
+ mobj_t **link;
+
+//
+// link into subsector
+//
+ ss = R_PointInSubsector(thing->x, thing->y);
+ thing->subsector = ss;
+ if (!(thing->flags & MF_NOSECTOR))
+ { // invisible things don't go into the sector links
+ sec = ss->sector;
+
+ thing->sprev = NULL;
+ thing->snext = sec->thinglist;
+ if (sec->thinglist)
+ sec->thinglist->sprev = thing;
+ sec->thinglist = thing;
+ }
+
+//
+// link into blockmap
+//
+ if (!(thing->flags & MF_NOBLOCKMAP))
+ { // inert things don't need to be in blockmap
+ blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
+ blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
+ if (blockx >= 0 && blockx < bmapwidth && blocky >= 0
+ && blocky < bmapheight)
+ {
+ link = &blocklinks[blocky * bmapwidth + blockx];
+ thing->bprev = NULL;
+ thing->bnext = *link;
+ if (*link)
+ (*link)->bprev = thing;
+ *link = thing;
+ }
+ else
+ { // thing is off the map
+ thing->bnext = thing->bprev = NULL;
+ }
+ }
+}
+
+
+
+/*
+===============================================================================
+
+ BLOCK MAP ITERATORS
+
+For each line/thing in the given mapblock, call the passed function.
+If the function returns false, exit with false without checking anything else.
+
+===============================================================================
+*/
+
+/*
+==================
+=
+= P_BlockLinesIterator
+=
+= The validcount flags are used to avoid checking lines
+= that are marked in multiple mapblocks, so increment validcount before
+= the first call to P_BlockLinesIterator, then make one or more calls to it
+===================
+*/
+
+boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *))
+{
+ int offset;
+ short *list;
+ line_t *ld;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return true;
+ offset = y * bmapwidth + x;
+
+ offset = *(blockmap + offset);
+
+ for (list = blockmaplump + offset; *list != -1; list++)
+ {
+ ld = &lines[*list];
+ if (ld->validcount == validcount)
+ continue; // line has already been checked
+ ld->validcount = validcount;
+
+ if (!func(ld))
+ return false;
+ }
+
+ return true; // everything was checked
+}
+
+
+/*
+==================
+=
+= P_BlockThingsIterator
+=
+==================
+*/
+
+boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *))
+{
+ mobj_t *mobj;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return true;
+
+ for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext)
+ if (!func(mobj))
+ return false;
+
+ return true;
+}
+
+/*
+===============================================================================
+
+ INTERCEPT ROUTINES
+
+===============================================================================
+*/
+
+intercept_t intercepts[MAXINTERCEPTS], *intercept_p;
+
+divline_t trace;
+boolean earlyout;
+int ptflags;
+
+/*
+==================
+=
+= PIT_AddLineIntercepts
+=
+= Looks for lines in the given block that intercept the given trace
+= to add to the intercepts list
+= A line is crossed if its endpoints are on opposite sides of the trace
+= Returns true if earlyout and a solid line hit
+==================
+*/
+
+boolean PIT_AddLineIntercepts(line_t * ld)
+{
+ int s1, s2;
+ fixed_t frac;
+ divline_t dl;
+
+// avoid precision problems with two routines
+ if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16
+ || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16)
+ {
+ s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace);
+ s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace);
+ }
+ else
+ {
+ s1 = P_PointOnLineSide(trace.x, trace.y, ld);
+ s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld);
+ }
+ if (s1 == s2)
+ return true; // line isn't crossed
+
+//
+// hit the line
+//
+ P_MakeDivline(ld, &dl);
+ frac = P_InterceptVector(&trace, &dl);
+ if (frac < 0)
+ return true; // behind source
+
+// try to early out the check
+ if (earlyout && frac < FRACUNIT && !ld->backsector)
+ return false; // stop checking
+
+ intercept_p->frac = frac;
+ intercept_p->isaline = true;
+ intercept_p->d.line = ld;
+ intercept_p++;
+
+ return true; // continue
+}
+
+
+
+/*
+==================
+=
+= PIT_AddThingIntercepts
+=
+==================
+*/
+
+boolean PIT_AddThingIntercepts(mobj_t * thing)
+{
+ fixed_t x1, y1, x2, y2;
+ int s1, s2;
+ boolean tracepositive;
+ divline_t dl;
+ fixed_t frac;
+
+ tracepositive = (trace.dx ^ trace.dy) > 0;
+
+ // check a corner to corner crossection for hit
+
+ if (tracepositive)
+ {
+ x1 = thing->x - thing->radius;
+ y1 = thing->y + thing->radius;
+
+ x2 = thing->x + thing->radius;
+ y2 = thing->y - thing->radius;
+ }
+ else
+ {
+ x1 = thing->x - thing->radius;
+ y1 = thing->y - thing->radius;
+
+ x2 = thing->x + thing->radius;
+ y2 = thing->y + thing->radius;
+ }
+ s1 = P_PointOnDivlineSide(x1, y1, &trace);
+ s2 = P_PointOnDivlineSide(x2, y2, &trace);
+ if (s1 == s2)
+ return true; // line isn't crossed
+
+ dl.x = x1;
+ dl.y = y1;
+ dl.dx = x2 - x1;
+ dl.dy = y2 - y1;
+ frac = P_InterceptVector(&trace, &dl);
+ if (frac < 0)
+ return true; // behind source
+ intercept_p->frac = frac;
+ intercept_p->isaline = false;
+ intercept_p->d.thing = thing;
+ intercept_p++;
+
+ return true; // keep going
+}
+
+
+/*
+====================
+=
+= P_TraverseIntercepts
+=
+= Returns true if the traverser function returns true for all lines
+====================
+*/
+
+boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac)
+{
+ int count;
+ fixed_t dist;
+ intercept_t *scan, *in;
+
+ count = intercept_p - intercepts;
+ in = 0; // shut up compiler warning
+
+ while (count--)
+ {
+ dist = INT_MAX;
+ for (scan = intercepts; scan < intercept_p; scan++)
+ if (scan->frac < dist)
+ {
+ dist = scan->frac;
+ in = scan;
+ }
+
+ if (dist > maxfrac)
+ return true; // checked everything in range
+#if 0
+ { // don't check these yet, ther may be others inserted
+ in = scan = intercepts;
+ for (scan = intercepts; scan < intercept_p; scan++)
+ if (scan->frac > maxfrac)
+ *in++ = *scan;
+ intercept_p = in;
+ return false;
+ }
+#endif
+
+ if (!func(in))
+ return false; // don't bother going farther
+ in->frac = INT_MAX;
+ }
+
+ return true; // everything was traversed
+}
+
+
+
+/*
+==================
+=
+= P_PathTraverse
+=
+= Traces a line from x1,y1 to x2,y2, calling the traverser function for each
+= Returns true if the traverser function returns true for all lines
+==================
+*/
+
+boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2,
+ int flags, boolean(*trav) (intercept_t *))
+{
+ fixed_t xt1, yt1, xt2, yt2;
+ fixed_t xstep, ystep;
+ fixed_t partial;
+ fixed_t xintercept, yintercept;
+ int mapx, mapy, mapxstep, mapystep;
+ int count;
+
+ earlyout = flags & PT_EARLYOUT;
+
+ validcount++;
+ intercept_p = intercepts;
+
+ if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0)
+ x1 += FRACUNIT; // don't side exactly on a line
+ if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0)
+ y1 += FRACUNIT; // don't side exactly on a line
+ trace.x = x1;
+ trace.y = y1;
+ trace.dx = x2 - x1;
+ trace.dy = y2 - y1;
+
+ x1 -= bmaporgx;
+ y1 -= bmaporgy;
+ xt1 = x1 >> MAPBLOCKSHIFT;
+ yt1 = y1 >> MAPBLOCKSHIFT;
+
+ x2 -= bmaporgx;
+ y2 -= bmaporgy;
+ xt2 = x2 >> MAPBLOCKSHIFT;
+ yt2 = y2 >> MAPBLOCKSHIFT;
+
+ if (xt2 > xt1)
+ {
+ mapxstep = 1;
+ partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1));
+ ystep = FixedDiv(y2 - y1, abs(x2 - x1));
+ }
+ else if (xt2 < xt1)
+ {
+ mapxstep = -1;
+ partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ ystep = FixedDiv(y2 - y1, abs(x2 - x1));
+ }
+ else
+ {
+ mapxstep = 0;
+ partial = FRACUNIT;
+ ystep = 256 * FRACUNIT;
+ }
+ yintercept = (y1 >> MAPBTOFRAC) + FixedMul(partial, ystep);
+
+
+ if (yt2 > yt1)
+ {
+ mapystep = 1;
+ partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1));
+ xstep = FixedDiv(x2 - x1, abs(y2 - y1));
+ }
+ else if (yt2 < yt1)
+ {
+ mapystep = -1;
+ partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ xstep = FixedDiv(x2 - x1, abs(y2 - y1));
+ }
+ else
+ {
+ mapystep = 0;
+ partial = FRACUNIT;
+ xstep = 256 * FRACUNIT;
+ }
+ xintercept = (x1 >> MAPBTOFRAC) + FixedMul(partial, xstep);
+
+
+//
+// step through map blocks
+// Count is present to prevent a round off error from skipping the break
+ mapx = xt1;
+ mapy = yt1;
+
+ for (count = 0; count < 64; count++)
+ {
+ if (flags & PT_ADDLINES)
+ {
+ if (!P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts))
+ return false; // early out
+ }
+ if (flags & PT_ADDTHINGS)
+ {
+ if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts))
+ return false; // early out
+ }
+
+ if (mapx == xt2 && mapy == yt2)
+ break;
+
+ if ((yintercept >> FRACBITS) == mapy)
+ {
+ yintercept += ystep;
+ mapx += mapxstep;
+ }
+ else if ((xintercept >> FRACBITS) == mapx)
+ {
+ xintercept += xstep;
+ mapy += mapystep;
+ }
+
+ }
+
+
+//
+// go through the sorted list
+//
+ return P_TraverseIntercepts(trav, FRACUNIT);
+}