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