1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 1997, 2005 - 3D Realms Entertainment
4 
5 This file is part of Shadow Warrior version 1.2
6 
7 Shadow Warrior is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 
22 Original Source: 1997 - Frank Maddin and Jim Norwood
23 Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
24 */
25 //-------------------------------------------------------------------------
26 #include "build.h"
27 
28 #include "names2.h"
29 #include "panel.h"
30 #include "game.h"
31 #include "warp.h"
32 
33 void _ErrMsg(const char *strFile, unsigned uLine, const char *format, ...);
34 void FAF_DrawRooms(int posx, int posy, int posz, fix16_t q16ang, fix16_t q16horiz, short cursectnum);
35 
36 ////////////////////////////////////////////////////////////////////
37 //
38 // FLOOR ABOVE FLOOR
39 //
40 ////////////////////////////////////////////////////////////////////
41 
42 
43 #define ZMAX 400
44 typedef struct
45 {
46     int32_t zval[ZMAX];
47     int16_t sectnum[ZMAX];
48     int16_t pic[ZMAX];
49     int16_t zcount;
50     int16_t slope[ZMAX];
51 } SAVE, *SAVEp;
52 
53 SAVE save;
54 
55 SWBOOL FAF_DebugView = 0;
56 
COVERupdatesector(int32_t x,int32_t y,int16_t * newsector)57 void COVERupdatesector(int32_t x, int32_t y, int16_t* newsector)
58 {
59     // ASSERT(*newsector>=0 && *newsector<MAXSECTORS);
60     updatesector(x,y,newsector);
61 }
62 
COVERinsertsprite(short sectnum,short stat)63 int COVERinsertsprite(short sectnum, short stat)
64 {
65     short spnum;
66     spnum = insertsprite(sectnum, stat);
67 
68     PRODUCTION_ASSERT(spnum >= 0);
69 
70     sprite[spnum].x = sprite[spnum].y = sprite[spnum].z = 0;
71     sprite[spnum].cstat = 0;
72     sprite[spnum].picnum = 0;
73     sprite[spnum].shade = 0;
74     sprite[spnum].pal = 0;
75     sprite[spnum].clipdist = 0;
76     sprite[spnum].xrepeat = sprite[spnum].yrepeat = 0;
77     sprite[spnum].xoffset = sprite[spnum].yoffset = 0;
78     sprite[spnum].ang = 0;
79     sprite[spnum].owner = -1;
80     sprite[spnum].xvel = sprite[spnum].yvel = sprite[spnum].zvel = 0;
81     sprite[spnum].lotag = 0;
82     sprite[spnum].hitag = 0;
83     sprite[spnum].extra = 0;
84 
85     return spnum;
86 }
87 
88 SWBOOL
FAF_Sector(short sectnum)89 FAF_Sector(short sectnum)
90 {
91     short SpriteNum, Next;
92     SPRITEp sp;
93 
94     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], SpriteNum, Next)
95     {
96         sp = &sprite[SpriteNum];
97 
98         if (sp->statnum == STAT_FAF &&
99             (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6))
100         {
101             return TRUE;
102         }
103     }
104 
105     return FALSE;
106 }
107 
SetWallWarpHitscan(short sectnum)108 void SetWallWarpHitscan(short sectnum)
109 {
110     short start_wall, wall_num;
111     SPRITEp sp_warp;
112 
113     if (!WarpSectorInfo(sectnum, &sp_warp))
114         return;
115 
116     if (!sp_warp)
117         return;
118 
119     // move the the next wall
120     wall_num = start_wall = sector[sectnum].wallptr;
121 
122     // Travel all the way around loop setting wall bits
123     do
124     {
125         if ((uint16_t)wall[wall_num].nextwall < MAXWALLS)
126             SET(wall[wall_num].cstat, CSTAT_WALL_WARP_HITSCAN);
127         wall_num = wall[wall_num].point2;
128     }
129     while (wall_num != start_wall);
130 }
131 
ResetWallWarpHitscan(short sectnum)132 void ResetWallWarpHitscan(short sectnum)
133 {
134     short start_wall, wall_num;
135 
136     // move the the next wall
137     wall_num = start_wall = sector[sectnum].wallptr;
138 
139     // Travel all the way around loop setting wall bits
140     do
141     {
142         RESET(wall[wall_num].cstat, CSTAT_WALL_WARP_HITSCAN);
143         wall_num = wall[wall_num].point2;
144     }
145     while (wall_num != start_wall);
146 }
147 
148 void
FAFhitscan(int32_t x,int32_t y,int32_t z,int16_t sectnum,int32_t xvect,int32_t yvect,int32_t zvect,hitdata_t * hitinfo,int32_t clipmask)149 FAFhitscan(int32_t x, int32_t y, int32_t z, int16_t sectnum,
150            int32_t xvect, int32_t yvect, int32_t zvect,
151            hitdata_t* hitinfo, int32_t clipmask)
152 {
153     vec3_t firstpos = { x, y, z };
154     int loz, hiz;
155     short newsectnum = sectnum;
156     int startclipmask = 0;
157     SWBOOL plax_found = FALSE;
158 
159     if (clipmask == CLIPMASK_MISSILE)
160         startclipmask = CLIPMASK_WARP_HITSCAN;
161 
162     hitscan(&firstpos, sectnum, xvect, yvect, zvect,
163             hitinfo, startclipmask);
164 
165     if (hitinfo->sect < 0)
166         return;
167 
168     if (hitinfo->wall >= 0)
169     {
170         // hitscan warping
171         if (TEST(wall[hitinfo->wall].cstat, CSTAT_WALL_WARP_HITSCAN))
172         {
173             short dest_sect;
174 
175             MONO_PRINT(ds);
176 
177             // back it up a bit to get a correct warp location
178             hitinfo->pos.x -= xvect>>9;
179             hitinfo->pos.y -= yvect>>9;
180 
181             // warp to new x,y,z, sectnum
182             if (Warp(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect))
183             {
184                 vec3_t pos = hitinfo->pos;
185 
186                 dest_sect = hitinfo->sect;
187 
188                 // hitscan needs to pass through dest sect
189                 ResetWallWarpHitscan(dest_sect);
190 
191                 // NOTE: This could be recursive I think if need be
192                 hitscan(&pos, hitinfo->sect, xvect, yvect, zvect,
193                         hitinfo, startclipmask);
194 
195                 // reset hitscan block for dest sect
196                 SetWallWarpHitscan(dest_sect);
197 
198                 return;
199             }
200             else
201             {
202                 //DSPRINTF(ds,"hitinfo->pos.x %d, hitinfo->pos.y %d, hitinfo->pos.z %d",hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z);
203                 MONO_PRINT(ds);
204                 ASSERT(TRUE == FALSE);
205             }
206         }
207     }
208 
209     // make sure it hit JUST a sector before doing a check
210     if (hitinfo->wall < 0 && hitinfo->sprite < 0)
211     {
212         if (TEST(sector[hitinfo->sect].extra, SECTFX_WARP_SECTOR))
213         {
214             if (TEST(wall[sector[hitinfo->sect].wallptr].cstat, CSTAT_WALL_WARP_HITSCAN))
215             {
216                 // hit the floor of a sector that is a warping sector
217                 if (Warp(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect))
218                 {
219                     vec3_t pos = hitinfo->pos;
220                     hitscan(&pos, hitinfo->sect, xvect, yvect, zvect,
221                             hitinfo, clipmask);
222 
223                     return;
224                 }
225             }
226             else
227             {
228                 if (WarpPlane(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect))
229                 {
230                     vec3_t pos = hitinfo->pos;
231                     hitscan(&pos, hitinfo->sect, xvect, yvect, zvect,
232                             hitinfo, clipmask);
233 
234                     return;
235                 }
236             }
237         }
238 
239         getzsofslope(hitinfo->sect, hitinfo->pos.x, hitinfo->pos.y, &hiz, &loz);
240         if (labs(hitinfo->pos.z - loz) < Z(4))
241         {
242             if (FAF_ConnectFloor(hitinfo->sect) && !TEST(sector[hitinfo->sect].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN))
243             {
244                 updatesectorz(hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z + Z(12), &newsectnum);
245                 plax_found = TRUE;
246             }
247         }
248         else if (labs(hitinfo->pos.z - hiz) < Z(4))
249         {
250             if (FAF_ConnectCeiling(hitinfo->sect) && !TEST(sector[hitinfo->sect].floorstat, CEILING_STAT_FAF_BLOCK_HITSCAN))
251             {
252                 updatesectorz(hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z - Z(12), &newsectnum);
253                 plax_found = TRUE;
254             }
255         }
256     }
257 
258     if (plax_found)
259     {
260         vec3_t pos = hitinfo->pos;
261         hitscan(&pos, newsectnum, xvect, yvect, zvect,
262                 hitinfo, clipmask);
263     }
264 }
265 
266 SWBOOL
FAFcansee(int32_t xs,int32_t ys,int32_t zs,int16_t sects,int32_t xe,int32_t ye,int32_t ze,int16_t secte)267 FAFcansee(int32_t xs, int32_t ys, int32_t zs, int16_t sects,
268           int32_t xe, int32_t ye, int32_t ze, int16_t secte)
269 {
270     int loz, hiz;
271     short newsectnum = sects;
272     int xvect, yvect, zvect;
273     short ang;
274     hitdata_t hitinfo;
275     int dist;
276     SWBOOL plax_found = FALSE;
277     vec3_t s = { xs, ys, zs };
278 
279     // ASSERT(sects >= 0 && secte >= 0);
280 
281     // early out to regular routine
282     if ((sects < 0 || !FAF_Sector(sects)) && (secte < 0 || !FAF_Sector(secte)))
283     {
284         return cansee(xs,ys,zs,sects,xe,ye,ze,secte);
285     }
286 
287     // get angle
288     ang = getangle(xe - xs, ye - ys);
289 
290     // get x,y,z, vectors
291     xvect = sintable[NORM_ANGLE(ang + 512)];
292     yvect = sintable[NORM_ANGLE(ang)];
293 
294     // find the distance to the target
295     dist = ksqrt(SQ(xe - xs) + SQ(ye - ys));
296 
297     if (dist != 0)
298     {
299         if (xe - xs != 0)
300             zvect = scale(xvect, ze - zs, xe - xs);
301         else if (ye - ys != 0)
302             zvect = scale(yvect, ze - zs, ye - ys);
303         else
304             zvect = 0;
305     }
306     else
307         zvect = 0;
308 
309     hitscan(&s, sects, xvect, yvect, zvect,
310             &hitinfo, CLIPMASK_MISSILE);
311 
312     if (hitinfo.sect < 0)
313         return FALSE;
314 
315     // make sure it hit JUST a sector before doing a check
316     if (hitinfo.wall < 0 && hitinfo.sprite < 0)
317     {
318         getzsofslope(hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, &hiz, &loz);
319         if (labs(hitinfo.pos.z - loz) < Z(4))
320         {
321             if (FAF_ConnectFloor(hitinfo.sect))
322             {
323                 updatesectorz(hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z + Z(12), &newsectnum);
324                 plax_found = TRUE;
325             }
326         }
327         else if (labs(hitinfo.pos.z - hiz) < Z(4))
328         {
329             if (FAF_ConnectCeiling(hitinfo.sect))
330             {
331                 updatesectorz(hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z - Z(12), &newsectnum);
332                 plax_found = TRUE;
333             }
334         }
335     }
336     else
337     {
338         return cansee(xs,ys,zs,sects,xe,ye,ze,secte);
339     }
340 
341     if (plax_found)
342         return cansee(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,newsectnum,xe,ye,ze,secte);
343 
344     return FALSE;
345 }
346 
347 
348 int
GetZadjustment(short sectnum,short hitag)349 GetZadjustment(short sectnum, short hitag)
350 {
351     short i, nexti;
352     SPRITEp sp;
353 
354     if (sectnum < 0 || !TEST(sector[sectnum].extra, SECTFX_Z_ADJUST))
355         return 0L;
356 
357     TRAVERSE_SPRITE_STAT(headspritestat[STAT_ST1], i, nexti)
358     {
359         sp = &sprite[i];
360 
361         if (sp->hitag == hitag && sp->sectnum == sectnum)
362         {
363             return Z(sp->lotag);
364         }
365     }
366 
367     return 0L;
368 }
369 
SectorZadjust(int ceilhit,int32_t * hiz,short florhit,int32_t * loz)370 SWBOOL SectorZadjust(int ceilhit, int32_t* hiz, short florhit, int32_t* loz)
371 {
372     extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust;
373     int z_amt = 0;
374 
375     SWBOOL SkipFAFcheck = FALSE;
376 
377     if ((int)florhit != -1)
378     {
379         switch (TEST(florhit, HIT_MASK))
380         {
381         case HIT_SECTOR:
382         {
383             short hit_sector = NORM_SECTOR(florhit);
384 
385             // don't jack with connect sectors
386             if (FAF_ConnectFloor(hit_sector))
387             {
388                 // rippers were dying through the floor in $rock
389                 if (TEST(sector[hit_sector].floorstat, CEILING_STAT_FAF_BLOCK_HITSCAN))
390                     break;
391 
392                 if (TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST))
393                 {
394                     // see if a z adjust ST1 is around
395                     z_amt = GetZadjustment(hit_sector, FLOOR_Z_ADJUST);
396 
397                     if (z_amt)
398                     {
399                         // explicit z adjust overrides Connect Floor
400                         *loz += z_amt;
401                         SkipFAFcheck = TRUE;
402                     }
403                 }
404 
405                 break;
406             }
407 
408             if (!TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST))
409                 break;
410 
411             // see if a z adjust ST1 is around
412             z_amt = GetZadjustment(hit_sector, FLOOR_Z_ADJUST);
413 
414             if (z_amt)
415             {
416                 // explicit z adjust overrides plax default
417                 *loz += z_amt;
418             }
419             else
420             // default adjustment for plax
421             if (TEST(sector[hit_sector].floorstat, FLOOR_STAT_PLAX))
422             {
423                 *loz += PlaxFloorGlobZadjust;
424             }
425 
426             break;
427         }
428         }
429     }
430 
431     if ((int)ceilhit != -1)
432     {
433         switch (TEST(ceilhit, HIT_MASK))
434         {
435         case HIT_SECTOR:
436         {
437             short hit_sector = NORM_SECTOR(ceilhit);
438 
439             // don't jack with connect sectors
440             if (FAF_ConnectCeiling(hit_sector))
441             {
442                 if (TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST))
443                 {
444                     // see if a z adjust ST1 is around
445                     z_amt = GetZadjustment(hit_sector, CEILING_Z_ADJUST);
446 
447                     if (z_amt)
448                     {
449                         // explicit z adjust overrides Connect Floor
450                         *loz += z_amt;
451                         SkipFAFcheck = TRUE;
452                     }
453                 }
454 
455                 break;
456             }
457 
458             if (!TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST))
459                 break;
460 
461             // see if a z adjust ST1 is around
462             z_amt = GetZadjustment(hit_sector, CEILING_Z_ADJUST);
463 
464             if (z_amt)
465             {
466                 // explicit z adjust overrides plax default
467                 *hiz -= z_amt;
468             }
469             else
470             // default adjustment for plax
471             if (TEST(sector[hit_sector].ceilingstat, CEILING_STAT_PLAX))
472             {
473                 *hiz -= PlaxCeilGlobZadjust;
474             }
475 
476             break;
477         }
478         }
479     }
480 
481     return SkipFAFcheck;
482 }
483 
WaterAdjust(short florhit,int32_t * loz)484 void WaterAdjust(short florhit, int32_t* loz)
485 {
486     switch (TEST(florhit, HIT_MASK))
487     {
488     case HIT_SECTOR:
489     {
490         SECT_USERp sectu = SectUser[NORM_SECTOR(florhit)];
491 
492         if (sectu && sectu->depth)
493             *loz += Z(sectu->depth);
494     }
495     break;
496     case HIT_SPRITE:
497         break;
498     }
499 }
500 
FAFgetzrange(int32_t x,int32_t y,int32_t z,int16_t sectnum,int32_t * hiz,int32_t * ceilhit,int32_t * loz,int32_t * florhit,int32_t clipdist,int32_t clipmask)501 void FAFgetzrange(int32_t x, int32_t y, int32_t z, int16_t sectnum,
502                   int32_t* hiz, int32_t* ceilhit,
503                   int32_t* loz, int32_t* florhit,
504                   int32_t clipdist, int32_t clipmask)
505 {
506     int foo1;
507     int foo2;
508     SWBOOL SkipFAFcheck;
509 
510     // IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
511     // This will return invalid FAF ceiling and floor heights inside of analyzesprite
512     // because the ceiling and floors get moved out of the way for drawing.
513 
514     // early out to regular routine
515     if (sectnum < 0 || !FAF_ConnectArea(sectnum))
516     {
517         getzrange_old(x, y, z, sectnum, hiz,  ceilhit, loz,  florhit, clipdist, clipmask);
518         SectorZadjust(*ceilhit, hiz, *florhit, loz);
519         WaterAdjust(*florhit, loz);
520         return;
521     }
522 
523     getzrange_old(x, y, z, sectnum, hiz,  ceilhit, loz,  florhit, clipdist, clipmask);
524     SkipFAFcheck = SectorZadjust(*ceilhit, hiz, *florhit, loz);
525     WaterAdjust(*florhit, loz);
526 
527     if (SkipFAFcheck)
528         return;
529 
530     if (FAF_ConnectCeiling(sectnum))
531     {
532         short uppersect = sectnum;
533         int newz = *hiz - Z(2);
534 
535         switch (TEST(*ceilhit, HIT_MASK))
536         {
537         case HIT_SPRITE:
538             return;
539         }
540 
541         updatesectorz(x, y, newz, &uppersect);
542         if (uppersect < 0)
543             return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz);
544         getzrange_old(x, y, newz, uppersect, hiz,  ceilhit, &foo1,  &foo2, clipdist, clipmask);
545         SectorZadjust(*ceilhit, hiz, -1, NULL);
546     }
547     else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN))
548     //if (FAF_ConnectFloor(sectnum))
549     {
550         short lowersect = sectnum;
551         int newz = *loz + Z(2);
552 
553         switch (TEST(*florhit, HIT_MASK))
554         {
555         case HIT_SECTOR:
556         {
557             break;
558         }
559         case HIT_SPRITE:
560             return;
561         }
562 
563         updatesectorz(x, y, newz, &lowersect);
564         if (lowersect < 0)
565             return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz);
566         getzrange_old(x, y, newz, lowersect, &foo1,  &foo2, loz,  florhit, clipdist, clipmask);
567         SectorZadjust(-1, NULL, *florhit, loz);
568         WaterAdjust(*florhit, loz);
569     }
570 }
571 
FAFgetzrangepoint(int32_t x,int32_t y,int32_t z,int16_t sectnum,int32_t * hiz,int32_t * ceilhit,int32_t * loz,int32_t * florhit)572 void FAFgetzrangepoint(int32_t x, int32_t y, int32_t z, int16_t sectnum,
573                        int32_t* hiz, int32_t* ceilhit,
574                        int32_t* loz, int32_t* florhit)
575 {
576     int foo1;
577     int foo2;
578     SWBOOL SkipFAFcheck;
579 
580     // IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
581     // This will return invalid FAF ceiling and floor heights inside of analyzesprite
582     // because the ceiling and floors get moved out of the way for drawing.
583 
584     // early out to regular routine
585     if (!FAF_ConnectArea(sectnum))
586     {
587         getzrangepoint(x, y, z, sectnum, hiz,  ceilhit, loz,  florhit);
588         SectorZadjust(*ceilhit, hiz, *florhit, loz);
589         WaterAdjust(*florhit, loz);
590         return;
591     }
592 
593     getzrangepoint(x, y, z, sectnum, hiz,  ceilhit, loz,  florhit);
594     SkipFAFcheck = SectorZadjust(*ceilhit, hiz, *florhit, loz);
595     WaterAdjust(*florhit, loz);
596 
597     if (SkipFAFcheck)
598         return;
599 
600     if (FAF_ConnectCeiling(sectnum))
601     {
602         short uppersect = sectnum;
603         int newz = *hiz - Z(2);
604         switch (TEST(*ceilhit, HIT_MASK))
605         {
606         case HIT_SPRITE:
607             return;
608         }
609         updatesectorz(x, y, newz, &uppersect);
610         if (uppersect < 0)
611             return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d, sectnum %d", x, y, newz, sectnum);
612         getzrangepoint(x, y, newz, uppersect, hiz,  ceilhit, &foo1,  &foo2);
613         SectorZadjust(*ceilhit, hiz, -1, NULL);
614     }
615     else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN))
616     //if (FAF_ConnectFloor(sectnum))
617     {
618         short lowersect = sectnum;
619         int newz = *loz + Z(2);
620         switch (TEST(*florhit, HIT_MASK))
621         {
622         case HIT_SPRITE:
623             return;
624         }
625         updatesectorz(x, y, newz, &lowersect);
626         if (lowersect < 0)
627             return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d, sectnum %d", x, y, newz, sectnum);
628         getzrangepoint(x, y, newz, lowersect, &foo1,  &foo2, loz,  florhit);
629         SectorZadjust(-1, NULL, *florhit, loz);
630         WaterAdjust(*florhit, loz);
631     }
632 }
633 
634 #if 0
635 SWBOOL
636 FAF_ConnectCeiling(short sectnum)
637 {
638     return sector[sectnum].ceilingpicnum == FAF_MIRROR_PIC;
639 }
640 
641 SWBOOL
642 FAF_ConnectFloor(short sectnum)
643 {
644     return sector[sectnum].floorpicnum == FAF_MIRROR_PIC;
645 }
646 #endif
647 
648 
649 // doesn't work for blank pics
650 SWBOOL
PicInView(short tile_num,SWBOOL reset)651 PicInView(short tile_num, SWBOOL reset)
652 {
653     if (TEST(gotpic[tile_num >> 3], 1 << (tile_num & 7)))
654     {
655         if (reset)
656             RESET(gotpic[tile_num >> 3], 1 << (tile_num & 7));
657 
658         return TRUE;
659     }
660 
661     return FALSE;
662 }
663 
664 void
SetupMirrorTiles(void)665 SetupMirrorTiles(void)
666 {
667     short i, nexti;
668     SPRITEp sp;
669 
670     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
671     {
672         sp = &sprite[i];
673 
674         if (sector[sp->sectnum].ceilingpicnum == FAF_PLACE_MIRROR_PIC)
675         {
676             sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC;
677             SET(sector[sp->sectnum].ceilingstat, CEILING_STAT_PLAX);
678         }
679 
680         if (sector[sp->sectnum].floorpicnum == FAF_PLACE_MIRROR_PIC)
681         {
682             sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC;
683             SET(sector[sp->sectnum].floorstat, FLOOR_STAT_PLAX);
684         }
685 
686         if (sector[sp->sectnum].ceilingpicnum == FAF_PLACE_MIRROR_PIC+1)
687             sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC+1;
688 
689         if (sector[sp->sectnum].floorpicnum == FAF_PLACE_MIRROR_PIC+1)
690             sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC+1;
691     }
692 }
693 
694 
695 //This function is like updatesector, but it takes a z-coordinate in addition
696 //   to help it get the right sector when there's overlapping.  (I may be
697 //   adding this function to the engine or making the standard updatesector
698 //   use z's.  Until then, use this.  )
699 
700 #if 0
701 void
702 updatesectorz(int x, int y, int z, short *sectnum)
703 {
704     walltype *wal;
705     int i, j, cz, fz;
706 
707     ASSERT(*sectnum >=0 && *sectnum <= MAXSECTORS);
708 
709     getzsofslope(*sectnum, x, y, &cz, &fz);
710     // go ahead and check the current sector
711     if ((z >= cz) && (z <= fz))
712         if (inside(x, y, *sectnum) != 0)
713             return;
714 
715     // Test the sectors immediately around your current sector
716     if ((*sectnum >= 0) && (*sectnum < numsectors))
717     {
718         wal = &wall[sector[*sectnum].wallptr];
719         j = sector[*sectnum].wallnum;
720         do
721         {
722             i = wal->nextsector;
723             if (i >= 0)
724             {
725                 getzsofslope(i, x, y, &cz, &fz);
726                 if ((z >= cz) && (z <= fz))
727                 {
728                     if (inside(x, y, (short) i) == 1)
729                     {
730                         *sectnum = i;
731                         return;
732                     }
733                 }
734             }
735             wal++;
736             j--;
737         }
738         while (j != 0);
739     }
740 
741     // didn't find it yet so test ALL sectors
742     for (i = numsectors - 1; i >= 0; i--)
743     {
744         getzsofslope(i, x, y, &cz, &fz);
745         if ((z >= cz) && (z <= fz))
746         {
747             if (inside(x, y, (short) i) == 1)
748             {
749                 *sectnum = i;
750                 return;
751             }
752         }
753     }
754 
755     *sectnum = -1;
756 }
757 #endif
758 
759 short GlobStackSect[2];
760 
761 void
GetUpperLowerSector(short match,int x,int y,short * upper,short * lower)762 GetUpperLowerSector(short match, int x, int y, short *upper, short *lower)
763 {
764     int i;
765     short sectorlist[16];
766     int sln = 0;
767     short SpriteNum, Next;
768     SPRITEp sp;
769 
770     // keep a list of the last stacked sectors the view was in and
771     // check those fisrt
772     sln = 0;
773     for (i = 0; i < (int)SIZ(GlobStackSect); i++)
774     {
775         // will not hurt if GlobStackSect is invalid - inside checks for this
776         if (inside(x, y, GlobStackSect[i]) == 1)
777         {
778             SWBOOL found = FALSE;
779 
780             TRAVERSE_SPRITE_SECT(headspritesect[GlobStackSect[i]], SpriteNum, Next)
781             {
782                 sp = &sprite[SpriteNum];
783 
784                 if (sp->statnum == STAT_FAF &&
785                     (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6)
786                     && sp->lotag == match)
787                 {
788                     found = TRUE;
789                 }
790             }
791 
792             if (!found)
793                 continue;
794 
795             sectorlist[sln] = GlobStackSect[i];
796             sln++;
797         }
798     }
799 
800     // didn't find it yet so test ALL sectors
801     if (sln < 2)
802     {
803         sln = 0;
804         for (i = numsectors - 1; i >= 0; i--)
805         {
806             if (inside(x, y, (short) i) == 1)
807             {
808                 SWBOOL found = FALSE;
809 
810                 TRAVERSE_SPRITE_SECT(headspritesect[i], SpriteNum, Next)
811                 {
812                     sp = &sprite[SpriteNum];
813 
814                     if (sp->statnum == STAT_FAF &&
815                         (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6)
816                         && sp->lotag == match)
817                     {
818                         found = TRUE;
819                     }
820                 }
821 
822                 if (!found)
823                     continue;
824 
825                 if (sln < (int)SIZ(GlobStackSect))
826                     GlobStackSect[sln] = i;
827                 if (sln < (int)SIZ(sectorlist))
828                     sectorlist[sln] = i;
829                 sln++;
830             }
831         }
832     }
833 
834     // might not find ANYTHING if not tagged right
835     if (sln == 0)
836     {
837         *upper = -1;
838         *lower = -1;
839         return;
840     }
841     // Map rooms have NOT been dragged on top of each other
842     else if (sln == 1)
843     {
844         *lower = sectorlist[0];
845         *upper = sectorlist[0];
846         return;
847     }
848     // Map rooms HAVE been dragged on top of each other
849     // inside will somtimes find that you are in two different sectors if the x,y
850     // is exactly on a sector line.
851     else if (sln > 2)
852     {
853         //DSPRINTF(ds, "TOO MANY SECTORS FOUND: x=%d, y=%d, match=%d, num sectors %d, %d, %d, %d, %d, %d", x, y, match, sln, sectorlist[0], sectorlist[1], sectorlist[2], sectorlist[3], sectorlist[4]);
854         MONO_PRINT(ds);
855         // try again moving the x,y pos around until you only get two sectors
856         GetUpperLowerSector(match, x - 1, y, upper, lower);
857     }
858 
859     if (sln == 2)
860     {
861         if (sector[sectorlist[0]].floorz < sector[sectorlist[1]].floorz)
862         {
863             // swap
864             // make sectorlist[0] the LOW sector
865             short hold;
866 
867             hold = sectorlist[0];
868             sectorlist[0] = sectorlist[1];
869             sectorlist[1] = hold;
870         }
871 
872         *lower = sectorlist[0];
873         *upper = sectorlist[1];
874     }
875 }
876 
877 SWBOOL
FindCeilingView(short match,int32_t * x,int32_t * y,int32_t z,int16_t * sectnum)878 FindCeilingView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum)
879 {
880     int xoff = 0;
881     int yoff = 0;
882     short i, nexti;
883     SPRITEp sp = NULL;
884     int pix_diff;
885     int newz;
886 
887     save.zcount = 0;
888 
889     // Search Stat List For closest ceiling view sprite
890     // Get the match, xoff, yoff from this point
891     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
892     {
893         sp = &sprite[i];
894 
895         if (sp->hitag == VIEW_THRU_CEILING && sp->lotag == match)
896         {
897             xoff = *x - sp->x;
898             yoff = *y - sp->y;
899             break;
900         }
901     }
902 
903     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
904     {
905         sp = &sprite[i];
906 
907         if (sp->lotag == match)
908         {
909             // determine x,y position
910             if (sp->hitag == VIEW_THRU_FLOOR)
911             {
912                 short upper, lower;
913 
914                 *x = sp->x + xoff;
915                 *y = sp->y + yoff;
916 
917                 // get new sector
918                 GetUpperLowerSector(match, *x, *y, &upper, &lower);
919                 *sectnum = upper;
920                 break;
921             }
922         }
923     }
924 
925     if (*sectnum < 0)
926         return FALSE;
927 
928     ASSERT(sp);
929     ASSERT(sp->hitag == VIEW_THRU_FLOOR);
930 
931     pix_diff = labs(z - sector[sp->sectnum].floorz) >> 8;
932     newz = sector[sp->sectnum].floorz + ((pix_diff / 128) + 1) * Z(128);
933 
934     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
935     {
936         sp = &sprite[i];
937 
938         if (sp->lotag == match)
939         {
940             // move lower levels ceilings up for the correct view
941             if (sp->hitag == VIEW_LEVEL2)
942             {
943                 // save it off
944                 save.sectnum[save.zcount] = sp->sectnum;
945                 save.zval[save.zcount] = sector[sp->sectnum].floorz;
946                 save.pic[save.zcount] = sector[sp->sectnum].floorpicnum;
947                 save.slope[save.zcount] = sector[sp->sectnum].floorheinum;
948 
949                 sector[sp->sectnum].floorz = newz;
950                 // don't change FAF_MIRROR_PIC - ConnectArea
951                 if (sector[sp->sectnum].floorpicnum != FAF_MIRROR_PIC)
952                     sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC+1;
953                 sector[sp->sectnum].floorheinum = 0;
954 
955                 save.zcount++;
956                 PRODUCTION_ASSERT(save.zcount < ZMAX);
957             }
958         }
959     }
960 
961     return TRUE;
962 }
963 
964 SWBOOL
FindFloorView(short match,int32_t * x,int32_t * y,int32_t z,int16_t * sectnum)965 FindFloorView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum)
966 {
967     int xoff = 0;
968     int yoff = 0;
969     short i, nexti;
970     SPRITEp sp = NULL;
971     int newz;
972     int pix_diff;
973 
974     save.zcount = 0;
975 
976     // Search Stat List For closest ceiling view sprite
977     // Get the match, xoff, yoff from this point
978     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
979     {
980         sp = &sprite[i];
981 
982         if (sp->hitag == VIEW_THRU_FLOOR && sp->lotag == match)
983         {
984             xoff = *x - sp->x;
985             yoff = *y - sp->y;
986             break;
987         }
988     }
989 
990 
991     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
992     {
993         sp = &sprite[i];
994 
995         if (sp->lotag == match)
996         {
997             // determine x,y position
998             if (sp->hitag == VIEW_THRU_CEILING)
999             {
1000                 short upper, lower;
1001 
1002                 *x = sp->x + xoff;
1003                 *y = sp->y + yoff;
1004 
1005                 // get new sector
1006                 GetUpperLowerSector(match, *x, *y, &upper, &lower);
1007                 *sectnum = lower;
1008                 break;
1009             }
1010         }
1011     }
1012 
1013     if (*sectnum < 0)
1014         return FALSE;
1015 
1016     ASSERT(sp);
1017     ASSERT(sp->hitag == VIEW_THRU_CEILING);
1018 
1019     // move ceiling multiple of 128 so that the wall tile will line up
1020     pix_diff = labs(z - sector[sp->sectnum].ceilingz) >> 8;
1021     newz = sector[sp->sectnum].ceilingz - ((pix_diff / 128) + 1) * Z(128);
1022 
1023     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
1024     {
1025         sp = &sprite[i];
1026 
1027         if (sp->lotag == match)
1028         {
1029             // move upper levels floors down for the correct view
1030             if (sp->hitag == VIEW_LEVEL1)
1031             {
1032                 // save it off
1033                 save.sectnum[save.zcount] = sp->sectnum;
1034                 save.zval[save.zcount] = sector[sp->sectnum].ceilingz;
1035                 save.pic[save.zcount] = sector[sp->sectnum].ceilingpicnum;
1036                 save.slope[save.zcount] = sector[sp->sectnum].ceilingheinum;
1037 
1038                 sector[sp->sectnum].ceilingz = newz;
1039 
1040                 // don't change FAF_MIRROR_PIC - ConnectArea
1041                 if (sector[sp->sectnum].ceilingpicnum != FAF_MIRROR_PIC)
1042                     sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC+1;
1043                 sector[sp->sectnum].ceilingheinum = 0;
1044 
1045                 save.zcount++;
1046                 PRODUCTION_ASSERT(save.zcount < ZMAX);
1047             }
1048         }
1049     }
1050 
1051     return TRUE;
1052 }
1053 
1054 short
ViewSectorInScene(short cursectnum,short level)1055 ViewSectorInScene(short cursectnum, short level)
1056 {
1057     int i, nexti;
1058     SPRITEp sp;
1059     short match;
1060 
1061     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti)
1062     {
1063         sp = &sprite[i];
1064 
1065         if (sp->hitag == level)
1066         {
1067             if (cursectnum == sp->sectnum)
1068             {
1069                 // ignore case if sprite is pointing up
1070                 if (sp->ang == 1536)
1071                     continue;
1072 
1073                 // only gets to here is sprite is pointing down
1074 
1075                 // found a potential match
1076                 match = sp->lotag;
1077 
1078                 if (!PicInView(FAF_MIRROR_PIC, TRUE))
1079                     return -1;
1080 
1081                 return match;
1082             }
1083         }
1084     }
1085 
1086     return -1;
1087 }
1088 
1089 void
DrawOverlapRoom(int tx,int ty,int tz,fix16_t tq16ang,fix16_t tq16horiz,short tsectnum)1090 DrawOverlapRoom(int tx, int ty, int tz, fix16_t tq16ang, fix16_t tq16horiz, short tsectnum)
1091 {
1092     short i;
1093     short match;
1094 
1095     save.zcount = 0;
1096 
1097     match = ViewSectorInScene(tsectnum, VIEW_LEVEL1);
1098     if (match != -1)
1099     {
1100         FindCeilingView(match, &tx, &ty, tz, &tsectnum);
1101 
1102         if (tsectnum < 0)
1103             return;
1104 
1105         renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum);
1106         //FAF_DrawRooms(tx, ty, tz, tq16ang, tq16horiz, tsectnum);
1107 
1108         // reset Z's
1109         for (i = 0; i < save.zcount; i++)
1110         {
1111             sector[save.sectnum[i]].floorz = save.zval[i];
1112             sector[save.sectnum[i]].floorpicnum = save.pic[i];
1113             sector[save.sectnum[i]].floorheinum = save.slope[i];
1114         }
1115 
1116         analyzesprites(tx, ty, tz, FALSE);
1117         post_analyzesprites();
1118         renderDrawMasks();
1119 
1120     }
1121     else
1122     {
1123         match = ViewSectorInScene(tsectnum, VIEW_LEVEL2);
1124         if (match != -1)
1125         {
1126             FindFloorView(match, &tx, &ty, tz, &tsectnum);
1127 
1128             if (tsectnum < 0)
1129                 return;
1130 
1131             renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum);
1132             //FAF_DrawRooms(tx, ty, tz, tq16ang, tq16horiz, tsectnum);
1133 
1134             // reset Z's
1135             for (i = 0; i < save.zcount; i++)
1136             {
1137                 sector[save.sectnum[i]].ceilingz = save.zval[i];
1138                 sector[save.sectnum[i]].ceilingpicnum = save.pic[i];
1139                 sector[save.sectnum[i]].ceilingheinum = save.slope[i];
1140             }
1141 
1142             analyzesprites(tx, ty, tz, FALSE);
1143             post_analyzesprites();
1144             renderDrawMasks();
1145 
1146         }
1147     }
1148 }
1149 
1150