summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/p_sight.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/doom/p_sight.c')
-rw-r--r--apps/plugins/doom/p_sight.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/apps/plugins/doom/p_sight.c b/apps/plugins/doom/p_sight.c
new file mode 100644
index 0000000000..737fcf6be9
--- /dev/null
+++ b/apps/plugins/doom/p_sight.c
@@ -0,0 +1,346 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
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
11 *
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.
16 *
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.
21 *
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
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION:
28 * LineOfSight/Visibility checks, uses REJECT Lookup Table.
29 *
30 *-----------------------------------------------------------------------------*/
31
32#include "doomstat.h"
33#include "r_main.h"
34#include "p_maputl.h"
35#include "p_setup.h"
36#include "m_bbox.h"
37#include "rockmacros.h"
38//
39// P_CheckSight
40//
41// killough 4/19/98:
42// Convert LOS info to struct for reentrancy and efficiency of data locality
43
44typedef struct {
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
48 fixed_t bbox[4];
49 fixed_t maxz,minz; // cph - z optimisations for 2sided lines
50} los_t;
51
52static los_t los; // cph - made static
53
54//
55// P_DivlineSide
56// Returns side 0 (front), 1 (back), or 2 (on).
57//
58// killough 4/19/98: made static, cleaned up
59
60inline static int P_DivlineSide(fixed_t x, fixed_t y, const divline_t *node)
61{
62 fixed_t left, right;
63 return
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;
69}
70
71//
72// P_InterceptVector2
73// Returns the fractional intercept point
74// along the first divline.
75//
76// killough 4/19/98: made static, cleaned up
77
78static fixed_t P_InterceptVector2(const divline_t *v2, const divline_t *v1)
79{
80 fixed_t den;
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;
84}
85
86//
87// P_CrossSubsector
88// Returns true
89// if strace crosses the given subsector successfully.
90//
91// killough 4/19/98: made static and cleaned up
92
93static boolean P_CrossSubsector(int num)
94{
95 seg_t *seg = segs + subsectors[num].firstline;
96 int count;
97 fixed_t opentop = 0, openbottom = 0;
98 const sector_t *front = NULL, *back = NULL;
99
100#ifdef RANGECHECK
101 if (num >= numsubsectors)
102 I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors);
103#endif
104
105 for (count = subsectors[num].numlines; --count >= 0; seg++) { // check lines
106 line_t *line = seg->linedef;
107 divline_t divl;
108
109 if(!line) // figgi -- skip minisegs
110 continue;
111
112 // allready checked other side?
113 if (line->validcount == validcount)
114 continue;
115
116 line->validcount = validcount;
117
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.
121 */
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])
127 continue;
128
129 // cph - do what we can before forced to check intersection
130 if (line->flags & ML_TWOSIDED) {
131
132 // no wall to block sight with?
133 if ((front = seg->frontsector)->floorheight ==
134 (back = seg->backsector)->floorheight &&
135 front->ceilingheight == back->ceilingheight)
136 continue;
137
138 // possible occluder
139 // because of ceiling height differences
140 opentop = front->ceilingheight < back->ceilingheight ?
141 front->ceilingheight : back->ceilingheight ;
142
143 // because of floor height differences
144 openbottom = front->floorheight > back->floorheight ?
145 front->floorheight : back->floorheight ;
146
147 // cph - reject if does not intrude in the z-space of the possible LOS
148 if ((opentop >= los.maxz) && (openbottom <= los.minz))
149 continue;
150 }
151
152 { // Forget this line if it doesn't cross the line of sight
153 const vertex_t *v1,*v2;
154
155 v1 = line->v1;
156 v2 = line->v2;
157
158 if (P_DivlineSide(v1->x, v1->y, &los.strace) ==
159 P_DivlineSide(v2->x, v2->y, &los.strace))
160 continue;
161
162 divl.dx = v2->x - (divl.x = v1->x);
163 divl.dy = v2->y - (divl.y = v1->y);
164
165 // line isn't crossed?
166 if (P_DivlineSide(los.strace.x, los.strace.y, &divl) ==
167 P_DivlineSide(los.t2x, los.t2y, &divl))
168 continue;
169 }
170
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))
175 return false;
176
177 { // crosses a two sided line
178 fixed_t frac = P_InterceptVector2(&los.strace, &divl);
179
180 if (front->floorheight != back->floorheight)
181 {
182 fixed_t slope = FixedDiv(openbottom - los.sightzstart , frac);
183 if (slope > los.bottomslope)
184 los.bottomslope = slope;
185 }
186
187 if (front->ceilingheight != back->ceilingheight)
188 {
189 fixed_t slope = FixedDiv(opentop - los.sightzstart , frac);
190 if (slope < los.topslope)
191 los.topslope = slope;
192 }
193
194 if (los.topslope <= los.bottomslope)
195 return false; // stop
196 }
197 }
198 // passed the subsector ok
199 return true;
200}
201
202//
203// P_CrossBSPNode
204// Returns true
205// if strace crosses the given node successfully.
206//
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 :-)
211
212static boolean P_CrossBSPNode_LxDoom(int bspnum)
213{
214 while (!(bspnum & NF_SUBSECTOR))
215 {
216 register const node_t *bsp = nodes + bspnum;
217 int side,side2;
218 side = R_PointOnSide(los.strace.x, los.strace.y, bsp);
219 side2 = R_PointOnSide(los.t2x, los.t2y, bsp);
220 if (side == side2)
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
225 else
226 bspnum = bsp->children[side^1]; // cross the ending side
227 }
228 return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
229}
230
231static boolean P_CrossBSPNode_PrBoom(int bspnum)
232{
233 while (!(bspnum & NF_SUBSECTOR))
234 {
235 register const node_t *bsp = nodes + bspnum;
236 int side,side2;
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);
239 if (side == side2)
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
244 else
245 bspnum = bsp->children[side^1]; // cross the ending side
246 }
247 return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
248}
249
250/* proff - Moved the compatibility check outside the functions
251 * this gives a slight speedup
252 */
253static boolean P_CrossBSPNode(int bspnum)
254{
255 /* cph - LxDoom used some R_* funcs here */
256 if (compatibility_level == lxdoom_1_compatibility)
257 return P_CrossBSPNode_LxDoom(bspnum);
258 else
259 return P_CrossBSPNode_PrBoom(bspnum);
260}
261
262//
263// P_CheckSight
264// Returns true
265// if a straight line between t1 and t2 is unobstructed.
266// Uses REJECT.
267//
268// killough 4/20/98: cleaned up, made to use new LOS struct
269
270boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
271{
272 const sector_t *s1 = t1->subsector->sector;
273 const sector_t *s2 = t2->subsector->sector;
274 int pnum = (s1-sectors)*numsectors + (s2-sectors);
275
276 // First check for trivial rejection.
277 // Determine subsector entries in REJECT table.
278 //
279 // Check in REJECT table.
280
281 if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
282 return false;
283
284 // killough 4/19/98: make fake floors and ceilings block monster view
285
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)))
291 ||
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))))
297 return false;
298
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))
304 return true;
305
306 // An unobstructed LOS is possible.
307 // Now look from eyes of t1 to any part of t2.
308
309 validcount++;
310
311 los.topslope = (los.bottomslope = t2->z - (los.sightzstart =
312 t1->z + t1->height -
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);
316
317 if (t1->x > t2->x)
318 los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
319 else
320 los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
321
322 if (t1->y > t2->y)
323 los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
324 else
325 los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
326
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
329 * the extremes */
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;
336 } else {
337 los.maxz = t2->z + t2->height; los.minz = t2->z;
338 }
339 break;
340 default:
341 los.maxz = INT_MAX; los.minz = INT_MIN;
342 }
343
344 // the head node is the last node output
345 return P_CrossBSPNode(numnodes-1);
346}