//------------------------------------------------------------------------- /* 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 "misc.h" #include "hw_drawinfo.h" BEGIN_SW_NS //////////////////////////////////////////////////////////////////// // // 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; bool FAF_DebugView = false; int COVERinsertsprite(short sectnum, short stat) { short spnum; spnum = insertsprite(sectnum, stat); auto pSprite = &sprite[spnum]; PRODUCTION_ASSERT(spnum >= 0); pSprite->x = sprite[spnum].y = sprite[spnum].z = 0; pSprite->cstat = 0; pSprite->picnum = 0; pSprite->shade = 0; pSprite->pal = 0; pSprite->clipdist = 0; pSprite->xrepeat = sprite[spnum].yrepeat = 0; pSprite->xoffset = sprite[spnum].yoffset = 0; pSprite->ang = 0; pSprite->owner = -1; pSprite->xvel = sprite[spnum].yvel = sprite[spnum].zvel = 0; pSprite->lotag = 0; pSprite->hitag = 0; pSprite->extra = 0; return spnum; } bool FAF_Sector(short sectnum) { int SpriteNum; SPRITEp sp; SectIterator it(sectnum); while ((SpriteNum = it.NextIndex()) >= 0) { 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 (validWallIndex(wall[wall_num].nextwall)) 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; int newsectnum = sectnum; int startclipmask = 0; bool 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 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); } } bool 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; int newsectnum = sects; int xvect, yvect, zvect; short ang; hitdata_t hitinfo; int dist; bool plax_found = false; vec3_t s = { xs, ys, zs }; // ASSERT(sects >= 0 && secte >= 0); // early out to regular routine if ((sects < 0 || !FAF_Sector(sects)) && (secte < 0 || !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 = bcos(ang); yvect = bsin(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) { int i; SPRITEp sp; if (sectnum < 0 || !TEST(sector[sectnum].extra, SECTFX_Z_ADJUST)) return 0L; StatIterator it(STAT_ST1); while ((i = it.NextIndex()) >= 0) { sp = &sprite[i]; if (sp->hitag == hitag && sp->sectnum == sectnum) { return Z(sp->lotag); } } return 0L; } bool SectorZadjust(int ceilhit, int32_t* hiz, short florhit, int32_t* loz) { extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; int z_amt = 0; bool 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)].Data(); if (sectu && FixedToInt(sectu->depth_fixed)) *loz += Z(FixedToInt(sectu->depth_fixed)); } break; case HIT_SPRITE: break; } } void FAFgetzrange(vec3_t pos, 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; bool 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 (sectnum < 0 || !FAF_ConnectArea(sectnum)) { getzrange(&pos, sectnum, hiz, ceilhit, loz, florhit, clipdist, clipmask); SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); return; } getzrange(&pos, sectnum, hiz, ceilhit, loz, florhit, clipdist, clipmask); SkipFAFcheck = SectorZadjust(*ceilhit, hiz, *florhit, loz); WaterAdjust(*florhit, loz); if (SkipFAFcheck) return; if (FAF_ConnectCeiling(sectnum)) { int uppersect = sectnum; int newz = *hiz - Z(2); switch (TEST(*ceilhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(pos.x, pos.y, newz, &uppersect); if (uppersect < 0) return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz); vec3_t npos = pos; npos.z = newz; getzrange(&npos, uppersect, hiz, ceilhit, &foo1, &foo2, clipdist, clipmask); SectorZadjust(*ceilhit, hiz, -1, nullptr); } else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN)) //if (FAF_ConnectFloor(sectnum)) { int lowersect = sectnum; int newz = *loz + Z(2); switch (TEST(*florhit, HIT_MASK)) { case HIT_SECTOR: { break; } case HIT_SPRITE: return; } updatesectorz(pos.x, pos.y, newz, &lowersect); if (lowersect < 0) return; // _ErrMsg(ERR_STD_ARG, "Did not find a sector at %d, %d, %d", x, y, newz); vec3_t npos = pos; npos.z = newz; getzrange(&npos, lowersect, &foo1, &foo2, loz, florhit, clipdist, clipmask); SectorZadjust(-1, nullptr, *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; bool 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)) { int uppersect = sectnum; int newz = *hiz - Z(2); switch (TEST(*ceilhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(x, y, newz, &uppersect); if (uppersect < 0) return; // _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, nullptr); } else if (FAF_ConnectFloor(sectnum) && !TEST(sector[sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN)) //if (FAF_ConnectFloor(sectnum)) { int lowersect = sectnum; int newz = *loz + Z(2); switch (TEST(*florhit, HIT_MASK)) { case HIT_SPRITE: return; } updatesectorz(x, y, newz, &lowersect); if (lowersect < 0) return; // _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, nullptr, *florhit, loz); WaterAdjust(*florhit, loz); } } void SetupMirrorTiles(void) { int i; SPRITEp sp; StatIterator it(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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; } } short GlobStackSect[2]; void GetUpperLowerSector(short match, int x, int y, short *upper, short *lower) { int i; short sectorlist[16]; int sln = 0; int SpriteNum; SPRITEp sp; #if 0 // 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) { bool found = false; SectIterator it(GlobStackSect[i]); while ((SpriteNum = it.NextIndex()) >= 0) { 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++; } } #endif // didn't find it yet so test ALL sectors if (sln < 2) { sln = 0; for (i = 0; i < numsectors; i++)// - 1; i >= 0; i--) { if (inside(x, y, (short) i) == 1) { bool found = false; SectIterator it(i); while ((SpriteNum = it.NextIndex()) >= 0) { 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]; } } bool FindCeilingView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum) { int xoff = 0; int yoff = 0; int i; SPRITEp sp = nullptr; int pix_diff; int newz; save.zcount = 0; // Search Stat List For closest ceiling view sprite // Get the match, xoff, yoff from this point StatIterator it(STAT_FAF); while ((i = it.NextIndex()) >= 0) { sp = &sprite[i]; if (sp->hitag == VIEW_THRU_CEILING && sp->lotag == match) { xoff = *x - sp->x; yoff = *y - sp->y; break; } } it.Reset(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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; if (!sp || sp->hitag != VIEW_THRU_FLOOR) { *sectnum = 0; return false; } if (!testnewrenderer) { pix_diff = labs(z - sector[sp->sectnum].floorz) >> 8; newz = sector[sp->sectnum].floorz + ((pix_diff / 128) + 1) * Z(128); it.Reset(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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; } bool FindFloorView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum) { int xoff = 0; int yoff = 0; int i; SPRITEp sp = nullptr; int newz; int pix_diff; save.zcount = 0; // Search Stat List For closest ceiling view sprite // Get the match, xoff, yoff from this point StatIterator it(STAT_FAF); while ((i = it.NextIndex()) >= 0) { sp = &sprite[i]; if (sp->hitag == VIEW_THRU_FLOOR && sp->lotag == match) { xoff = *x - sp->x; yoff = *y - sp->y; break; } } it.Reset(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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; if (!sp || sp->hitag != VIEW_THRU_CEILING) { *sectnum = 0; return false; } if (!testnewrenderer) { // 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); it.Reset(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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 FindViewSectorInScene(short cursectnum, short level) { int i; SPRITEp sp; short match; StatIterator it(STAT_FAF); while ((i = it.NextIndex()) >= 0) { 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; return match; } } } return -1; } struct PortalGroup { TArray sectors; int othersector = -1; vec3_t offset = { 0,0,0 }; }; // This is very messy because some portals are linked outside the actual portal sectors, so we have to use the complicated original linking logic to find the connection. :? void CollectPortals() { int t = testnewrenderer; testnewrenderer = true; TArray floorportals; TArray ceilingportals; BitArray floordone(numsectors), ceilingdone(numsectors); for (int i = 0; i < numsectors; i++) { sector[i].portalflags = sector[i].portalnum = 0; } floordone.Zero(); ceilingdone.Zero(); portalClear(); for (int i = 0; i < numsectors; i++) { if (sector[i].floorpicnum == FAF_MIRROR_PIC && !floordone[i]) { auto& fp = floorportals[floorportals.Reserve(1)]; fp.sectors.Push(i); floordone.Set(i); for (unsigned ii = 0; ii < fp.sectors.Size(); ii++) { auto sec = §or[fp.sectors[ii]]; for (int w = 0; w < sec->wallnum; w++) { auto ns = wall[sec->wallptr + w].nextsector; if (ns < 0 || floordone[ns] || sector[ns].floorpicnum != FAF_MIRROR_PIC) continue; fp.sectors.Push(ns); floordone.Set(ns); } } } if (sector[i].ceilingpicnum == FAF_MIRROR_PIC && !ceilingdone[i]) { auto& fp = ceilingportals[ceilingportals.Reserve(1)]; fp.sectors.Push(i); ceilingdone.Set(i); for (unsigned ii = 0; ii < fp.sectors.Size(); ii++) { auto sec = §or[fp.sectors[ii]]; for (int w = 0; w < sec->wallnum; w++) { auto ns = wall[sec->wallptr + w].nextsector; if (ns < 0 || ceilingdone[ns] || sector[ns].ceilingpicnum != FAF_MIRROR_PIC) continue; fp.sectors.Push(ns); ceilingdone.Set(ns); } } } } // now try to find connections. for (auto& fp : ceilingportals) { // pick one sprite out of the sectors, repeat until we get a valid connection for (auto sec : fp.sectors) { SectIterator it(sec); int spr; while ((spr = it.NextIndex()) >= 0) { int tx = sprite[spr].x; int ty = sprite[spr].y; int tz = sprite[spr].z; int16_t tsectnum = sec; int match = FindViewSectorInScene(tsectnum, VIEW_LEVEL1); if (match != -1) { FindCeilingView(match, &tx, &ty, tz, &tsectnum); if (tsectnum >= 0 && sector[tsectnum].floorpicnum == FAF_MIRROR_PIC) { // got something! fp.othersector = tsectnum; fp.offset = { tx, ty, tz }; fp.offset -= sprite[spr].pos; goto nextfg; } } } } nextfg:; } for (auto& fp : floorportals) { for (auto sec : fp.sectors) { SectIterator it(sec); int spr; while ((spr = it.NextIndex()) >= 0) { int tx = sprite[spr].x; int ty = sprite[spr].y; int tz = sprite[spr].z; int16_t tsectnum = sec; int match = FindViewSectorInScene(tsectnum, VIEW_LEVEL2); if (match != -1) { FindFloorView(match, &tx, &ty, tz, &tsectnum); if (tsectnum >= 0 && sector[tsectnum].ceilingpicnum == FAF_MIRROR_PIC) { // got something! fp.othersector = tsectnum; fp.offset = { tx, ty, tz }; fp.offset -= sprite[spr].pos; goto nextcg; } } } } nextcg:; } for (auto& pt : floorportals) { if (pt.othersector > -1) { auto findother = [&](int other) -> PortalGroup* { for (auto& pt2 : ceilingportals) { if (pt2.sectors.Find(other) != pt2.sectors.Size()) return &pt2; } return nullptr; }; auto pt2 = findother(pt.othersector); if (pt2) { int pnum = portalAdd(PORTAL_SECTOR_FLOOR, -1, pt.offset.x, pt.offset.y, 0); allPortals[pnum].targets = pt2->sectors; // do not move! We still need the original. for (auto sec : pt.sectors) { sector[sec].portalflags = PORTAL_SECTOR_FLOOR; sector[sec].portalnum = pnum; } } } } for (auto& pt : ceilingportals) { if (pt.othersector > -1) { auto findother = [&](int other) -> PortalGroup* { for (auto& pt2 : floorportals) { if (pt2.sectors.Find(other) != pt2.sectors.Size()) return &pt2; } return nullptr; }; auto pt2 = findother(pt.othersector); if (pt2) { int pnum = portalAdd(PORTAL_SECTOR_CEILING, -1, pt.offset.x, pt.offset.y, 0); allPortals[pnum].targets = std::move(pt2->sectors); for (auto sec : pt.sectors) { sector[sec].portalflags = PORTAL_SECTOR_CEILING; sector[sec].portalnum = pnum; } } } } testnewrenderer = t; } END_SW_NS