RingRacers/src/p_maputl.c
Sally Coolatta 7dfa597c7d SRB2 -> DRRR copyright in src, acs, android folder
Be consistent with toaster's recent changes to copyright
2024-04-05 02:08:23 -04:00

1861 lines
45 KiB
C
Raw Blame History

// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Kart Krew.
// Copyright (C) 2020 by Sonic Team Junior.
// Copyright (C) 2000 by DooM Legacy Team.
// Copyright (C) 1996 by id Software, Inc.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file p_maputl.c
/// \brief Movement/collision utility functions, as used by functions in p_map.c
/// Blockmap iterator functions, and some PIT_* functions to use for iteration
#include "doomdef.h"
#include "doomstat.h"
#include "k_kart.h"
#include "p_local.h"
#include "r_main.h"
#include "r_data.h"
#include "r_textures.h"
#include "p_maputl.h"
#include "p_polyobj.h"
#include "p_slopes.h"
#include "z_zone.h"
//
// P_ClosestPointOnLine
// Finds the closest point on a given line to the supplied point
//
void P_ClosestPointOnLine(fixed_t x, fixed_t y, const line_t *line, vertex_t *result)
{
fixed_t startx = line->v1->x;
fixed_t starty = line->v1->y;
fixed_t dx = line->dx;
fixed_t dy = line->dy;
// Determine t (the length of the vector from <20>Line[0]<5D> to <20>p<EFBFBD>)
fixed_t cx, cy;
fixed_t vx, vy;
fixed_t magnitude;
fixed_t t;
//Sub (p, &Line[0], &c);
cx = x - startx;
cy = y - starty;
//Sub (&Line[1], &Line[0], &V);
vx = dx;
vy = dy;
//Normalize (&V, &V);
magnitude = R_PointToDist2(line->v2->x, line->v2->y, startx, starty);
vx = FixedDiv(vx, magnitude);
vy = FixedDiv(vy, magnitude);
t = (FixedMul(vx, cx) + FixedMul(vy, cy));
// Return the point between <20>Line[0]<5D> and <20>Line[1]<5D>
vx = FixedMul(vx, t);
vy = FixedMul(vy, t);
//Add (&Line[0], &V, out);
result->x = startx + vx;
result->y = starty + vy;
return;
}
/// Similar to FV3_ClosestPointOnLine() except it actually works.
void P_ClosestPointOnLine3D(const vector3_t *p, const vector3_t *Line, vector3_t *result)
{
const vector3_t* v1 = &Line[0];
const vector3_t* v2 = &Line[1];
vector3_t c, V, n;
fixed_t t, d;
FV3_SubEx(v2, v1, &V);
FV3_SubEx(p, v1, &c);
d = R_PointToDist2(0, v2->z, R_PointToDist2(v2->x, v2->y, v1->x, v1->y), v1->z);
FV3_Copy(&n, &V);
FV3_Divide(&n, d);
t = FV3_Dot(&n, &c);
// Set closest point to the end if it extends past -Red
if (t <= 0)
{
FV3_Copy(result, v1);
return;
}
else if (t >= d)
{
FV3_Copy(result, v2);
return;
}
FV3_Mul(&n, t);
FV3_AddEx(v1, &n, result);
return;
}
//
// P_PointOnLineSide
// Returns 0 or 1
//
// killough 5/3/98: reformatted, cleaned up
// ioanch 20151228: made line const
//
INT32 P_PointOnLineSide(fixed_t x, fixed_t y, const line_t *line)
{
return
!line->dx ? x <= line->v1->x ? line->dy > 0 : line->dy < 0 :
!line->dy ? y <= line->v1->y ? line->dx < 0 : line->dx > 0 :
((INT64)y - line->v1->y) * line->dx >= line->dy * ((INT64)x - line->v1->x);
}
//
// P_BoxOnLineSide
// Considers the line to be infinite
// Returns side 0 or 1, -1 if box crosses the line.
//
// killough 5/3/98: reformatted, cleaned up
//
INT32 P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld)
{
INT32 p;
switch (ld->slopetype)
{
default: // shut up compiler warnings -- killough
case ST_HORIZONTAL:
return
(tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ?
p ^ (ld->dx < 0) : -1;
case ST_VERTICAL:
return
(tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ?
p ^ (ld->dy < 0) : -1;
case ST_POSITIVE:
return
P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) ==
(p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1;
case ST_NEGATIVE:
return
(P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) ==
(p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1;
}
}
//
// P_PointOnDivlineSide
// Returns 0 or 1.
//
// killough 5/3/98: reformatted, cleaned up
//
static INT32 P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line)
{
return
line->dx == 0 ? x <= line->x ? line->dy > 0 : line->dy < 0 :
line->dy == 0 ? y <= line->y ? line->dx < 0 : line->dx > 0 :
(line->dy ^ line->dx ^ (x -= line->x) ^ (y -= line->y)) < 0 ? (line->dy ^ x) < 0 :
(INT64)(y) * line->dx >= (INT64)(line->dy) * x;
}
//
// P_MakeDivline
//
// ioanch 20151230: made const
//
void P_MakeDivline(const 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.
//
// killough 5/3/98: reformatted, cleaned up
// ioanch 20151229: added const
//
fixed_t P_InterceptVector(const divline_t *v2, const divline_t *v1)
{
// This is from PRBoom+ by Colin Phipps , GPL 2
// no precision/overflow problems
INT64 den = (INT64)(v1->dy) * v2->dx - (INT64)(v1->dx) * v2->dy;
den >>= 16;
if (!den)
{
return 0;
}
return (fixed_t)(((INT64)(v1->x - v2->x) * v1->dy - (INT64)(v1->y - v2->y) * v1->dx) / den);
}
static fixed_t dist2line(const line_t *ld, const fixed_t x, const fixed_t y)
{
return FixedHypot
(
ld->v1->x + (ld->dx / 2) - x,
ld->v1->y + (ld->dy / 2) - y
);
}
static void checknearline
( line_t * line,
fixed_t * nearest,
line_t ** near_line,
const fixed_t x,
const fixed_t y)
{
const fixed_t d = dist2line(line, x, y);
if (d < *nearest)
{
*nearest = d;
*near_line = line;
}
}
//
// P_FindNearestLine
// Returns the nearest line to a point which
// is in a sector and/or a specific type.
//
line_t * P_FindNearestLine
( const fixed_t x,
const fixed_t y,
const sector_t * sector,
const INT32 special)
{
fixed_t nearest = INT32_MAX;
line_t *near_line = NULL;
size_t i;
INT32 line = -1;
if (special == -1)
{
if (sector == NULL)
sector = R_PointInSubsector(x, y)->sector;
for (i = 0; i < sector->linecount; ++i)
{
checknearline(sector->lines[i], &nearest, &near_line, x, y);
}
}
else if (sector != NULL)
{
for (i = 0; i < sector->linecount; ++i)
{
if (sector->lines[i]->special == special)
checknearline(sector->lines[i], &nearest, &near_line, x, y);
}
}
else
{
while ((line = P_FindSpecialLineFromTag(special, -1, line)) != -1)
{
checknearline(&lines[line], &nearest, &near_line, x, y);
}
}
return near_line;
}
//
// P_LineOpening
// Sets opentop and openbottom to the window through a two sided line.
// OPTIMIZE: keep this precalculated
//
// P_CameraLineOpening
// P_LineOpening, but for camera
// Tails 09-29-2002
void P_CameraLineOpening(line_t *linedef, opening_t *open)
{
sector_t *front;
sector_t *back;
fixed_t frontfloor, frontceiling, backfloor, backceiling;
fixed_t thingtop;
open->ceiling = open->highceiling = INT32_MAX;
open->floor = open->lowfloor = INT32_MIN;
open->range = 0;
if (linedef->sidenum[1] == 0xffff)
{
// single sided line
return;
}
front = linedef->frontsector;
back = linedef->backsector;
// Cameras use the heightsec's heights rather then the actual sector heights.
// If you can see through it, why not move the camera through it too?
if (front->camsec >= 0)
{
// SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetSectorFloorZAt (&sectors[front->camsec], mapcampointer->x, mapcampointer->y);
frontceiling = P_GetSectorCeilingZAt(&sectors[front->camsec], mapcampointer->x, mapcampointer->y);
}
else if (front->heightsec >= 0)
{
// SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetSectorFloorZAt (&sectors[front->heightsec], mapcampointer->x, mapcampointer->x);
frontceiling = P_GetSectorCeilingZAt(&sectors[front->heightsec], mapcampointer->x, mapcampointer->y);
}
else
{
frontfloor = P_CameraGetFloorZ (mapcampointer, front, g_tm.x, g_tm.y, linedef);
frontceiling = P_CameraGetCeilingZ(mapcampointer, front, g_tm.x, g_tm.y, linedef);
}
if (back->camsec >= 0)
{
// SRB2CBTODO: ESLOPE (sectors[back->heightsec].f_slope)
backfloor = P_GetSectorFloorZAt (&sectors[back->camsec], mapcampointer->x, mapcampointer->y);
backceiling = P_GetSectorCeilingZAt(&sectors[back->camsec], mapcampointer->x, mapcampointer->y);
}
else if (back->heightsec >= 0)
{
// SRB2CBTODO: ESLOPE (sectors[back->heightsec].f_slope)
backfloor = P_GetSectorFloorZAt (&sectors[back->heightsec], mapcampointer->x, mapcampointer->y);
backceiling = P_GetSectorCeilingZAt(&sectors[back->heightsec], mapcampointer->x, mapcampointer->y);
}
else
{
backfloor = P_CameraGetFloorZ (mapcampointer, back, g_tm.x, g_tm.y, linedef);
backceiling = P_CameraGetCeilingZ(mapcampointer, back, g_tm.x, g_tm.y, linedef);
}
thingtop = mapcampointer->z + mapcampointer->height;
if (frontceiling < backceiling)
{
open->ceiling = frontceiling;
open->highceiling = backceiling;
}
else
{
open->ceiling = backceiling;
open->highceiling = frontceiling;
}
if (frontfloor > backfloor)
{
open->floor = frontfloor;
open->lowfloor = backfloor;
}
else
{
open->floor = backfloor;
open->lowfloor = frontfloor;
}
// Check for fake floors in the sector.
if (front->ffloors || back->ffloors)
{
ffloor_t *rover;
fixed_t delta1, delta2;
// Check for frontsector's fake floors
if (front->ffloors)
for (rover = front->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
if (!(rover->fofflags & FOF_BLOCKOTHERS) || !(rover->fofflags & FOF_RENDERALL) || !(rover->fofflags & FOF_EXISTS) || (rover->master->frontsector->flags & MSF_NOCLIPCAMERA))
continue;
topheight = P_CameraGetFOFTopZ(mapcampointer, front, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, front, rover, g_tm.x, g_tm.y, linedef);
delta1 = abs(mapcampointer->z - (bottomheight + ((topheight - bottomheight)/2)));
delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
if (bottomheight < open->ceiling && delta1 >= delta2)
open->ceiling = bottomheight;
else if (bottomheight < open->highceiling && delta1 >= delta2)
open->highceiling = bottomheight;
if (topheight > open->floor && delta1 < delta2)
open->floor = topheight;
else if (topheight > open->lowfloor && delta1 < delta2)
open->lowfloor = topheight;
}
// Check for backsectors fake floors
if (back->ffloors)
for (rover = back->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
if (!(rover->fofflags & FOF_BLOCKOTHERS) || !(rover->fofflags & FOF_RENDERALL) || !(rover->fofflags & FOF_EXISTS) || (rover->master->frontsector->flags & MSF_NOCLIPCAMERA))
continue;
topheight = P_CameraGetFOFTopZ(mapcampointer, back, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_CameraGetFOFBottomZ(mapcampointer, back, rover, g_tm.x, g_tm.y, linedef);
delta1 = abs(mapcampointer->z - (bottomheight + ((topheight - bottomheight)/2)));
delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
if (bottomheight < open->ceiling && delta1 >= delta2)
open->ceiling = bottomheight;
else if (bottomheight < open->highceiling && delta1 >= delta2)
open->highceiling = bottomheight;
if (topheight > open->floor && delta1 < delta2)
open->floor = topheight;
else if (topheight > open->lowfloor && delta1 < delta2)
open->lowfloor = topheight;
}
}
open->range = (open->ceiling - open->floor);
}
boolean
P_GetMidtextureTopBottom
( line_t * linedef,
fixed_t x,
fixed_t y,
fixed_t * return_top,
fixed_t * return_bottom)
{
side_t *side = &sides[linedef->sidenum[0]];
fixed_t textop, texbottom, texheight;
INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
sector_t *front = linedef->frontsector;
sector_t *back = linedef->backsector;
fixed_t z;
if (!texnum)
return false;
textop = P_GetSectorCeilingZAt(front, x, y);
texbottom = P_GetSectorFloorZAt(front, x, y);
if (back)
{
z = P_GetSectorCeilingZAt(back, x, y);
if (z < textop)
textop = z;
z = P_GetSectorFloorZAt(back, x, y);
if (z > texbottom)
texbottom = z;
}
// Get the midtexture's height
texheight = textures[texnum]->height << FRACBITS;
// Set texbottom and textop to the Z coordinates of the texture's boundaries
#if 0
// don't remove this code unless solid midtextures
// on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT))
{
if ((linedef->flags & ML_WRAPMIDTEX) && !side->repeatcnt) // "infinite" repeat
{
texbottom = back->floorheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset;
}
else if (linedef->flags & ML_MIDPEG)
{
texbottom = back->floorheight + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
}
else
{
textop = back->ceilingheight + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
}
}
else
#endif
{
if ((linedef->flags & ML_WRAPMIDTEX) && !side->repeatcnt) // "infinite" repeat
{
texbottom += side->rowoffset;
textop += side->rowoffset;
}
else if (linedef->flags & ML_MIDPEG)
{
texbottom += side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
}
else
{
textop += side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
}
}
if (return_top)
*return_top = textop;
if (return_bottom)
*return_bottom = texbottom;
return true;
}
static boolean P_MidtextureIsSolid(line_t *linedef, mobj_t *mobj)
{
if (linedef->polyobj)
{
// don't do anything for polyobjects! ...for now
return false;
}
if (P_IsLineTripWire(linedef) == true)
{
// Tripwire behavior.
return (mobj->player != NULL && K_TripwirePass(mobj->player) == false);
}
// Determined solely by the flag.
return ((linedef->flags & ML_MIDSOLID) == ML_MIDSOLID);
}
void P_LineOpening(line_t *linedef, mobj_t *mobj, opening_t *open)
{
enum { FRONT, BACK };
sector_t *front, *back;
fixed_t thingtop = 0;
vertex_t cross;
/* these init to shut compiler up */
fixed_t topedge[2] = {0};
fixed_t botedge[2] = {0};
int hi = 0;
int lo = 0;
// set these defaults so that polyobjects don't interfere with collision above or below them
open->ceiling = open->highceiling = INT32_MAX;
open->floor = open->lowfloor = INT32_MIN;
open->range = 0;
open->ceilingslope = open->floorslope = NULL;
open->ceilingrover = open->floorrover = NULL;
open->ceilingpic = open->floorpic = -1;
open->ceilingstep = open->floorstep = 0;
open->ceilingdrop = open->floordrop = 0;
if (linedef->sidenum[1] == 0xffff)
{
// single sided line
return;
}
P_ClosestPointOnLine(g_tm.x, g_tm.y, linedef, &cross);
// Treat polyobjects kind of like 3D Floors
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT))
{
front = linedef->frontsector;
back = linedef->frontsector;
}
else
{
front = linedef->frontsector;
back = linedef->backsector;
}
I_Assert(front != NULL);
I_Assert(back != NULL);
if (mobj)
{
thingtop = mobj->z + mobj->height;
}
if (!linedef->polyobj)
{
// Set open and high/low values here
fixed_t height[2];
const sector_t * sector[2] = { front, back };
height[FRONT] = P_GetCeilingZ(mobj, front, g_tm.x, g_tm.y, linedef);
height[BACK] = P_GetCeilingZ(mobj, back, g_tm.x, g_tm.y, linedef);
hi = ( height[0] < height[1] );
lo = ! hi;
open->ceiling = height[lo];
open->highceiling = height[hi];
open->ceilingslope = sector[lo]->c_slope;
open->ceilingpic = sector[lo]->ceilingpic;
if (mobj)
{
topedge[FRONT] = P_GetSectorCeilingZAt(front, cross.x, cross.y);
topedge[BACK] = P_GetSectorCeilingZAt(back, cross.x, cross.y);
open->ceilingstep = ( thingtop - topedge[lo] );
open->ceilingdrop = ( topedge[hi] - topedge[lo] );
}
height[FRONT] = P_GetFloorZ(mobj, front, g_tm.x, g_tm.y, linedef);
height[BACK] = P_GetFloorZ(mobj, back, g_tm.x, g_tm.y, linedef);
hi = ( height[0] < height[1] );
lo = ! hi;
open->floor = height[hi];
open->lowfloor = height[lo];
open->floorslope = sector[hi]->f_slope;
open->floorpic = sector[hi]->floorpic;
if (mobj)
{
botedge[FRONT] = P_GetSectorFloorZAt(front, cross.x, cross.y);
botedge[BACK] = P_GetSectorFloorZAt(back, cross.x, cross.y);
open->floorstep = ( botedge[hi] - mobj->z );
open->floordrop = ( botedge[hi] - botedge[lo] );
}
}
if (mobj)
{
// Check for collision with front side's midtexture if Effect 4 is set
if (P_MidtextureIsSolid(linedef, mobj) == true)
{
fixed_t textop, texbottom;
fixed_t texmid, delta1, delta2;
if (P_GetMidtextureTopBottom(linedef, cross.x, cross.y, &textop, &texbottom))
{
texmid = texbottom+(textop-texbottom)/2;
delta1 = abs(mobj->z - texmid);
delta2 = abs(thingtop - texmid);
if (delta1 > delta2)
{
// Below
if (open->ceiling > texbottom)
{
topedge[lo] -= ( open->ceiling - texbottom );
open->ceiling = texbottom;
open->ceilingstep = ( thingtop - topedge[lo] );
open->ceilingdrop = ( topedge[hi] - topedge[lo] );
}
}
else
{
// Above
if (open->floor < textop)
{
botedge[hi] += ( textop - open->floor );
open->floor = textop;
open->floorstep = ( botedge[hi] - mobj->z );
open->floordrop = ( botedge[hi] - botedge[lo] );
}
}
}
}
if (linedef->polyobj)
{
// Treat polyobj's backsector like a 3D Floor
if (linedef->polyobj->flags & POF_TESTHEIGHT)
{
const sector_t *polysec = linedef->backsector;
fixed_t polytop, polybottom, polymid;
fixed_t delta1, delta2;
if (linedef->polyobj->flags & POF_CLIPPLANES)
{
polytop = polysec->ceilingheight;
polybottom = polysec->floorheight;
}
else
{
polytop = INT32_MAX;
polybottom = INT32_MIN;
}
switch (open->fofType)
{
case LO_FOF_FLOORS:
{
if (mobj->z >= polytop)
{
if (polytop > open->floor)
{
open->floor = polytop;
}
else if (polytop > open->lowfloor)
{
open->lowfloor = polytop;
}
}
break;
}
case LO_FOF_CEILINGS:
{
if (thingtop <= polybottom)
{
if (polybottom < open->ceiling)
{
open->ceiling = polybottom;
}
else if (polybottom < open->highceiling)
{
open->highceiling = polybottom;
}
}
break;
}
default:
{
polymid = polybottom + (polytop - polybottom) / 2;
delta1 = abs(mobj->z - polymid);
delta2 = abs(thingtop - polymid);
if (delta1 > delta2)
{
if (polybottom < open->ceiling)
{
open->ceiling = polybottom;
}
else if (polybottom < open->highceiling)
{
open->highceiling = polybottom;
}
}
else
{
if (polytop > open->floor)
{
open->floor = polytop;
}
else if (polytop > open->lowfloor)
{
open->lowfloor = polytop;
}
}
break;
}
}
}
// otherwise don't do anything special, pretend there's nothing else there
}
else
{
// Check for fake floors in the sector.
if (front->ffloors || back->ffloors)
{
ffloor_t *rover;
fixed_t delta1, delta2;
/* yuck */
struct
{
fixed_t ceiling;
fixed_t floor;
ffloor_t * ceilingrover;
ffloor_t * floorrover;
} fofopen[2] = {
{ INT32_MAX, INT32_MIN, NULL, NULL },
{ INT32_MAX, INT32_MIN, NULL, NULL },
};
// Check for frontsector's fake floors
for (rover = front->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight, midheight;
if (!(rover->fofflags & FOF_EXISTS))
continue;
if (P_CheckSolidFFloorSurface(mobj, rover))
;
else if (!((rover->fofflags & FOF_BLOCKPLAYER && mobj->player)
|| (rover->fofflags & FOF_BLOCKOTHERS && !mobj->player)))
continue;
if (open->fofType != LO_FOF_ANY)
{
topheight = P_VeryTopOfFOF(rover);
bottomheight = P_VeryBottomOfFOF(rover);
}
else
{
topheight = P_GetFOFTopZ(mobj, front, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, front, rover, g_tm.x, g_tm.y, linedef);
}
switch (open->fofType)
{
case LO_FOF_FLOORS:
{
if (mobj->z >= topheight)
{
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_REVERSEPLATFORM)
{
if (topheight > fofopen[FRONT].floor)
{
fofopen[FRONT].floor = topheight;
fofopen[FRONT].floorrover = rover;
}
}
}
break;
}
case LO_FOF_CEILINGS:
{
if (thingtop <= bottomheight)
{
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_PLATFORM)
{
if (bottomheight < fofopen[FRONT].ceiling)
{
fofopen[FRONT].ceiling = bottomheight;
fofopen[FRONT].ceilingrover = rover;
}
}
}
break;
}
default:
{
midheight = bottomheight + (topheight - bottomheight) / 2;
delta1 = abs(mobj->z - midheight);
delta2 = abs(thingtop - midheight);
if (delta1 > delta2)
{
// thing is below FOF
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_PLATFORM)
{
if (bottomheight < fofopen[FRONT].ceiling)
{
fofopen[FRONT].ceiling = bottomheight;
fofopen[FRONT].ceilingrover = rover;
}
}
}
else
{
// thing is above FOF
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_REVERSEPLATFORM)
{
if (topheight > fofopen[FRONT].floor)
{
fofopen[FRONT].floor = topheight;
fofopen[FRONT].floorrover = rover;
}
}
}
break;
}
}
}
// Check for backsectors fake floors
for (rover = back->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight, midheight;
if (!(rover->fofflags & FOF_EXISTS))
continue;
if (P_CheckSolidFFloorSurface(mobj, rover))
;
else if (!((rover->fofflags & FOF_BLOCKPLAYER && mobj->player)
|| (rover->fofflags & FOF_BLOCKOTHERS && !mobj->player)))
continue;
if (open->fofType != LO_FOF_ANY)
{
topheight = P_VeryTopOfFOF(rover);
bottomheight = P_VeryBottomOfFOF(rover);
}
else
{
topheight = P_GetFOFTopZ(mobj, back, rover, g_tm.x, g_tm.y, linedef);
bottomheight = P_GetFOFBottomZ(mobj, back, rover, g_tm.x, g_tm.y, linedef);
}
switch (open->fofType)
{
case LO_FOF_FLOORS:
{
if (mobj->z >= topheight)
{
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_REVERSEPLATFORM)
{
if (topheight > fofopen[BACK].floor)
{
fofopen[BACK].floor = topheight;
fofopen[BACK].floorrover = rover;
}
}
}
break;
}
case LO_FOF_CEILINGS:
{
if (thingtop <= bottomheight)
{
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_PLATFORM)
{
if (bottomheight < fofopen[BACK].ceiling)
{
fofopen[BACK].ceiling = bottomheight;
fofopen[BACK].ceilingrover = rover;
}
}
}
break;
}
default:
{
midheight = bottomheight + (topheight - bottomheight) / 2;
delta1 = abs(mobj->z - midheight);
delta2 = abs(thingtop - midheight);
if (delta1 > delta2)
{
// thing is below FOF
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_PLATFORM)
{
if (bottomheight < fofopen[BACK].ceiling)
{
fofopen[BACK].ceiling = bottomheight;
fofopen[BACK].ceilingrover = rover;
}
}
}
else
{
// thing is above FOF
if ((rover->fofflags & FOF_INTANGIBLEFLATS) != FOF_REVERSEPLATFORM)
{
if (topheight > fofopen[BACK].floor)
{
fofopen[BACK].floor = topheight;
fofopen[BACK].floorrover = rover;
}
}
}
break;
}
}
}
hi = ( fofopen[0].ceiling < fofopen[1].ceiling );
lo = ! hi;
if (fofopen[lo].ceiling <= open->ceiling)
{
topedge[lo] = P_GetFFloorBottomZAt(fofopen[lo].ceilingrover, cross.x, cross.y);
if (fofopen[hi].ceiling < open->ceiling)
{
topedge[hi] = P_GetFFloorBottomZAt(fofopen[hi].ceilingrover, cross.x, cross.y);
}
open->ceiling = fofopen[lo].ceiling;
open->ceilingrover = fofopen[lo].ceilingrover;
open->ceilingslope = *fofopen[lo].ceilingrover->b_slope;
open->ceilingpic = *fofopen[lo].ceilingrover->bottompic;
open->ceilingstep = ( thingtop - topedge[lo] );
open->ceilingdrop = ( topedge[hi] - topedge[lo] );
if (fofopen[hi].ceiling < open->highceiling)
{
open->highceiling = fofopen[hi].ceiling;
}
}
else if (fofopen[lo].ceiling < open->highceiling)
{
open->highceiling = fofopen[lo].ceiling;
}
hi = ( fofopen[0].floor < fofopen[1].floor );
lo = ! hi;
if (fofopen[hi].floor >= open->floor)
{
botedge[hi] = P_GetFFloorTopZAt(fofopen[hi].floorrover, cross.x, cross.y);
if (fofopen[lo].floor > open->floor)
{
botedge[lo] = P_GetFFloorTopZAt(fofopen[lo].floorrover, cross.x, cross.y);
}
open->floor = fofopen[hi].floor;
open->floorrover = fofopen[hi].floorrover;
open->floorslope = *fofopen[hi].floorrover->t_slope;
open->floorpic = *fofopen[hi].floorrover->toppic;
open->floorstep = ( botedge[hi] - mobj->z );
open->floordrop = ( botedge[hi] - botedge[lo] );
if (fofopen[lo].floor > open->lowfloor)
{
open->lowfloor = fofopen[lo].floor;
}
}
else if (fofopen[hi].floor > open->lowfloor)
{
open->lowfloor = fofopen[hi].floor;
}
}
}
}
open->range = (open->ceiling - open->floor);
}
//
// THING POSITION SETTING
//
//
// P_UnsetThingPosition
// Unlinks a thing from block map and sectors.
// On each position change, BLOCKMAP and other
// lookups maintaining lists ot things inside
// these structures need to be updated.
//
void P_UnsetThingPosition(mobj_t *thing)
{
I_Assert(thing != NULL);
I_Assert(!P_MobjWasRemoved(thing));
if (!(thing->flags & MF_NOSECTOR))
{
/* invisible things don't need to be in sector list
* unlink from subsector
*
* killough 8/11/98: simpler scheme using pointers-to-pointers for prev
* pointers, allows head node pointers to be treated like everything else
*/
mobj_t **sprev = thing->sprev;
mobj_t *snext = thing->snext;
if ((*sprev = snext) != NULL) // unlink from sector list
snext->sprev = sprev;
// phares 3/14/98
//
// Save the sector list pointed to by touching_sectorlist.
// In P_SetThingPosition, we'll keep any nodes that represent
// sectors the Thing still touches. We'll add new ones then, and
// delete any nodes for sectors the Thing has vacated. Then we'll
// put it back into touching_sectorlist. It's done this way to
// avoid a lot of deleting/creating for nodes, when most of the
// time you just get back what you deleted anyway.
//
// If this Thing is being removed entirely, then the calling
// routine will clear out the nodes in sector_list.
sector_list = thing->touching_sectorlist;
thing->touching_sectorlist = NULL; //to be restored by P_SetThingPosition
}
if (!(thing->flags & MF_NOBLOCKMAP))
{
/* inert things don't need to be in blockmap
*
* killough 8/11/98: simpler scheme using pointers-to-pointers for prev
* pointers, allows head node pointers to be treated like everything else
*
* Also more robust, since it doesn't depend on current position for
* unlinking. Old method required computing head node based on position
* at time of unlinking, assuming it was the same position as during
* linking.
*/
mobj_t *bnext, **bprev = thing->bprev;
if (bprev && (*bprev = bnext = thing->bnext) != NULL) // unlink from block map
bnext->bprev = bprev;
}
}
void P_UnsetPrecipThingPosition(precipmobj_t *thing)
{
precipmobj_t **bprev = thing->bprev;
precipmobj_t *bnext = thing->bnext;
if (bprev && (*bprev = bnext) != NULL) // unlink from block map
bnext->bprev = bprev;
precipsector_list = thing->touching_sectorlist;
thing->touching_sectorlist = NULL; //to be restored by P_SetPrecipThingPosition
}
static void P_LinkToBlockMap(mobj_t *thing, mobj_t **bmap)
{
const INT32 blockx = (unsigned)(thing->x - bmaporgx) >> MAPBLOCKSHIFT;
const INT32 blocky = (unsigned)(thing->y - bmaporgy) >> MAPBLOCKSHIFT;
if (blockx >= 0 && blockx < bmapwidth
&& blocky >= 0 && blocky < bmapheight)
{
// killough 8/11/98: simpler scheme using
// pointer-to-pointer prev pointers --
// allows head nodes to be treated like everything else
mobj_t **link = &bmap[(blocky * bmapwidth) + blockx];
mobj_t *bnext = *link;
thing->bnext = bnext;
if (bnext != NULL)
bnext->bprev = &thing->bnext;
thing->bprev = link;
*link = thing;
}
else // thing is off the map
{
thing->bnext = NULL, thing->bprev = NULL;
}
}
//
// 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)
{ // link into subsector
subsector_t *ss;
sector_t *oldsec = NULL;
fixed_t tfloorz, tceilz;
I_Assert(thing != NULL);
I_Assert(!P_MobjWasRemoved(thing));
if (thing->player && thing->z <= thing->floorz && thing->subsector)
{
// I don't trust this so I'm leaving it alone. -Sal
oldsec = thing->subsector->sector;
}
ss = thing->subsector = R_PointInSubsector(thing->x, thing->y);
if (!(thing->flags & MF_NOSECTOR))
{
// invisible things don't go into the sector links
// killough 8/11/98: simpler scheme using pointer-to-pointer prev
// pointers, allows head nodes to be treated like everything else
mobj_t **link = &ss->sector->thinglist;
mobj_t *snext = *link;
if ((thing->snext = snext) != NULL)
snext->sprev = &thing->snext;
thing->sprev = link;
*link = thing;
// phares 3/16/98
//
// If sector_list isn't NULL, it has a collection of sector
// nodes that were just removed from this Thing.
// Collect the sectors the object will live in by looking at
// the existing sector_list and adding new nodes and deleting
// obsolete ones.
// When a node is deleted, its sector links (the links starting
// at sector_t->touching_thinglist) are broken. When a node is
// added, new sector links are created.
P_CreateSecNodeList(thing,thing->x,thing->y);
thing->touching_sectorlist = sector_list; // Attach to Thing's mobj_t
sector_list = NULL; // clear for next time
}
// link into blockmap
if (!(thing->flags & MF_NOBLOCKMAP))
{
// inert things don't need to be in blockmap
P_LinkToBlockMap(thing, blocklinks);
}
// Allows you to 'step' on a new linedef exec when the previous
// sector's floor is the same height.
if (thing->player && oldsec != NULL && thing->subsector && oldsec != thing->subsector->sector)
{
tfloorz = P_GetFloorZ(thing, ss->sector, thing->x, thing->y, NULL);
tceilz = P_GetCeilingZ(thing, ss->sector, thing->x, thing->y, NULL);
if (thing->eflags & MFE_VERTICALFLIP)
{
if (thing->z + thing->height >= tceilz)
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
}
else if (thing->z <= tfloorz)
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
}
}
//
// P_SetUnderlayPosition
// Links a thing into a subsector at the other end of the stack,
// so it appears behind all other sprites in that subsector.
// Sets thing->subsector properly
//
void P_SetUnderlayPosition(mobj_t *thing)
{ // link into subsector
subsector_t *ss;
mobj_t **link, *lend;
I_Assert(thing);
ss = thing->subsector = R_PointInSubsector(thing->x, thing->y);
link = &ss->sector->thinglist;
for (lend = *link; lend && lend->snext; lend = lend->snext)
;
thing->snext = NULL;
if (!lend)
{
thing->sprev = link;
*link = thing;
}
else
{
thing->sprev = &lend->snext;
lend->snext = thing;
}
P_CreateSecNodeList(thing,thing->x,thing->y);
thing->touching_sectorlist = sector_list; // Attach to Thing's mobj_t
sector_list = NULL; // clear for next time
}
void P_SetPrecipitationThingPosition(precipmobj_t *thing)
{
thing->subsector = R_PointInSubsector(thing->x, thing->y);
P_CreatePrecipSecNodeList(thing, thing->x, thing->y);
thing->touching_sectorlist = precipsector_list; // Attach to Thing's precipmobj_t
precipsector_list = NULL; // clear for next time
// NOTE: this works because bnext/bprev are at the same
// offsets in precipmobj_t and mobj_t
P_LinkToBlockMap((mobj_t*)thing, (mobj_t**)precipblocklinks);
}
//
// BLOCK MAP ITERATORS
// For each line/thing in the given mapblock,
// call the passed PIT_* 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(INT32 x, INT32 y, BlockItReturn_t (*func)(line_t *))
{
INT32 offset;
const INT32 *list; // Big blockmap
polymaplink_t *plink; // haleyjd 02/22/06
line_t *ld;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return true;
offset = y*bmapwidth + x;
// haleyjd 02/22/06: consider polyobject lines
plink = polyblocklinks[offset];
while (plink)
{
polyobj_t *po = plink->po;
if (po->validcount != validcount) // if polyobj hasn't been checked
{
size_t i;
po->validcount = validcount;
for (i = 0; i < po->numLines; ++i)
{
BlockItReturn_t ret = BMIT_CONTINUE;
if (po->lines[i]->validcount == validcount) // line has been checked
continue;
po->lines[i]->validcount = validcount;
ret = func(po->lines[i]);
if (ret == BMIT_ABORT)
{
return false;
}
else if (ret == BMIT_STOP)
{
return true;
}
}
}
plink = (polymaplink_t *)(plink->link.next);
}
offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x];
// First index is really empty, so +1 it.
for (list = blockmaplump + offset + 1; *list != -1; list++)
{
BlockItReturn_t ret = BMIT_CONTINUE;
ld = &lines[*list];
if (ld->validcount == validcount)
continue; // Line has already been checked.
ld->validcount = validcount;
ret = func(ld);
if (ret == BMIT_ABORT)
{
return false;
}
else if (ret == BMIT_STOP)
{
return true;
}
}
return true; // Everything was checked.
}
//
// P_BlockThingsIterator
//
boolean P_BlockThingsIterator(INT32 x, INT32 y, BlockItReturn_t (*func)(mobj_t *))
{
mobj_t *mobj, *bnext = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return true;
// Check interaction with the objects in the blockmap.
for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
{
BlockItReturn_t ret = BMIT_CONTINUE;
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
ret = func(mobj);
if (ret == BMIT_ABORT)
{
P_SetTarget(&bnext, NULL);
return false; // failure
}
if ((ret == BMIT_STOP)
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return true; // success
}
}
return true;
}
//
// INTERCEPT ROUTINES
//
//SoM: 4/6/2000: Limit removal
static intercept_t *intercepts = NULL;
static intercept_t *intercept_p = NULL;
divline_t g_trace;
//SoM: 4/6/2000: Remove limit on intercepts.
static void P_CheckIntercepts(void)
{
static size_t max_intercepts = 0;
size_t count = intercept_p - intercepts;
if (max_intercepts <= count)
{
if (!max_intercepts)
max_intercepts = 128;
else
max_intercepts *= 2;
intercepts = Z_Realloc(intercepts, sizeof (*intercepts) * max_intercepts, PU_STATIC, NULL);
intercept_p = intercepts + count;
}
}
//
// 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.
//
static BlockItReturn_t PIT_AddLineIntercepts(line_t *ld)
{
INT32 s1, s2;
fixed_t frac;
divline_t dl;
// avoid precision problems with two routines
if (g_trace.dx > FRACUNIT*16 || g_trace.dy > FRACUNIT*16
|| g_trace.dx < -FRACUNIT*16 || g_trace.dy < -FRACUNIT*16)
{
// Hurdler: crash here with phobia when you shoot
// on the door next the stone bridge
// stack overflow???
s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &g_trace);
s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &g_trace);
}
else
{
s1 = P_PointOnLineSide(g_trace.x, g_trace.y, ld);
s2 = P_PointOnLineSide(g_trace.x+g_trace.dx, g_trace.y+g_trace.dy, ld);
}
if (s1 == s2)
{
// Line isn't crossed.
return BMIT_CONTINUE;
}
// Hit the line.
P_MakeDivline(ld, &dl);
frac = P_InterceptVector(&g_trace, &dl);
if (frac < 0)
{
// behind source
return BMIT_CONTINUE;
}
P_CheckIntercepts();
intercept_p->frac = frac;
intercept_p->isaline = true;
intercept_p->d.line = ld;
intercept_p++;
return BMIT_CONTINUE; // continue
}
//
// PIT_AddThingIntercepts
//
static BlockItReturn_t PIT_AddThingIntercepts(mobj_t *thing)
{
size_t numfronts = 0;
divline_t line;
INT32 i;
// [RH] Don't check a corner to corner crossection for hit.
// Instead, check against the actual bounding box
// There's probably a smarter way to determine which two sides
// of the thing face the trace than by trying all four sides...
for (i = 0; i < 4; ++i)
{
switch (i)
{
case 0: // Top edge
{
line.y = thing->y + thing->radius;
if (g_trace.y < line.y)
{
continue;
}
line.x = thing->x + thing->radius;
line.dx = thing->radius * -2;
line.dy = 0;
break;
}
case 1: // Right edge
{
line.x = thing->x + thing->radius;
if (g_trace.x < line.x)
{
continue;
}
line.y = thing->y - thing->radius;
line.dx = 0;
line.dy = thing->radius * 2;
break;
}
case 2: // Bottom edge
{
line.y = thing->y - thing->radius;
if (g_trace.y > line.y)
{
continue;
}
line.x = thing->x - thing->radius;
line.dx = thing->radius * 2;
line.dy = 0;
break;
}
case 3: // Left edge
{
line.x = thing->x - thing->radius;
if (g_trace.x > line.x)
{
continue;
}
line.y = thing->y + thing->radius;
line.dx = 0;
line.dy = thing->radius * -2;
break;
}
}
// Check if this side is facing the trace origin
numfronts++;
// If it is, see if the trace crosses it
if (P_PointOnDivlineSide(line.x, line.y, &g_trace) !=
P_PointOnDivlineSide(line.x + line.dx, line.y + line.dy, &g_trace))
{
// It's a hit
fixed_t frac = P_InterceptVector(&g_trace, &line);
if (frac < 0)
{
// behind source
continue;
}
P_CheckIntercepts();
intercept_p->frac = frac;
intercept_p->isaline = false;
intercept_p->d.thing = thing;
intercept_p++;
return BMIT_CONTINUE; // Keep going.
}
}
// If none of the sides was facing the trace, then the trace
// must have started inside the box, so add it as an intercept.
if (numfronts == 0)
{
P_CheckIntercepts();
intercept_p->frac = 0;
intercept_p->isaline = false;
intercept_p->d.thing = thing;
intercept_p++;
}
return BMIT_CONTINUE; // Keep going.
}
//
// P_TraverseIntercepts
// Returns true if the traverser function returns true
// for all lines.
//
// killough 5/3/98: reformatted, cleaned up
//
static boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac)
{
size_t count;
intercept_t *in = NULL;
count = intercept_p - intercepts;
while (count--)
{
fixed_t dist = INT32_MAX;
intercept_t *scan = NULL;
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 (in)
{
if (!func(in))
{
return false; // Don't bother going farther.
}
in->frac = INT32_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 px1, fixed_t py1, fixed_t px2, fixed_t py2,
INT32 flags, traverser_t trav)
{
fixed_t xt1, yt1, xt2, yt2;
fixed_t xstep, ystep, partialx, partialy, xintercept, yintercept;
INT32 mapx, mapy, mapxstep, mapystep, count;
validcount++;
intercept_p = intercepts;
if (((px1 - bmaporgx) & (MAPBLOCKSIZE-1)) == 0)
px1 += FRACUNIT; // Don't side exactly on a line.
if (((py1 - bmaporgy) & (MAPBLOCKSIZE-1)) == 0)
py1 += FRACUNIT; // Don't side exactly on a line.
g_trace.x = px1;
g_trace.y = py1;
g_trace.dx = px2 - px1;
g_trace.dy = py2 - py1;
px1 -= bmaporgx;
py1 -= bmaporgy;
xt1 = (unsigned)px1>>MAPBLOCKSHIFT;
yt1 = (unsigned)py1>>MAPBLOCKSHIFT;
px2 -= bmaporgx;
py2 -= bmaporgy;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT;
yt2 = (unsigned)py2>>MAPBLOCKSHIFT;
if (xt2 > xt1)
{
mapxstep = 1;
partialx = FRACUNIT - ((px1>>MAPBTOFRAC) & FRACMASK);
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
}
else if (xt2 < xt1)
{
mapxstep = -1;
partialx = (px1>>MAPBTOFRAC) & FRACMASK;
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
}
else
{
mapxstep = 0;
partialx = FRACUNIT;
ystep = 256*FRACUNIT;
}
yintercept = (py1>>MAPBTOFRAC) + FixedMul(partialx, ystep);
if (yt2 > yt1)
{
mapystep = 1;
partialy = FRACUNIT - ((py1>>MAPBTOFRAC) & FRACMASK);
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else if (yt2 < yt1)
{
mapystep = -1;
partialy = (py1>>MAPBTOFRAC) & FRACMASK;
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else
{
mapystep = 0;
partialy = FRACUNIT;
xstep = 256*FRACUNIT;
}
xintercept = (px1>>MAPBTOFRAC) + FixedMul(partialy, xstep);
// [RH] Fix for traces that pass only through blockmap corners. In that case,
// xintercept and yintercept can both be set ahead of mapx and mapy, so the
// for loop would never advance anywhere.
if (abs(xstep) == FRACUNIT && abs(ystep) == FRACUNIT)
{
if (ystep < 0)
{
partialx = FRACUNIT - partialx;
}
if (xstep < 0)
{
partialy = FRACUNIT - partialy;
}
if (partialx == partialy)
{
xintercept = xt1 << FRACBITS;
yintercept = yt1 << FRACBITS;
}
}
// 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 < 100; count++)
{
if (flags & PT_ADDLINES)
{
P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts);
}
if (flags & PT_ADDTHINGS)
{
P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts);
}
// both coordinates reached the end, so end the traversing.
if ((mapxstep | mapystep) == 0)
{
break;
}
// [RH] Handle corner cases properly instead of pretending they don't exist.
switch ( (((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx) )
{
case 0: // neither xintercept nor yintercept match!
{
count = 100; // Stop traversing, because somebody screwed up.
break;
}
case 1: // xintercept matches
{
xintercept += xstep;
mapy += mapystep;
if (mapy == yt2)
{
mapystep = 0;
}
break;
}
case 2: // yintercept matches
{
yintercept += ystep;
mapx += mapxstep;
if (mapx == xt2)
{
mapxstep = 0;
}
break;
}
case 3: // xintercept and yintercept both match
{
// The trace is exiting a block through its corner. Not only does the block
// being entered need to be checked (which will happen when this loop
// continues), but the other two blocks adjacent to the corner also need to
// be checked.
if (flags & PT_ADDLINES)
{
P_BlockLinesIterator(mapx + mapxstep, mapy, PIT_AddLineIntercepts);
P_BlockLinesIterator(mapx, mapy + mapystep, PIT_AddLineIntercepts);
}
if (flags & PT_ADDTHINGS)
{
P_BlockThingsIterator(mapx + mapxstep, mapy, PIT_AddThingIntercepts);
P_BlockThingsIterator(mapx, mapy + mapystep, PIT_AddThingIntercepts);
}
xintercept += xstep;
yintercept += ystep;
mapx += mapxstep;
mapy += mapystep;
if (mapx == xt2)
{
mapxstep = 0;
}
if (mapy == yt2)
{
mapystep = 0;
}
break;
}
}
}
// Go through the sorted list
return P_TraverseIntercepts(trav, FRACUNIT);
}
// =========================================================================
// BLOCKMAP ITERATORS
// =========================================================================
// blockmap iterator for all sorts of use
// your routine must return FALSE to exit the loop earlier
// returns FALSE if the loop exited early after a false return
// value from your user function
//abandoned, maybe I'll need it someday..
/*
boolean P_RadiusLinesCheck(fixed_t radius, fixed_t x, fixed_t y,
boolean (*func)(line_t *))
{
INT32 xl, xh, yl, yh;
INT32 bx, by;
g_tm.bbox[BOXTOP] = y + radius;
g_tm.bbox[BOXBOTTOM] = y - radius;
g_tm.bbox[BOXRIGHT] = x + radius;
g_tm.bbox[BOXLEFT] = x - radius;
// check lines
xl = (unsigned)(g_tm.bbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(g_tm.bbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(g_tm.bbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(g_tm.bbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++)
if (!P_BlockLinesIterator(bx, by, func))
return false;
return true;
}
*/