mirror of
https://github.com/ZDoom/Raze.git
synced 2025-01-18 14:41:55 +00:00
e5236d3423
It is not enough to just copy the properties - if this happens the entire portal needs to be reinitialized. The only way to do this is to run CollectPortals again to reset all portals in the map.
1196 lines
33 KiB
C++
1196 lines
33 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
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;
|
|
|
|
void COVERupdatesector(int32_t x, int32_t y, int16_t* newsector)
|
|
{
|
|
// ASSERT(*newsector>=0 && *newsector<MAXSECTORS);
|
|
updatesector(x,y,newsector);
|
|
}
|
|
|
|
int COVERinsertsprite(short sectnum, short stat)
|
|
{
|
|
short spnum;
|
|
spnum = insertsprite(sectnum, stat);
|
|
|
|
PRODUCTION_ASSERT(spnum >= 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;
|
|
}
|
|
|
|
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 ((uint16_t)wall[wall_num].nextwall < MAXWALLS)
|
|
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;
|
|
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;
|
|
short 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(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;
|
|
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_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)
|
|
return; // _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:
|
|
{
|
|
break;
|
|
}
|
|
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", 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;
|
|
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))
|
|
{
|
|
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)
|
|
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, 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)
|
|
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, NULL, *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 = NULL;
|
|
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 = 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
|
|
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<int> 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<PortalGroup> floorportals;
|
|
TArray<PortalGroup> ceilingportals;
|
|
FixedBitArray<MAXSECTORS> floordone, ceilingdone;
|
|
|
|
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
|