gzdoom/code/P_sight.c

292 lines
8 KiB
C
Raw Normal View History

1998-04-07 00:00:00 +00:00
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// 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:$
//
// DESCRIPTION:
// LineOfSight/Visibility checks, uses REJECT Lookup Table.
//
//-----------------------------------------------------------------------------
#include "doomdef.h"
#include "i_system.h"
#include "p_local.h"
1998-04-07 00:00:00 +00:00
#include "m_random.h"
1998-12-22 00:00:00 +00:00
#include "m_bbox.h"
1998-04-07 00:00:00 +00:00
// State.
#include "r_state.h"
//
// P_CheckSight
//
1998-12-22 00:00:00 +00:00
// killough 4/19/98:
// Convert LOS info to struct for reentrancy and efficiency of data locality
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
typedef struct {
fixed_t sightzstart; // eye z of looker
fixed_t t2x, t2y;
divline_t strace; // from t1 to t2
fixed_t topslope, bottomslope; // slopes to top and bottom of target
fixed_t bbox[4];
} los_t;
1998-04-07 00:00:00 +00:00
//
// P_DivlineSide
// Returns side 0 (front), 1 (back), or 2 (on).
//
1998-12-22 00:00:00 +00:00
static int P_DivlineSide (const fixed_t x, const fixed_t y, const divline_t *node)
1998-04-07 00:00:00 +00:00
{
if (!node->dx)
{
1998-12-22 00:00:00 +00:00
return (x==node->x) ? 2 : ((x < node->x) ? node->dy > 0 : node->dy < 0);
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
else if (!node->dy)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
return (y==node->y) ? 2 : ((y < node->y) ? node->dx < 0 : node->dx > 0);
// ^^^^^^^^^^^^
1998-04-07 00:00:00 +00:00
// [RH] Original code was (x==node->y), but that's obviously not right.
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
else
{
fixed_t left = (node->dy>>FRACBITS) * ((x - node->x)>>FRACBITS);
fixed_t right = ((y - node->y)>>FRACBITS) * (node->dx>>FRACBITS);
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
return (right < left) ? 0 : ((left == right) ? 2 : 1);
}
1998-04-07 00:00:00 +00:00
}
//
// P_InterceptVector2
// Returns the fractional intercept point
// along the first divline.
// This is only called by the addthings and addlines traversers.
//
1998-12-22 00:00:00 +00:00
//
// killough 4/19/98: made static, cleaned up
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
static fixed_t P_InterceptVector2(const divline_t *v2, const divline_t *v1)
{
fixed_t den;
return (den = FixedMul(v1->dy>>8, v2->dx) - FixedMul(v1->dx>>8, v2->dy)) ?
FixedDiv(FixedMul((v1->x - v2->x)>>8, v1->dy) +
FixedMul((v2->y - v1->y)>>8, v1->dx), den) : 0;
1998-04-07 00:00:00 +00:00
}
//
// P_CrossSubsector
// Returns true
// if strace crosses the given subsector successfully.
//
1998-12-22 00:00:00 +00:00
//
// killough 4/19/98: made static and cleaned up
static BOOL P_CrossSubsector(int num, register los_t *los)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
seg_t *seg = segs + subsectors[num].firstline;
int count;
1998-04-07 00:00:00 +00:00
#ifdef RANGECHECK
1998-12-22 00:00:00 +00:00
if (num >= numsubsectors)
I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors);
1998-04-07 00:00:00 +00:00
#endif
1998-12-22 00:00:00 +00:00
for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
line_t *line = seg->linedef;
divline_t divl;
fixed_t opentop, openbottom;
const sector_t *front, *back;
const vertex_t *v1,*v2;
fixed_t frac;
// already checked other side?
1998-04-07 00:00:00 +00:00
if (line->validcount == validcount)
continue;
1998-12-22 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
line->validcount = validcount;
1998-12-22 00:00:00 +00:00
// OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test
if (line->bbox[BOXLEFT ] > los->bbox[BOXRIGHT ] ||
line->bbox[BOXRIGHT ] < los->bbox[BOXLEFT ] ||
line->bbox[BOXBOTTOM] > los->bbox[BOXTOP ] ||
line->bbox[BOXTOP] < los->bbox[BOXBOTTOM])
continue;
1998-04-07 00:00:00 +00:00
v1 = line->v1;
v2 = line->v2;
// line isn't crossed?
1998-12-22 00:00:00 +00:00
if (P_DivlineSide(v1->x, v1->y, &los->strace) ==
P_DivlineSide(v2->x, v2->y, &los->strace))
1998-04-07 00:00:00 +00:00
continue;
1998-12-22 00:00:00 +00:00
divl.dx = v2->x - (divl.x = v1->x);
divl.dy = v2->y - (divl.y = v1->y);
1998-04-07 00:00:00 +00:00
// line isn't crossed?
1998-12-22 00:00:00 +00:00
if (P_DivlineSide(los->strace.x, los->strace.y, &divl) ==
P_DivlineSide(los->t2x, los->t2y, &divl))
continue;
1998-04-07 00:00:00 +00:00
// stop because it is not two sided anyway
1998-12-22 00:00:00 +00:00
if (!(line->flags & ML_TWOSIDED) || (line->flags & ML_BLOCKEVERYTHING))
1998-04-07 00:00:00 +00:00
return false;
1998-12-22 00:00:00 +00:00
// crosses a two sided line
1998-04-07 00:00:00 +00:00
// no wall to block sight with?
1998-12-22 00:00:00 +00:00
if ((front = seg->frontsector)->floorheight ==
(back = seg->backsector)->floorheight &&
front->ceilingheight == back->ceilingheight)
continue;
1998-04-07 00:00:00 +00:00
// possible occluder
// because of ceiling height differences
1998-12-22 00:00:00 +00:00
opentop = front->ceilingheight < back->ceilingheight ?
front->ceilingheight : back->ceilingheight ;
1998-04-07 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
// because of floor height differences
1998-12-22 00:00:00 +00:00
openbottom = front->floorheight > back->floorheight ?
front->floorheight : back->floorheight ;
1998-04-07 00:00:00 +00:00
// quick test for totally closed doors
1998-12-22 00:00:00 +00:00
if (openbottom >= opentop)
1998-04-07 00:00:00 +00:00
return false; // stop
1998-12-22 00:00:00 +00:00
frac = P_InterceptVector2(&los->strace, &divl);
1998-04-07 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
if (front->floorheight != back->floorheight)
{
1998-12-22 00:00:00 +00:00
fixed_t slope = FixedDiv(openbottom - los->sightzstart , frac);
if (slope > los->bottomslope)
los->bottomslope = slope;
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
if (front->ceilingheight != back->ceilingheight)
{
1998-12-22 00:00:00 +00:00
fixed_t slope = FixedDiv(opentop - los->sightzstart , frac);
if (slope < los->topslope)
los->topslope = slope;
1998-04-07 00:00:00 +00:00
}
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
if (los->topslope <= los->bottomslope)
return false; // stop
1998-04-07 00:00:00 +00:00
}
// passed the subsector ok
1998-12-22 00:00:00 +00:00
return true;
1998-04-07 00:00:00 +00:00
}
//
// P_CrossBSPNode
// Returns true
// if strace crosses the given node successfully.
//
1998-12-22 00:00:00 +00:00
// killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
static BOOL P_CrossBSPNode(int bspnum, register los_t *los)
{
while (!(bspnum & NF_SUBSECTOR))
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
register const node_t *bsp = nodes + bspnum;
int side = P_DivlineSide(los->strace.x,los->strace.y,(divline_t *)bsp)&1;
if (side == P_DivlineSide(los->t2x, los->t2y, (divline_t *) bsp))
bspnum = bsp->children[side]; // doesn't touch the other side
else { // the partition plane is crossed here
if (!P_CrossBSPNode(bsp->children[side], los))
return 0; // cross the starting side
else
bspnum = bsp->children[side^1]; // cross the ending side
}
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR, los);
1998-04-07 00:00:00 +00:00
}
//
// P_CheckSight
// Returns true
// if a straight line between t1 and t2 is unobstructed.
// Uses REJECT.
//
1998-12-22 00:00:00 +00:00
// killough 4/20/98: cleaned up, made to use new LOS struct
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
BOOL P_CheckSight (const mobj_t *t1, const mobj_t *t2, BOOL ignoreInvisibility)
{
const sector_t *s1 = t1->subsector->sector;
const sector_t *s2 = t2->subsector->sector;
int pnum = (s1-sectors)*numsectors + (s2-sectors);
los_t los;
1998-04-07 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
// First check for trivial rejection.
// Determine subsector entries in REJECT table.
// Check in REJECT table.
1998-12-22 00:00:00 +00:00
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
return false;
// [RH] Andy Baker's stealth monsters
// Cannot see an invisible object
if (!ignoreInvisibility && (t2->flags2 & MF2_INVISIBLE)
&& P_Random (pr_checksight) > 50) //^^^^^^^^ small chance of an attack being made anyway
return false;
// killough 4/19/98: make fake floors and ceilings block monster view
if ((s1->heightsec != -1 &&
((t1->z + t1->height <= sectors[s1->heightsec].floorheight &&
t2->z >= sectors[s1->heightsec].floorheight) ||
(t1->z >= sectors[s1->heightsec].ceilingheight &&
t2->z + t1->height <= sectors[s1->heightsec].ceilingheight)))
||
(s2->heightsec != -1 &&
((t2->z + t2->height <= sectors[s2->heightsec].floorheight &&
t1->z >= sectors[s2->heightsec].floorheight) ||
(t2->z >= sectors[s2->heightsec].ceilingheight &&
t1->z + t2->height <= sectors[s2->heightsec].ceilingheight))))
return false;
1998-04-07 00:00:00 +00:00
// An unobstructed LOS is possible.
// Now look from eyes of t1 to any part of t2.
validcount++;
1998-12-22 00:00:00 +00:00
los.topslope = (los.bottomslope = t2->z - (los.sightzstart =
t1->z + t1->height -
(t1->height>>2))) + t2->height;
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
if (t1->x > t2->x)
los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
else
los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
if (t1->y > t2->y)
los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
else
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
1998-04-07 00:00:00 +00:00
// the head node is the last node output
1998-12-22 00:00:00 +00:00
return P_CrossBSPNode (numnodes-1, &los);
1998-04-07 00:00:00 +00:00
}