1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 
17 
18 #include "h2def.h"
19 #include "i_system.h"
20 #include "m_bbox.h"
21 #include "p_local.h"
22 
23 static mobj_t *RoughBlockCheck(mobj_t * mo, int index);
24 
25 /*
26 ===================
27 =
28 = P_AproxDistance
29 =
30 = Gives an estimation of distance (not exact)
31 =
32 ===================
33 */
34 
P_AproxDistance(fixed_t dx,fixed_t dy)35 fixed_t P_AproxDistance(fixed_t dx, fixed_t dy)
36 {
37     dx = abs(dx);
38     dy = abs(dy);
39     if (dx < dy)
40         return dx + dy - (dx >> 1);
41     return dx + dy - (dy >> 1);
42 }
43 
44 
45 /*
46 ==================
47 =
48 = P_PointOnLineSide
49 =
50 = Returns 0 or 1
51 ==================
52 */
53 
P_PointOnLineSide(fixed_t x,fixed_t y,line_t * line)54 int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line)
55 {
56     fixed_t dx, dy;
57     fixed_t left, right;
58 
59     if (!line->dx)
60     {
61         if (x <= line->v1->x)
62             return line->dy > 0;
63         return line->dy < 0;
64     }
65     if (!line->dy)
66     {
67         if (y <= line->v1->y)
68             return line->dx < 0;
69         return line->dx > 0;
70     }
71 
72     dx = (x - line->v1->x);
73     dy = (y - line->v1->y);
74 
75     left = FixedMul(line->dy >> FRACBITS, dx);
76     right = FixedMul(dy, line->dx >> FRACBITS);
77 
78     if (right < left)
79         return 0;               // front side
80     return 1;                   // back side
81 }
82 
83 
84 /*
85 =================
86 =
87 = P_BoxOnLineSide
88 =
89 = Considers the line to be infinite
90 = Returns side 0 or 1, -1 if box crosses the line
91 =================
92 */
93 
P_BoxOnLineSide(fixed_t * tmbox,line_t * ld)94 int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld)
95 {
96     int p1 = 0, p2 = 0;
97 
98     switch (ld->slopetype)
99     {
100         case ST_HORIZONTAL:
101             p1 = tmbox[BOXTOP] > ld->v1->y;
102             p2 = tmbox[BOXBOTTOM] > ld->v1->y;
103             if (ld->dx < 0)
104             {
105                 p1 ^= 1;
106                 p2 ^= 1;
107             }
108             break;
109         case ST_VERTICAL:
110             p1 = tmbox[BOXRIGHT] < ld->v1->x;
111             p2 = tmbox[BOXLEFT] < ld->v1->x;
112             if (ld->dy < 0)
113             {
114                 p1 ^= 1;
115                 p2 ^= 1;
116             }
117             break;
118         case ST_POSITIVE:
119             p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld);
120             p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);
121             break;
122         case ST_NEGATIVE:
123             p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld);
124             p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);
125             break;
126     }
127 
128     if (p1 == p2)
129         return p1;
130     return -1;
131 }
132 
133 /*
134 ==================
135 =
136 = P_PointOnDivlineSide
137 =
138 = Returns 0 or 1
139 ==================
140 */
141 
P_PointOnDivlineSide(fixed_t x,fixed_t y,divline_t * line)142 int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line)
143 {
144     fixed_t dx, dy;
145     fixed_t left, right;
146 
147     if (!line->dx)
148     {
149         if (x <= line->x)
150             return line->dy > 0;
151         return line->dy < 0;
152     }
153     if (!line->dy)
154     {
155         if (y <= line->y)
156             return line->dx < 0;
157         return line->dx > 0;
158     }
159 
160     dx = (x - line->x);
161     dy = (y - line->y);
162 
163 // try to quickly decide by looking at sign bits
164     if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000)
165     {
166         if ((line->dy ^ dx) & 0x80000000)
167             return 1;           // (left is negative)
168         return 0;
169     }
170 
171     left = FixedMul(line->dy >> 8, dx >> 8);
172     right = FixedMul(dy >> 8, line->dx >> 8);
173 
174     if (right < left)
175         return 0;               // front side
176     return 1;                   // back side
177 }
178 
179 
180 
181 /*
182 ==============
183 =
184 = P_MakeDivline
185 =
186 ==============
187 */
188 
P_MakeDivline(line_t * li,divline_t * dl)189 void P_MakeDivline(line_t * li, divline_t * dl)
190 {
191     dl->x = li->v1->x;
192     dl->y = li->v1->y;
193     dl->dx = li->dx;
194     dl->dy = li->dy;
195 }
196 
197 
198 /*
199 ===============
200 =
201 = P_InterceptVector
202 =
203 = Returns the fractional intercept point along the first divline
204 =
205 = This is only called by the addthings and addlines traversers
206 ===============
207 */
208 
P_InterceptVector(divline_t * v2,divline_t * v1)209 fixed_t P_InterceptVector(divline_t * v2, divline_t * v1)
210 {
211 #if 1
212     fixed_t frac, num, den;
213 
214     den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy);
215     if (den == 0)
216         return 0;
217 //              I_Error ("P_InterceptVector: parallel");
218     num = FixedMul((v1->x - v2->x) >> 8, v1->dy) +
219         FixedMul((v2->y - v1->y) >> 8, v1->dx);
220     frac = FixedDiv(num, den);
221 
222     return frac;
223 #else
224     float frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy;
225 
226     v1x = (float) v1->x / FRACUNIT;
227     v1y = (float) v1->y / FRACUNIT;
228     v1dx = (float) v1->dx / FRACUNIT;
229     v1dy = (float) v1->dy / FRACUNIT;
230     v2x = (float) v2->x / FRACUNIT;
231     v2y = (float) v2->y / FRACUNIT;
232     v2dx = (float) v2->dx / FRACUNIT;
233     v2dy = (float) v2->dy / FRACUNIT;
234 
235     den = v1dy * v2dx - v1dx * v2dy;
236     if (den == 0)
237         return 0;               // parallel
238     num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx;
239     frac = num / den;
240 
241     return frac * FRACUNIT;
242 #endif
243 }
244 
245 /*
246 ==================
247 =
248 = P_LineOpening
249 =
250 = Sets opentop and openbottom to the window through a two sided line
251 = OPTIMIZE: keep this precalculated
252 ==================
253 */
254 
255 fixed_t opentop, openbottom, openrange;
256 fixed_t lowfloor;
257 
P_LineOpening(line_t * linedef)258 void P_LineOpening(line_t * linedef)
259 {
260     sector_t *front, *back;
261 
262     if (linedef->sidenum[1] == -1)
263     {                           // single sided line
264         openrange = 0;
265         return;
266     }
267 
268     front = linedef->frontsector;
269     back = linedef->backsector;
270 
271     if (front->ceilingheight < back->ceilingheight)
272         opentop = front->ceilingheight;
273     else
274         opentop = back->ceilingheight;
275     if (front->floorheight > back->floorheight)
276     {
277         openbottom = front->floorheight;
278         lowfloor = back->floorheight;
279         tmfloorpic = front->floorpic;
280     }
281     else
282     {
283         openbottom = back->floorheight;
284         lowfloor = front->floorheight;
285         tmfloorpic = back->floorpic;
286     }
287 
288     openrange = opentop - openbottom;
289 }
290 
291 /*
292 ===============================================================================
293 
294 						THING POSITION SETTING
295 
296 ===============================================================================
297 */
298 
299 /*
300 ===================
301 =
302 = P_UnsetThingPosition
303 =
304 = Unlinks a thing from block map and sectors
305 =
306 ===================
307 */
308 
P_UnsetThingPosition(mobj_t * thing)309 void P_UnsetThingPosition(mobj_t * thing)
310 {
311     int blockx, blocky;
312 
313     if (!(thing->flags & MF_NOSECTOR))
314     {                           // inert things don't need to be in blockmap
315 // unlink from subsector
316         if (thing->snext)
317             thing->snext->sprev = thing->sprev;
318         if (thing->sprev)
319             thing->sprev->snext = thing->snext;
320         else
321             thing->subsector->sector->thinglist = thing->snext;
322     }
323 
324     if (!(thing->flags & MF_NOBLOCKMAP))
325     {                           // inert things don't need to be in blockmap
326 // unlink from block map
327         if (thing->bnext)
328             thing->bnext->bprev = thing->bprev;
329         if (thing->bprev)
330             thing->bprev->bnext = thing->bnext;
331         else
332         {
333             blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
334             blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
335             if (blockx >= 0 && blockx < bmapwidth
336                 && blocky >= 0 && blocky < bmapheight)
337                 blocklinks[blocky * bmapwidth + blockx] = thing->bnext;
338         }
339     }
340 }
341 
342 
343 /*
344 ===================
345 =
346 = P_SetThingPosition
347 =
348 = Links a thing into both a block and a subsector based on it's x y
349 = Sets thing->subsector properly
350 =
351 ===================
352 */
353 
P_SetThingPosition(mobj_t * thing)354 void P_SetThingPosition(mobj_t * thing)
355 {
356     subsector_t *ss;
357     sector_t *sec;
358     int blockx, blocky;
359     mobj_t **link;
360 
361 //
362 // link into subsector
363 //
364     ss = R_PointInSubsector(thing->x, thing->y);
365     thing->subsector = ss;
366     if (!(thing->flags & MF_NOSECTOR))
367     {                           // invisible things don't go into the sector links
368         sec = ss->sector;
369 
370         thing->sprev = NULL;
371         thing->snext = sec->thinglist;
372         if (sec->thinglist)
373             sec->thinglist->sprev = thing;
374         sec->thinglist = thing;
375     }
376 
377 //
378 // link into blockmap
379 //
380     if (!(thing->flags & MF_NOBLOCKMAP))
381     {                           // inert things don't need to be in blockmap
382         blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
383         blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
384         if (blockx >= 0 && blockx < bmapwidth && blocky >= 0
385             && blocky < bmapheight)
386         {
387             link = &blocklinks[blocky * bmapwidth + blockx];
388             thing->bprev = NULL;
389             thing->bnext = *link;
390             if (*link)
391                 (*link)->bprev = thing;
392             *link = thing;
393         }
394         else
395         {                       // thing is off the map
396             thing->bnext = thing->bprev = NULL;
397         }
398     }
399 }
400 
401 
402 
403 /*
404 ===============================================================================
405 
406 						BLOCK MAP ITERATORS
407 
408 For each line/thing in the given mapblock, call the passed function.
409 If the function returns false, exit with false without checking anything else.
410 
411 ===============================================================================
412 */
413 
414 /*
415 ==================
416 =
417 = P_BlockLinesIterator
418 =
419 = The validcount flags are used to avoid checking lines
420 = that are marked in multiple mapblocks, so increment validcount before
421 = the first call to P_BlockLinesIterator, then make one or more calls to it
422 ===================
423 */
424 
P_BlockLinesIterator(int x,int y,boolean (* func)(line_t *))425 boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *))
426 {
427     int offset;
428     short *list;
429     line_t *ld;
430 
431     int i;
432     polyblock_t *polyLink;
433     seg_t **tempSeg;
434     extern polyblock_t **PolyBlockMap;
435 
436     if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
437         return true;
438     offset = y * bmapwidth + x;
439 
440     polyLink = PolyBlockMap[offset];
441     while (polyLink)
442     {
443         if (polyLink->polyobj)
444         {
445             if (polyLink->polyobj->validcount != validcount)
446             {
447                 polyLink->polyobj->validcount = validcount;
448                 tempSeg = polyLink->polyobj->segs;
449                 for (i = 0; i < polyLink->polyobj->numsegs; i++, tempSeg++)
450                 {
451                     if ((*tempSeg)->linedef->validcount == validcount)
452                     {
453                         continue;
454                     }
455                     (*tempSeg)->linedef->validcount = validcount;
456                     if (!func((*tempSeg)->linedef))
457                     {
458                         return false;
459                     }
460                 }
461             }
462         }
463         polyLink = polyLink->next;
464     }
465 
466     offset = *(blockmap + offset);
467 
468     for (list = blockmaplump + offset; *list != -1; list++)
469     {
470         ld = &lines[*list];
471         if (ld->validcount == validcount)
472             continue;           // line has already been checked
473         ld->validcount = validcount;
474 
475         if (!func(ld))
476             return false;
477     }
478 
479     return true;                // everything was checked
480 }
481 
482 
483 /*
484 ==================
485 =
486 = P_BlockThingsIterator
487 =
488 ==================
489 */
490 
P_BlockThingsIterator(int x,int y,boolean (* func)(mobj_t *))491 boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *))
492 {
493     mobj_t *mobj;
494 
495     if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
496         return true;
497 
498     for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext)
499         if (!func(mobj))
500             return false;
501 
502     return true;
503 }
504 
505 /*
506 ===============================================================================
507 
508 					INTERCEPT ROUTINES
509 
510 ===============================================================================
511 */
512 
513 intercept_t intercepts[MAXINTERCEPTS], *intercept_p;
514 
515 divline_t trace;
516 boolean earlyout;
517 int ptflags;
518 
519 /*
520 ==================
521 =
522 = PIT_AddLineIntercepts
523 =
524 = Looks for lines in the given block that intercept the given trace
525 = to add to the intercepts list
526 = A line is crossed if its endpoints are on opposite sides of the trace
527 = Returns true if earlyout and a solid line hit
528 ==================
529 */
530 
PIT_AddLineIntercepts(line_t * ld)531 boolean PIT_AddLineIntercepts(line_t * ld)
532 {
533     int s1, s2;
534     fixed_t frac;
535     divline_t dl;
536 
537 // avoid precision problems with two routines
538     if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16
539         || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16)
540     {
541         s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace);
542         s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace);
543     }
544     else
545     {
546         s1 = P_PointOnLineSide(trace.x, trace.y, ld);
547         s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld);
548     }
549     if (s1 == s2)
550         return true;            // line isn't crossed
551 
552 //
553 // hit the line
554 //
555     P_MakeDivline(ld, &dl);
556     frac = P_InterceptVector(&trace, &dl);
557     if (frac < 0)
558         return true;            // behind source
559 
560 // try to early out the check
561     if (earlyout && frac < FRACUNIT && !ld->backsector)
562         return false;           // stop checking
563 
564     intercept_p->frac = frac;
565     intercept_p->isaline = true;
566     intercept_p->d.line = ld;
567     intercept_p++;
568 
569     return true;                // continue
570 }
571 
572 
573 
574 /*
575 ==================
576 =
577 = PIT_AddThingIntercepts
578 =
579 ==================
580 */
581 
PIT_AddThingIntercepts(mobj_t * thing)582 boolean PIT_AddThingIntercepts(mobj_t * thing)
583 {
584     fixed_t x1, y1, x2, y2;
585     int s1, s2;
586     boolean tracepositive;
587     divline_t dl;
588     fixed_t frac;
589 
590     tracepositive = (trace.dx ^ trace.dy) > 0;
591 
592     // check a corner to corner crossection for hit
593 
594     if (tracepositive)
595     {
596         x1 = thing->x - thing->radius;
597         y1 = thing->y + thing->radius;
598 
599         x2 = thing->x + thing->radius;
600         y2 = thing->y - thing->radius;
601     }
602     else
603     {
604         x1 = thing->x - thing->radius;
605         y1 = thing->y - thing->radius;
606 
607         x2 = thing->x + thing->radius;
608         y2 = thing->y + thing->radius;
609     }
610     s1 = P_PointOnDivlineSide(x1, y1, &trace);
611     s2 = P_PointOnDivlineSide(x2, y2, &trace);
612     if (s1 == s2)
613         return true;            // line isn't crossed
614 
615     dl.x = x1;
616     dl.y = y1;
617     dl.dx = x2 - x1;
618     dl.dy = y2 - y1;
619     frac = P_InterceptVector(&trace, &dl);
620     if (frac < 0)
621         return true;            // behind source
622     intercept_p->frac = frac;
623     intercept_p->isaline = false;
624     intercept_p->d.thing = thing;
625     intercept_p++;
626 
627     return true;                // keep going
628 }
629 
630 
631 /*
632 ====================
633 =
634 = P_TraverseIntercepts
635 =
636 = Returns true if the traverser function returns true for all lines
637 ====================
638 */
639 
P_TraverseIntercepts(traverser_t func,fixed_t maxfrac)640 boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac)
641 {
642     int count;
643     fixed_t dist;
644     intercept_t *scan, *in;
645 
646     count = intercept_p - intercepts;
647     in = 0;                     // shut up compiler warning
648 
649     while (count--)
650     {
651         dist = INT_MAX;
652         for (scan = intercepts; scan < intercept_p; scan++)
653             if (scan->frac < dist)
654             {
655                 dist = scan->frac;
656                 in = scan;
657             }
658 
659         if (dist > maxfrac)
660             return true;        // checked everything in range
661 #if 0
662         {                       // don't check these yet, ther may be others inserted
663             in = scan = intercepts;
664             for (scan = intercepts; scan < intercept_p; scan++)
665                 if (scan->frac > maxfrac)
666                     *in++ = *scan;
667             intercept_p = in;
668             return false;
669         }
670 #endif
671 
672         if (!func(in))
673             return false;       // don't bother going farther
674         in->frac = INT_MAX;
675     }
676 
677     return true;                // everything was traversed
678 }
679 
680 
681 
682 /*
683 ==================
684 =
685 = P_PathTraverse
686 =
687 = Traces a line from x1,y1 to x2,y2, calling the traverser function for each
688 = Returns true if the traverser function returns true for all lines
689 ==================
690 */
691 
P_PathTraverse(fixed_t x1,fixed_t y1,fixed_t x2,fixed_t y2,int flags,boolean (* trav)(intercept_t *))692 boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2,
693                        int flags, boolean(*trav) (intercept_t *))
694 {
695     fixed_t xt1, yt1, xt2, yt2;
696     fixed_t xstep, ystep;
697     fixed_t partial;
698     fixed_t xintercept, yintercept;
699     int mapx, mapy, mapxstep, mapystep;
700     int count;
701 
702     earlyout = (flags & PT_EARLYOUT) != 0;
703 
704     validcount++;
705     intercept_p = intercepts;
706 
707     if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0)
708         x1 += FRACUNIT;         // don't side exactly on a line
709     if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0)
710         y1 += FRACUNIT;         // don't side exactly on a line
711     trace.x = x1;
712     trace.y = y1;
713     trace.dx = x2 - x1;
714     trace.dy = y2 - y1;
715 
716     x1 -= bmaporgx;
717     y1 -= bmaporgy;
718     xt1 = x1 >> MAPBLOCKSHIFT;
719     yt1 = y1 >> MAPBLOCKSHIFT;
720 
721     x2 -= bmaporgx;
722     y2 -= bmaporgy;
723     xt2 = x2 >> MAPBLOCKSHIFT;
724     yt2 = y2 >> MAPBLOCKSHIFT;
725 
726     if (xt2 > xt1)
727     {
728         mapxstep = 1;
729         partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1));
730         ystep = FixedDiv(y2 - y1, abs(x2 - x1));
731     }
732     else if (xt2 < xt1)
733     {
734         mapxstep = -1;
735         partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1);
736         ystep = FixedDiv(y2 - y1, abs(x2 - x1));
737     }
738     else
739     {
740         mapxstep = 0;
741         partial = FRACUNIT;
742         ystep = 256 * FRACUNIT;
743     }
744     yintercept = (y1 >> MAPBTOFRAC) + FixedMul(partial, ystep);
745 
746 
747     if (yt2 > yt1)
748     {
749         mapystep = 1;
750         partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1));
751         xstep = FixedDiv(x2 - x1, abs(y2 - y1));
752     }
753     else if (yt2 < yt1)
754     {
755         mapystep = -1;
756         partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1);
757         xstep = FixedDiv(x2 - x1, abs(y2 - y1));
758     }
759     else
760     {
761         mapystep = 0;
762         partial = FRACUNIT;
763         xstep = 256 * FRACUNIT;
764     }
765     xintercept = (x1 >> MAPBTOFRAC) + FixedMul(partial, xstep);
766 
767 
768 //
769 // step through map blocks
770 // Count is present to prevent a round off error from skipping the break
771     mapx = xt1;
772     mapy = yt1;
773 
774     for (count = 0; count < 64; count++)
775     {
776         if (flags & PT_ADDLINES)
777         {
778             if (!P_BlockLinesIterator(mapx, mapy, PIT_AddLineIntercepts))
779                 return false;   // early out
780         }
781         if (flags & PT_ADDTHINGS)
782         {
783             if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts))
784                 return false;   // early out
785         }
786 
787         if (mapx == xt2 && mapy == yt2)
788             break;
789 
790         if ((yintercept >> FRACBITS) == mapy)
791         {
792             yintercept += ystep;
793             mapx += mapxstep;
794         }
795         else if ((xintercept >> FRACBITS) == mapx)
796         {
797             xintercept += xstep;
798             mapy += mapystep;
799         }
800 
801     }
802 
803 
804 //
805 // go through the sorted list
806 //
807     return P_TraverseIntercepts(trav, FRACUNIT);
808 }
809 
810 //===========================================================================
811 //
812 // P_RoughMonsterSearch
813 //
814 // Searches though the surrounding mapblocks for monsters/players
815 //              distance is in MAPBLOCKUNITS
816 //===========================================================================
817 
P_RoughMonsterSearch(mobj_t * mo,int distance)818 mobj_t *P_RoughMonsterSearch(mobj_t * mo, int distance)
819 {
820     int blockX;
821     int blockY;
822     int startX, startY;
823     int blockIndex;
824     int firstStop;
825     int secondStop;
826     int thirdStop;
827     int finalStop;
828     int count;
829     mobj_t *target;
830 
831     startX = (mo->x - bmaporgx) >> MAPBLOCKSHIFT;
832     startY = (mo->y - bmaporgy) >> MAPBLOCKSHIFT;
833 
834     if (startX >= 0 && startX < bmapwidth && startY >= 0
835         && startY < bmapheight)
836     {
837         target = RoughBlockCheck(mo, startY * bmapwidth + startX);
838         if (target != NULL)
839         {                       // found a target right away
840             return target;
841         }
842     }
843     for (count = 1; count <= distance; count++)
844     {
845         blockX = startX - count;
846         blockY = startY - count;
847 
848         if (blockY < 0)
849         {
850             blockY = 0;
851         }
852         else if (blockY >= bmapheight)
853         {
854             blockY = bmapheight - 1;
855         }
856         if (blockX < 0)
857         {
858             blockX = 0;
859         }
860         else if (blockX >= bmapwidth)
861         {
862             blockX = bmapwidth - 1;
863         }
864         blockIndex = blockY * bmapwidth + blockX;
865         firstStop = startX + count;
866         if (firstStop < 0)
867         {
868             continue;
869         }
870         if (firstStop >= bmapwidth)
871         {
872             firstStop = bmapwidth - 1;
873         }
874         secondStop = startY + count;
875         if (secondStop < 0)
876         {
877             continue;
878         }
879         if (secondStop >= bmapheight)
880         {
881             secondStop = bmapheight - 1;
882         }
883         thirdStop = secondStop * bmapwidth + blockX;
884         secondStop = secondStop * bmapwidth + firstStop;
885         firstStop += blockY * bmapwidth;
886         finalStop = blockIndex;
887 
888         // Trace the first block section (along the top)
889         for (; blockIndex <= firstStop; blockIndex++)
890         {
891             target = RoughBlockCheck(mo, blockIndex);
892             if (target != NULL)
893             {
894                 return target;
895             }
896         }
897         // Trace the second block section (right edge)
898         for (blockIndex--; blockIndex <= secondStop; blockIndex += bmapwidth)
899         {
900             target = RoughBlockCheck(mo, blockIndex);
901             if (target != NULL)
902             {
903                 return target;
904             }
905         }
906         // Trace the third block section (bottom edge)
907         for (blockIndex -= bmapwidth; blockIndex >= thirdStop; blockIndex--)
908         {
909             target = RoughBlockCheck(mo, blockIndex);
910             if (target != NULL)
911             {
912                 return target;
913             }
914         }
915         // Trace the final block section (left edge)
916         for (blockIndex++; blockIndex > finalStop; blockIndex -= bmapwidth)
917         {
918             target = RoughBlockCheck(mo, blockIndex);
919             if (target != NULL)
920             {
921                 return target;
922             }
923         }
924     }
925     return NULL;
926 }
927 
928 //===========================================================================
929 //
930 // RoughBlockCheck
931 //
932 //===========================================================================
933 
RoughBlockCheck(mobj_t * mo,int index)934 static mobj_t *RoughBlockCheck(mobj_t * mo, int index)
935 {
936     mobj_t *link;
937     mobj_t *master;
938     angle_t angle;
939 
940     link = blocklinks[index];
941     while (link)
942     {
943         if (mo->player)         // Minotaur looking around player
944         {
945             if ((link->flags & MF_COUNTKILL) ||
946                 (link->player && (link != mo)))
947             {
948                 if (!(link->flags & MF_SHOOTABLE))
949                 {
950                     link = link->bnext;
951                     continue;
952                 }
953                 if (link->flags2 & MF2_DORMANT)
954                 {
955                     link = link->bnext;
956                     continue;
957                 }
958                 if ((link->type == MT_MINOTAUR) &&
959                     (link->special1.m == mo))
960                 {
961                     link = link->bnext;
962                     continue;
963                 }
964                 if (netgame && !deathmatch && link->player)
965                 {
966                     link = link->bnext;
967                     continue;
968                 }
969                 if (P_CheckSight(mo, link))
970                 {
971                     return link;
972                 }
973             }
974             link = link->bnext;
975         }
976         else if (mo->type == MT_MINOTAUR)       // looking around minotaur
977         {
978             master = mo->special1.m;
979             if ((link->flags & MF_COUNTKILL) ||
980                 (link->player && (link != master)))
981             {
982                 if (!(link->flags & MF_SHOOTABLE))
983                 {
984                     link = link->bnext;
985                     continue;
986                 }
987                 if (link->flags2 & MF2_DORMANT)
988                 {
989                     link = link->bnext;
990                     continue;
991                 }
992                 if ((link->type == MT_MINOTAUR) &&
993                     (link->special1.m == mo->special1.m))
994                 {
995                     link = link->bnext;
996                     continue;
997                 }
998                 if (netgame && !deathmatch && link->player)
999                 {
1000                     link = link->bnext;
1001                     continue;
1002                 }
1003                 if (P_CheckSight(mo, link))
1004                 {
1005                     return link;
1006                 }
1007             }
1008             link = link->bnext;
1009         }
1010         else if (mo->type == MT_MSTAFF_FX2)     // bloodscourge
1011         {
1012             if ((link->flags & MF_COUNTKILL ||
1013                  (link->player && link != mo->target))
1014                 && !(link->flags2 & MF2_DORMANT))
1015             {
1016                 if (!(link->flags & MF_SHOOTABLE))
1017                 {
1018                     link = link->bnext;
1019                     continue;
1020                 }
1021                 if (netgame && !deathmatch && link->player)
1022                 {
1023                     link = link->bnext;
1024                     continue;
1025                 }
1026                 else if (P_CheckSight(mo, link))
1027                 {
1028                     master = mo->target;
1029                     angle = R_PointToAngle2(master->x, master->y,
1030                                             link->x, link->y) - master->angle;
1031                     angle >>= 24;
1032                     if (angle > 226 || angle < 30)
1033                     {
1034                         return link;
1035                     }
1036                 }
1037             }
1038             link = link->bnext;
1039         }
1040         else                    // spirits
1041         {
1042             if ((link->flags & MF_COUNTKILL ||
1043                  (link->player && link != mo->target))
1044                 && !(link->flags2 & MF2_DORMANT))
1045             {
1046                 if (!(link->flags & MF_SHOOTABLE))
1047                 {
1048                     link = link->bnext;
1049                     continue;
1050                 }
1051                 if (netgame && !deathmatch && link->player)
1052                 {
1053                     link = link->bnext;
1054                     continue;
1055                 }
1056                 if (link == mo->target)
1057                 {
1058                     link = link->bnext;
1059                     continue;
1060                 }
1061                 else if (P_CheckSight(mo, link))
1062                 {
1063                     return link;
1064                 }
1065             }
1066             link = link->bnext;
1067         }
1068     }
1069     return NULL;
1070 }
1071