//------------------------------------------------------------------------- /* Copyright (C) 1997, 2005 - 3D Realms Entertainment This file is part of Shadow Warrior version 1.2 Shadow Warrior is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Original Source: 1997 - Frank Maddin and Jim Norwood Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms */ //------------------------------------------------------------------------- #include "ns.h" #include "build.h" #include "names2.h" #include "panel.h" #include "game.h" #include "warp.h" BEGIN_SW_NS void _ErrMsg(const char *strFile, unsigned uLine, const char *format, ...); void FAF_DrawRooms(int posx, int posy, int posz, short ang, int horiz, short cursectnum); //////////////////////////////////////////////////////////////////// // // FLOOR ABOVE FLOOR // //////////////////////////////////////////////////////////////////// #define ZMAX 400 typedef struct { int32_t zval[ZMAX]; int16_t sectnum[ZMAX]; int16_t pic[ZMAX]; int16_t zcount; int16_t slope[ZMAX]; } SAVE, *SAVEp; SAVE save; SWBOOL FAF_DebugView = 0; void COVERupdatesector(int32_t x, int32_t y, int16_t* newsector) { ASSERT(*newsector>=0 && *newsector= 0); sprite[spnum].x = sprite[spnum].y = sprite[spnum].z = 0; sprite[spnum].cstat = 0; sprite[spnum].picnum = 0; sprite[spnum].shade = 0; sprite[spnum].pal = 0; sprite[spnum].clipdist = 0; sprite[spnum].xrepeat = sprite[spnum].yrepeat = 0; sprite[spnum].xoffset = sprite[spnum].yoffset = 0; sprite[spnum].ang = 0; sprite[spnum].owner = -1; sprite[spnum].xvel = sprite[spnum].yvel = sprite[spnum].zvel = 0; sprite[spnum].lotag = 0; sprite[spnum].hitag = 0; sprite[spnum].extra = 0; return spnum; } SWBOOL FAF_Sector(short sectnum) { short SpriteNum, Next; SPRITEp sp; SWBOOL found = FALSE; TRAVERSE_SPRITE_SECT(headspritesect[sectnum], SpriteNum, Next) { sp = &sprite[SpriteNum]; if (sp->statnum == STAT_FAF && (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6)) { return TRUE; } } return FALSE; } void SetWallWarpHitscan(short sectnum) { short start_wall, wall_num; SPRITEp sp_warp; if (!WarpSectorInfo(sectnum, &sp_warp)) return; if (!sp_warp) return; // move the the next wall wall_num = start_wall = sector[sectnum].wallptr; // Travel all the way around loop setting wall bits do { if (wall[wall_num].nextwall >= 0) SET(wall[wall_num].cstat, CSTAT_WALL_WARP_HITSCAN); wall_num = wall[wall_num].point2; } while (wall_num != start_wall); } void ResetWallWarpHitscan(short sectnum) { short start_wall, wall_num; // move the the next wall wall_num = start_wall = sector[sectnum].wallptr; // Travel all the way around loop setting wall bits do { RESET(wall[wall_num].cstat, CSTAT_WALL_WARP_HITSCAN); wall_num = wall[wall_num].point2; } while (wall_num != start_wall); } 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) { vec3_t firstpos = { x, y, z }; int loz, hiz; short newsectnum = sectnum; int startclipmask = 0; SWBOOL plax_found = FALSE; if (clipmask == CLIPMASK_MISSILE) startclipmask = CLIPMASK_WARP_HITSCAN; hitscan(&firstpos, sectnum, xvect, yvect, zvect, hitinfo, startclipmask); if (hitinfo->sect < 0) return; if (hitinfo->wall >= 0) { // hitscan warping if (TEST(wall[hitinfo->wall].cstat, CSTAT_WALL_WARP_HITSCAN)) { short src_sect = hitinfo->sect; short dest_sect; MONO_PRINT(ds); // back it up a bit to get a correct warp location hitinfo->pos.x -= xvect>>9; hitinfo->pos.y -= yvect>>9; // warp to new x,y,z, sectnum if (Warp(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect)) { vec3_t pos = hitinfo->pos; dest_sect = hitinfo->sect; // hitscan needs to pass through dest sect ResetWallWarpHitscan(dest_sect); // NOTE: This could be recursive I think if need be hitscan(&pos, hitinfo->sect, xvect, yvect, zvect, hitinfo, startclipmask); // reset hitscan block for dest sect SetWallWarpHitscan(dest_sect); return; } else { //DSPRINTF(ds,"hitinfo->pos.x %d, hitinfo->pos.y %d, hitinfo->pos.z %d",hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z); MONO_PRINT(ds); ASSERT(TRUE == FALSE); } } } // make sure it hit JUST a sector before doing a check if (hitinfo->wall < 0 && hitinfo->sprite < 0) { if (TEST(sector[hitinfo->sect].extra, SECTFX_WARP_SECTOR)) { if (TEST(wall[sector[hitinfo->sect].wallptr].cstat, CSTAT_WALL_WARP_HITSCAN)) { // hit the floor of a sector that is a warping sector if (Warp(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect)) { vec3_t pos = hitinfo->pos; hitscan(&pos, hitinfo->sect, xvect, yvect, zvect, hitinfo, clipmask); return; } } else { if (WarpPlane(&hitinfo->pos.x, &hitinfo->pos.y, &hitinfo->pos.z, &hitinfo->sect)) { vec3_t pos = hitinfo->pos; hitscan(&pos, hitinfo->sect, xvect, yvect, zvect, hitinfo, clipmask); return; } } } getzsofslope(hitinfo->sect, hitinfo->pos.x, hitinfo->pos.y, &hiz, &loz); if (labs(hitinfo->pos.z - loz) < Z(4)) { if (FAF_ConnectFloor(hitinfo->sect) && !TEST(sector[hitinfo->sect].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN)) { updatesectorz(hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z + Z(12), &newsectnum); plax_found = TRUE; } } else if (labs(hitinfo->pos.z - hiz) < Z(4)) { if (FAF_ConnectCeiling(hitinfo->sect) && !TEST(sector[hitinfo->sect].floorstat, CEILING_STAT_FAF_BLOCK_HITSCAN)) { updatesectorz(hitinfo->pos.x, hitinfo->pos.y, hitinfo->pos.z - Z(12), &newsectnum); plax_found = TRUE; } } } if (plax_found) { vec3_t pos = hitinfo->pos; hitscan(&pos, newsectnum, xvect, yvect, zvect, hitinfo, clipmask); } } 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) { int loz, hiz; short newsectnum = sects; int xvect, yvect, zvect; short ang; hitdata_t hitinfo; int dist; SWBOOL plax_found = FALSE; vec3_t s = { xs, ys, zs }; ASSERT(sects >= 0 && secte >= 0); // early out to regular routine if (!FAF_Sector(sects) && !FAF_Sector(secte)) { return cansee(xs,ys,zs,sects,xe,ye,ze,secte); } // get angle ang = getangle(xe - xs, ye - ys); // get x,y,z, vectors xvect = sintable[NORM_ANGLE(ang + 512)]; yvect = sintable[NORM_ANGLE(ang)]; // find the distance to the target dist = ksqrt(SQ(xe - xs) + SQ(ye - ys)); if (dist != 0) { if (xe - xs != 0) zvect = scale(xvect, ze - zs, xe - xs); else if (ye - ys != 0) zvect = scale(yvect, ze - zs, ye - ys); else zvect = 0; } else zvect = 0; hitscan(&s, sects, xvect, yvect, zvect, &hitinfo, CLIPMASK_MISSILE); if (hitinfo.sect < 0) return FALSE; // make sure it hit JUST a sector before doing a check if (hitinfo.wall < 0 && hitinfo.sprite < 0) { getzsofslope(hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, &hiz, &loz); if (labs(hitinfo.pos.z - loz) < Z(4)) { if (FAF_ConnectFloor(hitinfo.sect)) { updatesectorz(hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z + Z(12), &newsectnum); plax_found = TRUE; } } else if (labs(hitinfo.pos.z - hiz) < Z(4)) { if (FAF_ConnectCeiling(hitinfo.sect)) { updatesectorz(hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z - Z(12), &newsectnum); plax_found = TRUE; } } } else { return cansee(xs,ys,zs,sects,xe,ye,ze,secte); } if (plax_found) return cansee(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,newsectnum,xe,ye,ze,secte); return FALSE; } int GetZadjustment(short sectnum, short hitag) { short i, nexti; SPRITEp sp; if (!TEST(sector[sectnum].extra, SECTFX_Z_ADJUST)) return 0L; TRAVERSE_SPRITE_STAT(headspritestat[STAT_ST1], i, nexti) { sp = &sprite[i]; if (sp->hitag == hitag && sp->sectnum == sectnum) { return Z(sp->lotag); } } return 0L; } SWBOOL SectorZadjust(int ceilhit, int32_t* hiz, short florhit, int32_t* loz) { extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; int z_amt = 0; SWBOOL SkipFAFcheck = FALSE; if ((int)florhit != -1) { switch (TEST(florhit, HIT_MASK)) { case HIT_SECTOR: { short hit_sector = NORM_SECTOR(florhit); // don't jack with connect sectors if (FAF_ConnectFloor(hit_sector)) { // rippers were dying through the floor in $rock if (TEST(sector[hit_sector].floorstat, CEILING_STAT_FAF_BLOCK_HITSCAN)) break; if (TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST)) { // see if a z adjust ST1 is around z_amt = GetZadjustment(hit_sector, FLOOR_Z_ADJUST); if (z_amt) { // explicit z adjust overrides Connect Floor *loz += z_amt; SkipFAFcheck = TRUE; } } break; } if (!TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST)) break; // see if a z adjust ST1 is around z_amt = GetZadjustment(hit_sector, FLOOR_Z_ADJUST); if (z_amt) { // explicit z adjust overrides plax default *loz += z_amt; } else // default adjustment for plax if (TEST(sector[hit_sector].floorstat, FLOOR_STAT_PLAX)) { *loz += PlaxFloorGlobZadjust; } break; } } } if ((int)ceilhit != -1) { switch (TEST(ceilhit, HIT_MASK)) { case HIT_SECTOR: { short hit_sector = NORM_SECTOR(ceilhit); // don't jack with connect sectors if (FAF_ConnectCeiling(hit_sector)) { if (TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST)) { // see if a z adjust ST1 is around z_amt = GetZadjustment(hit_sector, CEILING_Z_ADJUST); if (z_amt) { // explicit z adjust overrides Connect Floor *loz += z_amt; SkipFAFcheck = TRUE; } } break; } if (!TEST(sector[hit_sector].extra, SECTFX_Z_ADJUST)) break; // see if a z adjust ST1 is around z_amt = GetZadjustment(hit_sector, CEILING_Z_ADJUST); if (z_amt) { // explicit z adjust overrides plax default *hiz -= z_amt; } else // default adjustment for plax if (TEST(sector[hit_sector].ceilingstat, CEILING_STAT_PLAX)) { *hiz -= PlaxCeilGlobZadjust; } break; } } } return SkipFAFcheck; } void WaterAdjust(short florhit, int32_t* loz) { switch (TEST(florhit, HIT_MASK)) { case HIT_SECTOR: { SECT_USERp sectu = SectUser[NORM_SECTOR(florhit)]; if (sectu && sectu->depth) *loz += Z(sectu->depth); } break; case HIT_SPRITE: break; } } void 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) { int foo1; int foo2; SWBOOL SkipFAFcheck; // IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // This will return invalid FAF ceiling and floor heights inside of analyzesprite // because the ceiling and floors get moved out of the way for drawing. // early out to regular routine if (!FAF_ConnectArea(sectnum)) { getzrange_old(x, y, z, sectnum, hiz, ceilhit, loz, florhit, clipdist, clipmask); SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); return; } getzrange_old(x, y, z, sectnum, hiz, ceilhit, loz, florhit, clipdist, clipmask); SkipFAFcheck = SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); if (SkipFAFcheck) return; if (FAF_ConnectCeiling(sectnum)) { short uppersect = sectnum; int newz = *hiz - Z(2); switch (TEST(*ceilhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(x, y, newz, &uppersect); if (uppersect < 0) _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz); getzrange_old(x, y, newz, uppersect, hiz, ceilhit, &foo1, &foo2, clipdist, clipmask); SectorZadjust(*ceilhit, hiz, -1, NULL); } else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN)) //if (FAF_ConnectFloor(sectnum)) { short lowersect = sectnum; int newz = *loz + Z(2); switch (TEST(*florhit, HIT_MASK)) { case HIT_SECTOR: { short hit_sector = NORM_SECTOR(*florhit); break; } case HIT_SPRITE: return; } updatesectorz(x, y, newz, &lowersect); if (lowersect < 0) _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz); getzrange_old(x, y, newz, lowersect, &foo1, &foo2, loz, florhit, clipdist, clipmask); SectorZadjust(-1, NULL, *florhit, loz); WaterAdjust(*florhit, loz); } } void 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) { int foo1; int foo2; SWBOOL SkipFAFcheck; // IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // This will return invalid FAF ceiling and floor heights inside of analyzesprite // because the ceiling and floors get moved out of the way for drawing. // early out to regular routine if (!FAF_ConnectArea(sectnum)) { getzrangepoint(x, y, z, sectnum, hiz, ceilhit, loz, florhit); SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); return; } getzrangepoint(x, y, z, sectnum, hiz, ceilhit, loz, florhit); SkipFAFcheck = SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); if (SkipFAFcheck) return; if (FAF_ConnectCeiling(sectnum)) { short uppersect = sectnum; int newz = *hiz - Z(2); switch (TEST(*ceilhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(x, y, newz, &uppersect); if (uppersect < 0) _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d, sectnum %d", x, y, newz, sectnum); getzrangepoint(x, y, newz, uppersect, hiz, ceilhit, &foo1, &foo2); SectorZadjust(*ceilhit, hiz, -1, NULL); } else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN)) //if (FAF_ConnectFloor(sectnum)) { short lowersect = sectnum; int newz = *loz + Z(2); switch (TEST(*florhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(x, y, newz, &lowersect); if (lowersect < 0) _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d, sectnum %d", x, y, newz, sectnum); getzrangepoint(x, y, newz, lowersect, &foo1, &foo2, loz, florhit); SectorZadjust(-1, NULL, *florhit, loz); WaterAdjust(*florhit, loz); } } #if 0 SWBOOL FAF_ConnectCeiling(short sectnum) { return sector[sectnum].ceilingpicnum == FAF_MIRROR_PIC; } SWBOOL FAF_ConnectFloor(short sectnum) { return sector[sectnum].floorpicnum == FAF_MIRROR_PIC; } #endif // doesn't work for blank pics SWBOOL PicInView(short tile_num, SWBOOL reset) { if (TEST(gotpic[tile_num >> 3], 1 << (tile_num & 7))) { if (reset) RESET(gotpic[tile_num >> 3], 1 << (tile_num & 7)); return TRUE; } return FALSE; } void SetupMirrorTiles(void) { short i, nexti; short j, nextj; SPRITEp sp; SWBOOL found; TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sector[sp->sectnum].ceilingpicnum == FAF_PLACE_MIRROR_PIC) { sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC; SET(sector[sp->sectnum].ceilingstat, CEILING_STAT_PLAX); } if (sector[sp->sectnum].floorpicnum == FAF_PLACE_MIRROR_PIC) { sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC; SET(sector[sp->sectnum].floorstat, FLOOR_STAT_PLAX); } if (sector[sp->sectnum].ceilingpicnum == FAF_PLACE_MIRROR_PIC+1) sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC+1; if (sector[sp->sectnum].floorpicnum == FAF_PLACE_MIRROR_PIC+1) sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC+1; } } //This function is like updatesector, but it takes a z-coordinate in addition // to help it get the right sector when there's overlapping. (I may be // adding this function to the engine or making the standard updatesector // use z's. Until then, use this. ) #if 0 void updatesectorz(int x, int y, int z, short *sectnum) { walltype *wal; int i, j, cz, fz; ASSERT(*sectnum >=0 && *sectnum <= MAXSECTORS); getzsofslope(*sectnum, x, y, &cz, &fz); // go ahead and check the current sector if ((z >= cz) && (z <= fz)) if (inside(x, y, *sectnum) != 0) return; // Test the sectors immediately around your current sector if ((*sectnum >= 0) && (*sectnum < numsectors)) { wal = &wall[sector[*sectnum].wallptr]; j = sector[*sectnum].wallnum; do { i = wal->nextsector; if (i >= 0) { getzsofslope(i, x, y, &cz, &fz); if ((z >= cz) && (z <= fz)) { if (inside(x, y, (short) i) == 1) { *sectnum = i; return; } } } wal++; j--; } while (j != 0); } // didn't find it yet so test ALL sectors for (i = numsectors - 1; i >= 0; i--) { getzsofslope(i, x, y, &cz, &fz); if ((z >= cz) && (z <= fz)) { if (inside(x, y, (short) i) == 1) { *sectnum = i; return; } } } *sectnum = -1; } #endif short GlobStackSect[2]; void GetUpperLowerSector(short match, int x, int y, short *upper, short *lower) { int i, j; short sectorlist[16]; int sln = 0; short SpriteNum, Next; SPRITEp sp; // keep a list of the last stacked sectors the view was in and // check those fisrt sln = 0; for (i = 0; i < (int)SIZ(GlobStackSect); i++) { // will not hurt if GlobStackSect is invalid - inside checks for this if (inside(x, y, GlobStackSect[i]) == 1) { SWBOOL found = FALSE; TRAVERSE_SPRITE_SECT(headspritesect[GlobStackSect[i]], SpriteNum, Next) { sp = &sprite[SpriteNum]; if (sp->statnum == STAT_FAF && (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6) && sp->lotag == match) { found = TRUE; } } if (!found) continue; sectorlist[sln] = GlobStackSect[i]; sln++; } } // didn't find it yet so test ALL sectors if (sln < 2) { sln = 0; for (i = numsectors - 1; i >= 0; i--) { if (inside(x, y, (short) i) == 1) { SWBOOL found = FALSE; TRAVERSE_SPRITE_SECT(headspritesect[i], SpriteNum, Next) { sp = &sprite[SpriteNum]; if (sp->statnum == STAT_FAF && (sp->hitag >= VIEW_LEVEL1 && sp->hitag <= VIEW_LEVEL6) && sp->lotag == match) { found = TRUE; } } if (!found) continue; if (sln < (int)SIZ(GlobStackSect)) GlobStackSect[sln] = i; if (sln < (int)SIZ(sectorlist)) sectorlist[sln] = i; sln++; } } } // might not find ANYTHING if not tagged right if (sln == 0) { *upper = -1; *lower = -1; return; } // Map rooms have NOT been dragged on top of each other else if (sln == 1) { *lower = sectorlist[0]; *upper = sectorlist[0]; return; } // Map rooms HAVE been dragged on top of each other // inside will somtimes find that you are in two different sectors if the x,y // is exactly on a sector line. else if (sln > 2) { //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]); MONO_PRINT(ds); // try again moving the x,y pos around until you only get two sectors GetUpperLowerSector(match, x - 1, y, upper, lower); } if (sln == 2) { if (sector[sectorlist[0]].floorz < sector[sectorlist[1]].floorz) { // swap // make sectorlist[0] the LOW sector short hold; hold = sectorlist[0]; sectorlist[0] = sectorlist[1]; sectorlist[1] = hold; } *lower = sectorlist[0]; *upper = sectorlist[1]; } } SWBOOL FindCeilingView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum) { int xoff = 0; int yoff = 0; short i, nexti; SPRITEp sp = NULL; short top_sprite = -1; int pix_diff; int newz; save.zcount = 0; // Search Stat List For closest ceiling view sprite // Get the match, xoff, yoff from this point TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->hitag == VIEW_THRU_CEILING && sp->lotag == match) { xoff = *x - sp->x; yoff = *y - sp->y; break; } } TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->lotag == match) { // determine x,y position if (sp->hitag == VIEW_THRU_FLOOR) { short upper, lower; *x = sp->x + xoff; *y = sp->y + yoff; // get new sector GetUpperLowerSector(match, *x, *y, &upper, &lower); *sectnum = upper; break; } } } if (*sectnum < 0) return FALSE; ASSERT(sp); ASSERT(sp->hitag == VIEW_THRU_FLOOR); pix_diff = labs(z - sector[sp->sectnum].floorz) >> 8; newz = sector[sp->sectnum].floorz + ((pix_diff / 128) + 1) * Z(128); TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->lotag == match) { // move lower levels ceilings up for the correct view if (sp->hitag == VIEW_LEVEL2) { // save it off save.sectnum[save.zcount] = sp->sectnum; save.zval[save.zcount] = sector[sp->sectnum].floorz; save.pic[save.zcount] = sector[sp->sectnum].floorpicnum; save.slope[save.zcount] = sector[sp->sectnum].floorheinum; sector[sp->sectnum].floorz = newz; // don't change FAF_MIRROR_PIC - ConnectArea if (sector[sp->sectnum].floorpicnum != FAF_MIRROR_PIC) sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC+1; sector[sp->sectnum].floorheinum = 0; save.zcount++; PRODUCTION_ASSERT(save.zcount < ZMAX); } } } return TRUE; } SWBOOL FindFloorView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum) { int xoff = 0; int yoff = 0; short i, nexti; SPRITEp sp = NULL; int newz; int pix_diff; save.zcount = 0; // Search Stat List For closest ceiling view sprite // Get the match, xoff, yoff from this point TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->hitag == VIEW_THRU_FLOOR && sp->lotag == match) { xoff = *x - sp->x; yoff = *y - sp->y; break; } } TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->lotag == match) { // determine x,y position if (sp->hitag == VIEW_THRU_CEILING) { short upper, lower; *x = sp->x + xoff; *y = sp->y + yoff; // get new sector GetUpperLowerSector(match, *x, *y, &upper, &lower); *sectnum = lower; break; } } } if (*sectnum < 0) return FALSE; ASSERT(sp); ASSERT(sp->hitag == VIEW_THRU_CEILING); // move ceiling multiple of 128 so that the wall tile will line up pix_diff = labs(z - sector[sp->sectnum].ceilingz) >> 8; newz = sector[sp->sectnum].ceilingz - ((pix_diff / 128) + 1) * Z(128); TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->lotag == match) { // move upper levels floors down for the correct view if (sp->hitag == VIEW_LEVEL1) { // save it off save.sectnum[save.zcount] = sp->sectnum; save.zval[save.zcount] = sector[sp->sectnum].ceilingz; save.pic[save.zcount] = sector[sp->sectnum].ceilingpicnum; save.slope[save.zcount] = sector[sp->sectnum].ceilingheinum; sector[sp->sectnum].ceilingz = newz; // don't change FAF_MIRROR_PIC - ConnectArea if (sector[sp->sectnum].ceilingpicnum != FAF_MIRROR_PIC) sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC+1; sector[sp->sectnum].ceilingheinum = 0; save.zcount++; PRODUCTION_ASSERT(save.zcount < ZMAX); } } } return TRUE; } short ViewSectorInScene(short cursectnum, short type, short level) { int i, nexti; int j, nextj; SPRITEp sp; SPRITEp sp2; int cz, fz; short match; TRAVERSE_SPRITE_STAT(headspritestat[STAT_FAF], i, nexti) { sp = &sprite[i]; if (sp->hitag == level) { if (cursectnum == sp->sectnum) { // ignore case if sprite is pointing up if (sp->ang == 1536) continue; // only gets to here is sprite is pointing down // found a potential match match = sp->lotag; if (!PicInView(FAF_MIRROR_PIC, TRUE)) return -1; return match; } } } return -1; } void DrawOverlapRoom(int tx, int ty, int tz, short tang, int thoriz, short tsectnum) { short i; short match; save.zcount = 0; match = ViewSectorInScene(tsectnum, VIEW_THRU_CEILING, VIEW_LEVEL1); if (match != -1) { FindCeilingView(match, &tx, &ty, tz, &tsectnum); if (tsectnum < 0) return; drawrooms(tx, ty, tz, tang, thoriz, tsectnum); //FAF_DrawRooms(tx, ty, tz, tang, thoriz, tsectnum); // reset Z's for (i = 0; i < save.zcount; i++) { sector[save.sectnum[i]].floorz = save.zval[i]; sector[save.sectnum[i]].floorpicnum = save.pic[i]; sector[save.sectnum[i]].floorheinum = save.slope[i]; } analyzesprites(tx, ty, tz, FALSE); post_analyzesprites(); renderDrawMasks(); } else { match = ViewSectorInScene(tsectnum, VIEW_THRU_FLOOR, VIEW_LEVEL2); if (match != -1) { FindFloorView(match, &tx, &ty, tz, &tsectnum); if (tsectnum < 0) return; drawrooms(tx, ty, tz, tang, thoriz, tsectnum); //FAF_DrawRooms(tx, ty, tz, tang, thoriz, tsectnum); // reset Z's for (i = 0; i < save.zcount; i++) { sector[save.sectnum[i]].ceilingz = save.zval[i]; sector[save.sectnum[i]].ceilingpicnum = save.pic[i]; sector[save.sectnum[i]].ceilingheinum = save.slope[i]; } analyzesprites(tx, ty, tz, FALSE); post_analyzesprites(); renderDrawMasks(); } } } END_SW_NS