1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 * LineOfSight/Visibility checks, uses REJECT Lookup Table.
30 *-----------------------------------------------------------------------------*/
37 #include "rockmacros.h"
42 // Convert LOS info to struct for reentrancy and efficiency of data locality
45 fixed_t sightzstart
, t2x
, t2y
; // eye z of looker
46 divline_t strace
; // from t1 to t2
47 fixed_t topslope
, bottomslope
; // slopes to top and bottom of target
49 fixed_t maxz
,minz
; // cph - z optimisations for 2sided lines
52 static los_t los
; // cph - made static
56 // Returns side 0 (front), 1 (back), or 2 (on).
58 // killough 4/19/98: made static, cleaned up
60 inline static int P_DivlineSide(fixed_t x
, fixed_t y
, const divline_t
*node
)
64 !node
->dx
? x
== node
->x
? 2 : x
<= node
->x
? node
->dy
> 0 : node
->dy
< 0 :
65 !node
->dy
? x
== node
->y
? 2 : y
<= node
->y
? node
->dx
< 0 : node
->dx
> 0 :
66 (right
= ((y
- node
->y
) >> FRACBITS
) * (node
->dx
>> FRACBITS
)) <
67 (left
= ((x
- node
->x
) >> FRACBITS
) * (node
->dy
>> FRACBITS
)) ? 0 :
68 right
== left
? 2 : 1;
73 // Returns the fractional intercept point
74 // along the first divline.
76 // killough 4/19/98: made static, cleaned up
78 static fixed_t
P_InterceptVector2(const divline_t
*v2
, const divline_t
*v1
)
81 return (den
= FixedMul(v1
->dy
>>8, v2
->dx
) - FixedMul(v1
->dx
>>8, v2
->dy
)) ?
82 FixedDiv(FixedMul((v1
->x
- v2
->x
)>>8, v1
->dy
) +
83 FixedMul((v2
->y
- v1
->y
)>>8, v1
->dx
), den
) : 0;
89 // if strace crosses the given subsector successfully.
91 // killough 4/19/98: made static and cleaned up
93 static boolean
P_CrossSubsector(int num
)
95 seg_t
*seg
= segs
+ subsectors
[num
].firstline
;
97 fixed_t opentop
= 0, openbottom
= 0;
98 const sector_t
*front
= NULL
, *back
= NULL
;
101 if (num
>= numsubsectors
)
102 I_Error("P_CrossSubsector: ss %i with numss = %i", num
, numsubsectors
);
105 for (count
= subsectors
[num
].numlines
; --count
>= 0; seg
++) { // check lines
106 line_t
*line
= seg
->linedef
;
109 if(!line
) // figgi -- skip minisegs
112 // allready checked other side?
113 if (line
->validcount
== validcount
)
116 line
->validcount
= validcount
;
118 /* OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test
119 * cph - this is causing demo desyncs on original Doom demos.
120 * Who knows why. Exclude test for those.
122 if (!demo_compatibility
)
123 if (line
->bbox
[BOXLEFT
] > los
.bbox
[BOXRIGHT
] ||
124 line
->bbox
[BOXRIGHT
] < los
.bbox
[BOXLEFT
] ||
125 line
->bbox
[BOXBOTTOM
] > los
.bbox
[BOXTOP
] ||
126 line
->bbox
[BOXTOP
] < los
.bbox
[BOXBOTTOM
])
129 // cph - do what we can before forced to check intersection
130 if (line
->flags
& ML_TWOSIDED
) {
132 // no wall to block sight with?
133 if ((front
= seg
->frontsector
)->floorheight
==
134 (back
= seg
->backsector
)->floorheight
&&
135 front
->ceilingheight
== back
->ceilingheight
)
139 // because of ceiling height differences
140 opentop
= front
->ceilingheight
< back
->ceilingheight
?
141 front
->ceilingheight
: back
->ceilingheight
;
143 // because of floor height differences
144 openbottom
= front
->floorheight
> back
->floorheight
?
145 front
->floorheight
: back
->floorheight
;
147 // cph - reject if does not intrude in the z-space of the possible LOS
148 if ((opentop
>= los
.maxz
) && (openbottom
<= los
.minz
))
152 { // Forget this line if it doesn't cross the line of sight
153 const vertex_t
*v1
,*v2
;
158 if (P_DivlineSide(v1
->x
, v1
->y
, &los
.strace
) ==
159 P_DivlineSide(v2
->x
, v2
->y
, &los
.strace
))
162 divl
.dx
= v2
->x
- (divl
.x
= v1
->x
);
163 divl
.dy
= v2
->y
- (divl
.y
= v1
->y
);
165 // line isn't crossed?
166 if (P_DivlineSide(los
.strace
.x
, los
.strace
.y
, &divl
) ==
167 P_DivlineSide(los
.t2x
, los
.t2y
, &divl
))
171 // cph - if bottom >= top or top < minz or bottom > maxz then it must be
172 // solid wrt this LOS
173 if (!(line
->flags
& ML_TWOSIDED
) || (openbottom
>= opentop
) ||
174 (opentop
< los
.minz
) || (openbottom
> los
.maxz
))
177 { // crosses a two sided line
178 fixed_t frac
= P_InterceptVector2(&los
.strace
, &divl
);
180 if (front
->floorheight
!= back
->floorheight
)
182 fixed_t slope
= FixedDiv(openbottom
- los
.sightzstart
, frac
);
183 if (slope
> los
.bottomslope
)
184 los
.bottomslope
= slope
;
187 if (front
->ceilingheight
!= back
->ceilingheight
)
189 fixed_t slope
= FixedDiv(opentop
- los
.sightzstart
, frac
);
190 if (slope
< los
.topslope
)
191 los
.topslope
= slope
;
194 if (los
.topslope
<= los
.bottomslope
)
195 return false; // stop
198 // passed the subsector ok
205 // if strace crosses the given node successfully.
207 // killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize
208 // cph - Made to use R_PointOnSide instead of P_DivlineSide, since the latter
209 // could return 2 which was ambigous, and the former is
210 // better optimised; also removes two casts :-)
212 static boolean
P_CrossBSPNode_LxDoom(int bspnum
)
214 while (!(bspnum
& NF_SUBSECTOR
))
216 register const node_t
*bsp
= nodes
+ bspnum
;
218 side
= R_PointOnSide(los
.strace
.x
, los
.strace
.y
, bsp
);
219 side2
= R_PointOnSide(los
.t2x
, los
.t2y
, bsp
);
221 bspnum
= bsp
->children
[side
]; // doesn't touch the other side
222 else // the partition plane is crossed here
223 if (!P_CrossBSPNode_LxDoom(bsp
->children
[side
]))
224 return 0; // cross the starting side
226 bspnum
= bsp
->children
[side
^1]; // cross the ending side
228 return P_CrossSubsector(bspnum
== -1 ? 0 : bspnum
& ~NF_SUBSECTOR
);
231 static boolean
P_CrossBSPNode_PrBoom(int bspnum
)
233 while (!(bspnum
& NF_SUBSECTOR
))
235 register const node_t
*bsp
= nodes
+ bspnum
;
237 side
= P_DivlineSide(los
.strace
.x
,los
.strace
.y
,(divline_t
*)bsp
)&1;
238 side2
= P_DivlineSide(los
.t2x
, los
.t2y
, (divline_t
*) bsp
);
240 bspnum
= bsp
->children
[side
]; // doesn't touch the other side
241 else // the partition plane is crossed here
242 if (!P_CrossBSPNode_PrBoom(bsp
->children
[side
]))
243 return 0; // cross the starting side
245 bspnum
= bsp
->children
[side
^1]; // cross the ending side
247 return P_CrossSubsector(bspnum
== -1 ? 0 : bspnum
& ~NF_SUBSECTOR
);
250 /* proff - Moved the compatibility check outside the functions
251 * this gives a slight speedup
253 static boolean
P_CrossBSPNode(int bspnum
)
255 /* cph - LxDoom used some R_* funcs here */
256 if (compatibility_level
== lxdoom_1_compatibility
)
257 return P_CrossBSPNode_LxDoom(bspnum
);
259 return P_CrossBSPNode_PrBoom(bspnum
);
265 // if a straight line between t1 and t2 is unobstructed.
268 // killough 4/20/98: cleaned up, made to use new LOS struct
270 boolean
P_CheckSight(mobj_t
*t1
, mobj_t
*t2
)
272 const sector_t
*s1
= t1
->subsector
->sector
;
273 const sector_t
*s2
= t2
->subsector
->sector
;
274 int pnum
= (s1
-sectors
)*numsectors
+ (s2
-sectors
);
276 // First check for trivial rejection.
277 // Determine subsector entries in REJECT table.
279 // Check in REJECT table.
281 if (rejectmatrix
[pnum
>>3] & (1 << (pnum
&7))) // can't possibly be connected
284 // killough 4/19/98: make fake floors and ceilings block monster view
286 if ((s1
->heightsec
!= -1 &&
287 ((t1
->z
+ t1
->height
<= sectors
[s1
->heightsec
].floorheight
&&
288 t2
->z
>= sectors
[s1
->heightsec
].floorheight
) ||
289 (t1
->z
>= sectors
[s1
->heightsec
].ceilingheight
&&
290 t2
->z
+ t1
->height
<= sectors
[s1
->heightsec
].ceilingheight
)))
292 (s2
->heightsec
!= -1 &&
293 ((t2
->z
+ t2
->height
<= sectors
[s2
->heightsec
].floorheight
&&
294 t1
->z
>= sectors
[s2
->heightsec
].floorheight
) ||
295 (t2
->z
>= sectors
[s2
->heightsec
].ceilingheight
&&
296 t1
->z
+ t2
->height
<= sectors
[s2
->heightsec
].ceilingheight
))))
299 /* killough 11/98: shortcut for melee situations
300 * same subsector? obviously visible
301 * cph - compatibility optioned for demo sync, cf HR06-UV.LMP */
302 if ((t1
->subsector
== t2
->subsector
) &&
303 (compatibility_level
>= mbf_compatibility
))
306 // An unobstructed LOS is possible.
307 // Now look from eyes of t1 to any part of t2.
311 los
.topslope
= (los
.bottomslope
= t2
->z
- (los
.sightzstart
=
313 (t1
->height
>>2))) + t2
->height
;
314 los
.strace
.dx
= (los
.t2x
= t2
->x
) - (los
.strace
.x
= t1
->x
);
315 los
.strace
.dy
= (los
.t2y
= t2
->y
) - (los
.strace
.y
= t1
->y
);
318 los
.bbox
[BOXRIGHT
] = t1
->x
, los
.bbox
[BOXLEFT
] = t2
->x
;
320 los
.bbox
[BOXRIGHT
] = t2
->x
, los
.bbox
[BOXLEFT
] = t1
->x
;
323 los
.bbox
[BOXTOP
] = t1
->y
, los
.bbox
[BOXBOTTOM
] = t2
->y
;
325 los
.bbox
[BOXTOP
] = t2
->y
, los
.bbox
[BOXBOTTOM
] = t1
->y
;
327 /* cph - calculate min and max z of the potential line of sight
328 * For old demos, we disable this optimisation by setting them to
330 switch (compatibility_level
) {
331 case lxdoom_1_compatibility
:
332 if (los
.sightzstart
< t2
->z
) {
333 los
.maxz
= t2
->z
+ t2
->height
; los
.minz
= los
.sightzstart
;
334 } else if (los
.sightzstart
> t2
->z
+ t2
->height
) {
335 los
.maxz
= los
.sightzstart
; los
.minz
= t2
->z
;
337 los
.maxz
= t2
->z
+ t2
->height
; los
.minz
= t2
->z
;
341 los
.maxz
= INT_MAX
; los
.minz
= INT_MIN
;
344 // the head node is the last node output
345 return P_CrossBSPNode(numnodes
-1);