mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-16 15:41:16 +00:00
945 lines
17 KiB
C++
945 lines
17 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "Precompiled.h"
|
|
#include "globaldata.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include "m_bbox.h"
|
|
|
|
#include "doomdef.h"
|
|
#include "p_local.h"
|
|
|
|
|
|
// State.
|
|
#include "r_state.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;
|
|
fixed_t dy;
|
|
fixed_t left;
|
|
fixed_t 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;
|
|
int 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;
|
|
fixed_t dy;
|
|
fixed_t left;
|
|
fixed_t 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;
|
|
fixed_t num;
|
|
fixed_t 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 // UNUSED, float debug.
|
|
float frac;
|
|
float num;
|
|
float den;
|
|
float v1x;
|
|
float v1y;
|
|
float v1dx;
|
|
float v1dy;
|
|
float v2x;
|
|
float v2y;
|
|
float v2dx;
|
|
float 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 ::g->opentop and ::g->openbottom to the window
|
|
// through a two sided line.
|
|
// OPTIMIZE: keep this precalculated
|
|
//
|
|
|
|
|
|
void P_LineOpening( line_t* maputil_linedef )
|
|
{
|
|
sector_t* front;
|
|
sector_t* back;
|
|
|
|
if( maputil_linedef->sidenum[1] == -1 )
|
|
{
|
|
// single sided line
|
|
::g->openrange = 0;
|
|
return;
|
|
}
|
|
|
|
front = maputil_linedef->frontsector;
|
|
back = maputil_linedef->backsector;
|
|
|
|
if( front->ceilingheight < back->ceilingheight )
|
|
{
|
|
::g->opentop = front->ceilingheight;
|
|
}
|
|
else
|
|
{
|
|
::g->opentop = back->ceilingheight;
|
|
}
|
|
|
|
if( front->floorheight > back->floorheight )
|
|
{
|
|
::g->openbottom = front->floorheight;
|
|
::g->lowfloor = back->floorheight;
|
|
}
|
|
else
|
|
{
|
|
::g->openbottom = back->floorheight;
|
|
::g->lowfloor = front->floorheight;
|
|
}
|
|
|
|
::g->openrange = ::g->opentop - ::g->openbottom;
|
|
}
|
|
|
|
|
|
//
|
|
// THING POSITION SETTING
|
|
//
|
|
|
|
|
|
//
|
|
// P_UnsetThingPosition
|
|
// Unlinks a thing from block map and ::g->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 )
|
|
{
|
|
int blockx;
|
|
int 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 ::g->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 - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
blocky = ( thing->y - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
if( blockx >= 0 && blockx < ::g->bmapwidth
|
|
&& blocky >= 0 && blocky < ::g->bmapheight )
|
|
{
|
|
::g->blocklinks[blocky*::g->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;
|
|
int 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 ::g->blockmap
|
|
if( !( thing->flags & MF_NOBLOCKMAP ) )
|
|
{
|
|
// inert things don't need to be in ::g->blockmap
|
|
blockx = ( thing->x - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
blocky = ( thing->y - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
if( blockx >= 0
|
|
&& blockx < ::g->bmapwidth
|
|
&& blocky >= 0
|
|
&& blocky < ::g->bmapheight )
|
|
{
|
|
link = &::g->blocklinks[blocky*::g->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 PIT_* function.
|
|
// If the function returns false,
|
|
// exit with false without checking anything else.
|
|
//
|
|
|
|
|
|
//
|
|
// P_BlockLinesIterator
|
|
// The ::g->validcount flags are used to avoid checking ::g->lines
|
|
// that are marked in multiple mapblocks,
|
|
// so increment ::g->validcount before the first call
|
|
// to P_BlockLinesIterator, then make one or more calls
|
|
// to it.
|
|
//
|
|
qboolean
|
|
P_BlockLinesIterator
|
|
( int x,
|
|
int y,
|
|
qboolean( *func )( line_t* ) )
|
|
{
|
|
int offset;
|
|
short* list;
|
|
line_t* ld;
|
|
|
|
if( x < 0
|
|
|| y < 0
|
|
|| x >=::g->bmapwidth
|
|
|| y >=::g->bmapheight )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
offset = y*::g->bmapwidth + x;
|
|
|
|
offset = *( ::g->blockmap + offset );
|
|
|
|
for( list = ::g->blockmaplump + offset ; *list != -1 ; list++ )
|
|
{
|
|
ld = &::g->lines[*list];
|
|
|
|
if( ld->validcount == ::g->validcount )
|
|
{
|
|
continue; // line has already been checked
|
|
}
|
|
|
|
ld->validcount = ::g->validcount;
|
|
|
|
if( !func( ld ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true; // everything was checked
|
|
}
|
|
|
|
|
|
//
|
|
// P_BlockThingsIterator
|
|
//
|
|
qboolean
|
|
P_BlockThingsIterator
|
|
( int x,
|
|
int y,
|
|
qboolean( *func )( mobj_t* ) )
|
|
{
|
|
mobj_t* mobj;
|
|
|
|
if( x < 0
|
|
|| y < 0
|
|
|| x >=::g->bmapwidth
|
|
|| y >=::g->bmapheight )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
for( mobj = ::g->blocklinks[y*::g->bmapwidth + x] ;
|
|
mobj ;
|
|
mobj = mobj->bnext )
|
|
{
|
|
if( !func( mobj ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// INTERCEPT ROUTINES
|
|
//
|
|
|
|
|
|
//
|
|
// PIT_AddLineIntercepts.
|
|
// Looks for ::g->lines in the given block
|
|
// that intercept the given ::g->trace
|
|
// to add to the ::g->intercepts list.
|
|
//
|
|
// A line is crossed if its endpoints
|
|
// are on opposite ::g->sides of the ::g->trace.
|
|
// Returns true if ::g->earlyout and a solid line hit.
|
|
//
|
|
qboolean
|
|
PIT_AddLineIntercepts( line_t* ld )
|
|
{
|
|
int s1;
|
|
int 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 )
|
|
{
|
|
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 )
|
|
{
|
|
return true; // line isn't crossed
|
|
}
|
|
|
|
// hit the line
|
|
P_MakeDivline( ld, &dl );
|
|
frac = P_InterceptVector( &::g->trace, &dl );
|
|
|
|
if( frac < 0 )
|
|
{
|
|
return true; // behind source
|
|
}
|
|
|
|
// try to early out the check
|
|
if( ::g->earlyout
|
|
&& frac < FRACUNIT
|
|
&& !ld->backsector )
|
|
{
|
|
return false; // stop checking
|
|
}
|
|
|
|
|
|
::g->intercept_p->frac = frac;
|
|
::g->intercept_p->isaline = true;
|
|
::g->intercept_p->d.line = ld;
|
|
::g->intercept_p++;
|
|
|
|
return true; // continue
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// PIT_AddThingIntercepts
|
|
//
|
|
qboolean PIT_AddThingIntercepts( mobj_t* thing )
|
|
{
|
|
fixed_t x1;
|
|
fixed_t y1;
|
|
fixed_t x2;
|
|
fixed_t y2;
|
|
|
|
int s1;
|
|
int s2;
|
|
|
|
qboolean tracepositive;
|
|
|
|
divline_t dl;
|
|
|
|
fixed_t frac;
|
|
|
|
tracepositive = ( ::g->trace.dx ^ ::g->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, &::g->trace );
|
|
s2 = P_PointOnDivlineSide( x2, y2, &::g->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( &::g->trace, &dl );
|
|
|
|
if( frac < 0 )
|
|
{
|
|
return true; // behind source
|
|
}
|
|
|
|
::g->intercept_p->frac = frac;
|
|
::g->intercept_p->isaline = false;
|
|
::g->intercept_p->d.thing = thing;
|
|
::g->intercept_p++;
|
|
|
|
return true; // keep going
|
|
}
|
|
|
|
|
|
//
|
|
// P_TraverseIntercepts
|
|
// Returns true if the traverser function returns true
|
|
// for all ::g->lines.
|
|
//
|
|
qboolean
|
|
P_TraverseIntercepts
|
|
( traverser_t func,
|
|
fixed_t maxfrac )
|
|
{
|
|
int count;
|
|
fixed_t dist;
|
|
intercept_t* scan;
|
|
intercept_t* in;
|
|
|
|
count = ::g->intercept_p - ::g->intercepts;
|
|
|
|
in = 0; // shut up compiler warning
|
|
|
|
while( count-- )
|
|
{
|
|
dist = MAXINT;
|
|
for( scan = ::g->intercepts ; scan < ::g->intercept_p ; scan++ )
|
|
{
|
|
if( scan->frac < dist )
|
|
{
|
|
dist = scan->frac;
|
|
in = scan;
|
|
}
|
|
}
|
|
|
|
if( dist > maxfrac )
|
|
{
|
|
return true; // checked everything in range
|
|
}
|
|
|
|
#if 0 // UNUSED
|
|
{
|
|
// don't check these yet, there may be others inserted
|
|
in = scan = ::g->intercepts;
|
|
for( scan = ::g->intercepts ; scan <::g->intercept_p ; scan++ )
|
|
if( scan->frac > maxfrac )
|
|
{
|
|
*in++ = *scan;
|
|
}
|
|
::g->intercept_p = in;
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if( !func( in ) )
|
|
{
|
|
return false; // don't bother going farther
|
|
}
|
|
|
|
in->frac = MAXINT;
|
|
}
|
|
|
|
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 ::g->lines.
|
|
//
|
|
qboolean
|
|
P_PathTraverse
|
|
( fixed_t x1,
|
|
fixed_t y1,
|
|
fixed_t x2,
|
|
fixed_t y2,
|
|
int flags,
|
|
qboolean( *trav )( intercept_t* ) )
|
|
{
|
|
fixed_t xt1;
|
|
fixed_t yt1;
|
|
fixed_t xt2;
|
|
fixed_t yt2;
|
|
|
|
fixed_t xstep;
|
|
fixed_t ystep;
|
|
|
|
fixed_t partial;
|
|
|
|
fixed_t xintercept;
|
|
fixed_t yintercept;
|
|
|
|
int mapx;
|
|
int mapy;
|
|
|
|
int mapxstep;
|
|
int mapystep;
|
|
|
|
int count;
|
|
|
|
::g->earlyout = flags & PT_EARLYOUT;
|
|
|
|
::g->validcount++;
|
|
::g->intercept_p = ::g->intercepts;
|
|
|
|
if( ( ( x1 -::g->bmaporgx ) & ( MAPBLOCKSIZE - 1 ) ) == 0 )
|
|
{
|
|
x1 += FRACUNIT; // don't side exactly on a line
|
|
}
|
|
|
|
if( ( ( y1 -::g->bmaporgy ) & ( MAPBLOCKSIZE - 1 ) ) == 0 )
|
|
{
|
|
y1 += FRACUNIT; // don't side exactly on a line
|
|
}
|
|
|
|
::g->trace.x = x1;
|
|
::g->trace.y = y1;
|
|
::g->trace.dx = x2 - x1;
|
|
::g->trace.dy = y2 - y1;
|
|
|
|
x1 -= ::g->bmaporgx;
|
|
y1 -= ::g->bmaporgy;
|
|
xt1 = x1 >> MAPBLOCKSHIFT;
|
|
yt1 = y1 >> MAPBLOCKSHIFT;
|
|
|
|
x2 -= ::g->bmaporgx;
|
|
y2 -= ::g->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 );
|
|
}
|
|
|
|
|
|
|
|
|