mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-28 20:40:47 +00:00
69a3f9e884
The map isn't necessarily "corrupt", but it has a large water sector with half a dozen different SE7 teleporter sprites in it, of which all but one have destination SE7 sprites that are constantly moving while the matching SE7 in the large sector stays in place. When you teleport, the game code picks one. It doesn't always pick the one that matches. Yeah, OK, it never picks the one that matches. The area is a total clusterfuck. git-svn-id: https://svn.eduke32.com/eduke32@8241 1a8010ca-5511-0410-912e-c29ae57300e0
2650 lines
90 KiB
C++
2650 lines
90 KiB
C++
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
|
|
// Ken Silverman's official web site: "http://www.advsys.net/ken"
|
|
// See the included license file "BUILDLIC.TXT" for license info.
|
|
//
|
|
// This file has been modified from Ken Silverman's original release
|
|
// by Jonathon Fowler (jf@jonof.id.au)
|
|
// by the EDuke32 team (development@voidpoint.com)
|
|
|
|
#include "a.h"
|
|
#include "build.h"
|
|
#include "baselayer.h"
|
|
#include "clip.h"
|
|
#include "engine_priv.h"
|
|
|
|
static int16_t clipnum;
|
|
static linetype clipit[MAXCLIPNUM];
|
|
static int32_t clipsectnum, origclipsectnum, clipspritenum;
|
|
int16_t clipsectorlist[MAXCLIPSECTORS];
|
|
static int16_t origclipsectorlist[MAXCLIPSECTORS];
|
|
static uint8_t clipsectormap[(MAXSECTORS+7)>>3];
|
|
static uint8_t origclipsectormap[(MAXSECTORS+7)>>3];
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
static int16_t clipspritelist[MAXCLIPNUM]; // sector-like sprite clipping
|
|
#endif
|
|
static int16_t clipobjectval[MAXCLIPNUM];
|
|
static uint8_t clipignore[(MAXCLIPNUM+7)>>3];
|
|
|
|
////// sector-like clipping for sprites //////
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
void engineSetClipMap(mapinfo_t *bak, mapinfo_t *newmap)
|
|
{
|
|
if (bak)
|
|
{
|
|
bak->numsectors = numsectors;
|
|
bak->numwalls = numwalls;
|
|
bak->sector = (usectortype *) sector;
|
|
bak->wall = (uwalltype *) wall;
|
|
}
|
|
|
|
if (newmap)
|
|
{
|
|
numsectors = newmap->numsectors;
|
|
numwalls = newmap->numwalls;
|
|
sector = const_cast<sectortype *>((sectortype const *)newmap->sector);
|
|
wall = const_cast<walltype *>((walltype const *)newmap->wall);
|
|
}
|
|
}
|
|
|
|
static mapinfo_t origmapinfo, clipmapinfo;
|
|
int32_t quickloadboard=0;
|
|
|
|
static clipinfo_t clipinfo[CM_MAX];
|
|
static int32_t numclipmaps;
|
|
|
|
static int32_t numclipsects; // number in sectq[]
|
|
static int16_t *sectoidx;
|
|
static int16_t *sectq; // [numsectors]
|
|
static int16_t pictoidx[MAXTILES]; // maps tile num to clipinfo[] index
|
|
static int16_t *tempictoidx;
|
|
|
|
static usectortype *loadsector;
|
|
static uwalltype *loadwall, *loadwallinv;
|
|
static uspritetype *loadsprite;
|
|
|
|
vec2_t hitscangoal = { (1<<29)-1, (1<<29)-1 };
|
|
#ifdef USE_OPENGL
|
|
int32_t hitallsprites = 0;
|
|
#endif
|
|
|
|
void engineInitClipMaps()
|
|
{
|
|
numclipmaps = 0;
|
|
numclipsects = 0;
|
|
|
|
DO_FREE_AND_NULL(sectq);
|
|
DO_FREE_AND_NULL(sectoidx);
|
|
DO_FREE_AND_NULL(tempictoidx);
|
|
DO_FREE_AND_NULL(loadsector);
|
|
DO_FREE_AND_NULL(loadwall);
|
|
DO_FREE_AND_NULL(loadwallinv);
|
|
DO_FREE_AND_NULL(loadsprite);
|
|
|
|
// two's complement trick, -1 = 0xff
|
|
Bmemset(&pictoidx, -1, sizeof(pictoidx));
|
|
Bmemset(&clipmapinfo, 0, sizeof(mapinfo_t));
|
|
|
|
numsectors = 0;
|
|
numwalls = 0;
|
|
}
|
|
|
|
// loads the clip maps.
|
|
// this should be called before any real map is loaded.
|
|
int32_t engineLoadClipMaps(void)
|
|
{
|
|
int32_t i, k, w;
|
|
|
|
int32_t lwcp = 0;
|
|
size_t fi;
|
|
size_t const g_clipMapFilesNum = g_clipMapFiles.size();
|
|
|
|
int32_t *fisec = NULL;
|
|
int32_t *fispr = NULL;
|
|
|
|
int32_t ournumsectors=0, ournumwalls=0, ournumsprites=0;
|
|
|
|
engineInitClipMaps();
|
|
|
|
loadsector = (usectortype *) Xmalloc(MAXSECTORS * sizeof(sectortype));
|
|
loadwall = (uwalltype *) Xmalloc(MAXWALLS * sizeof(walltype));
|
|
loadsprite = (uspritetype *) Xmalloc(MAXSPRITES * sizeof(spritetype));
|
|
|
|
if (g_clipMapFilesNum)
|
|
{
|
|
fisec = (int32_t *) Xcalloc(g_clipMapFilesNum, sizeof(int32_t));
|
|
fispr = (int32_t *) Xcalloc(g_clipMapFilesNum, sizeof(int32_t));
|
|
}
|
|
|
|
quickloadboard = 1;
|
|
for (fi = 0; fi < g_clipMapFilesNum; ++fi)
|
|
{
|
|
int16_t ang, cs;
|
|
vec3_t tmppos;
|
|
|
|
fisec[fi] = ournumsectors;
|
|
fispr[fi] = ournumsprites;
|
|
|
|
i = engineLoadBoard(g_clipMapFiles[fi], 8, &tmppos, &ang, &cs);
|
|
if (i<0)
|
|
continue;
|
|
// Numsprites will now be set!
|
|
|
|
initprintf("Loading clip map: %s\n", g_clipMapFiles[fi]);
|
|
|
|
if (ournumsectors+numsectors>MAXSECTORS ||
|
|
ournumwalls+numwalls>MAXWALLS ||
|
|
ournumsprites+Numsprites>MAXSPRITES)
|
|
{
|
|
initprintf("clip map: warning: exceeded limits when loading %s, aborting.\n", g_clipMapFiles[fi]);
|
|
break;
|
|
}
|
|
|
|
Bmemcpy(loadsector+ournumsectors, sector, numsectors*sizeof(sectortype));
|
|
Bmemcpy(loadwall+ournumwalls, wall, numwalls*sizeof(walltype));
|
|
Bmemcpy(loadsprite+ournumsprites, sprite, Numsprites*sizeof(spritetype));
|
|
for (i=ournumsectors; i<ournumsectors+numsectors; i++)
|
|
loadsector[i].wallptr += ournumwalls;
|
|
for (i=ournumwalls; i<ournumwalls+numwalls; i++)
|
|
{
|
|
if (loadwall[i].point2>=0)
|
|
loadwall[i].point2 += ournumwalls;
|
|
if (loadwall[i].nextwall>=0)
|
|
{
|
|
loadwall[i].nextwall += ournumwalls;
|
|
loadwall[i].nextsector += ournumsectors;
|
|
}
|
|
}
|
|
for (i=ournumsprites; i<ournumsprites+Numsprites; i++)
|
|
if (loadsprite[i].sectnum>=0)
|
|
loadsprite[i].sectnum += ournumsectors;
|
|
ournumsectors += numsectors;
|
|
ournumwalls += numwalls;
|
|
ournumsprites += Numsprites;
|
|
|
|
++lwcp;
|
|
}
|
|
quickloadboard = 0;
|
|
|
|
if (ournumsectors==0 || ournumwalls==0 || ournumsprites==0) // nothing loaded
|
|
{
|
|
engineInitClipMaps();
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return -1;
|
|
}
|
|
|
|
// shrink
|
|
loadsector = (usectortype *) Xrealloc(loadsector, ournumsectors*sizeof(sectortype));
|
|
loadwall = (uwalltype *) Xrealloc(loadwall, ournumwalls*sizeof(walltype));
|
|
|
|
Bmemcpy(sector, loadsector, ournumsectors*sizeof(sectortype));
|
|
Bmemcpy(wall, loadwall, ournumwalls*sizeof(walltype));
|
|
Bmemcpy(sprite, loadsprite, ournumsprites*sizeof(spritetype));
|
|
numsectors = ournumsectors;
|
|
numwalls = ournumwalls;
|
|
|
|
// vvvv don't use headsprite[sect,stat]! vvvv
|
|
|
|
sectoidx = (int16_t *) Xmalloc(numsectors*sizeof(sectoidx[0]));
|
|
|
|
for (i=0; i<numsectors; i++)
|
|
sectoidx[i] = CM_NONE;
|
|
|
|
// determine outer sectors
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
for (w=sector[i].wallptr; w<sector[i].wallptr+sector[i].wallnum; w++)
|
|
if (wall[w].nextsector<0)
|
|
{
|
|
sectoidx[i] = CM_OUTER;
|
|
break;
|
|
}
|
|
}
|
|
// break connections between outer sectors
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
if (sectoidx[i] == CM_OUTER)
|
|
for (w=sector[i].wallptr; w<sector[i].wallptr+sector[i].wallnum; w++)
|
|
{
|
|
k = wall[w].nextwall;
|
|
if (k>=0 && sectoidx[wall[w].nextsector]==CM_OUTER)
|
|
{
|
|
wall[k].nextwall = wall[k].nextsector = -1;
|
|
wall[w].nextwall = wall[w].nextsector = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
int16_t ns, outersect;
|
|
int32_t pn, scnt, x, y, z, maxdist;
|
|
|
|
sectq = (int16_t *) Xmalloc(numsectors*sizeof(sectq[0]));
|
|
tempictoidx = (int16_t *) Xmalloc(MAXTILES*sizeof(tempictoidx[0]));
|
|
|
|
for (i=0; i<MAXTILES; i++)
|
|
tempictoidx[i]=-1;
|
|
|
|
// collect sprite picnums
|
|
for (i=0; i<MAXSPRITES && sprite[i].statnum<MAXSTATUS; i++)
|
|
{
|
|
pn = sprite[i].picnum;
|
|
k = sprite[i].sectnum;
|
|
// -v- note the <= ignore sprites in outer sectors
|
|
if (pn<=0 || pn>=MAXTILES || k<0 || k>=numsectors || (sectoidx[k]&CM_OUTER))
|
|
continue;
|
|
|
|
if (numclipmaps >= CM_MAX)
|
|
{
|
|
initprintf("warning: reached max clip map number %d, not processing any more\n", CM_MAX);
|
|
break;
|
|
}
|
|
|
|
// chain
|
|
if (pictoidx[pn]>=0)
|
|
{
|
|
if (sectoidx[k]&CM_SOME)
|
|
{
|
|
for (fi = 0; fi < g_clipMapFilesNum; ++fi)
|
|
if (k>=fisec[fi])
|
|
break;
|
|
initprintf("clip map \"%s\": error: tried to chain picnum %d (sprite %d) in sector %d which"
|
|
" already belongs to picnum %d.\n", g_clipMapFiles[fi], pn, i-fispr[fi], k-fisec[fi],
|
|
clipinfo[sectoidx[k]].picnum);
|
|
engineInitClipMaps();
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return 2;
|
|
}
|
|
|
|
// new one is front
|
|
clipinfo[numclipmaps].next = pictoidx[pn];
|
|
pictoidx[pn] = numclipmaps;
|
|
}
|
|
else
|
|
{
|
|
clipinfo[numclipmaps].next = -1;
|
|
pictoidx[pn] = numclipmaps;
|
|
}
|
|
|
|
if (!CM_NOROT(i))
|
|
{
|
|
if (sprite[i].ang!=1536 && sprite[i].ang!=512)
|
|
{
|
|
for (fi = 0; fi < g_clipMapFilesNum; ++fi)
|
|
if (i>=fispr[fi])
|
|
break;
|
|
initprintf("clip map \"%s\": warning: sprite %d pointing neither northward nor southward. %s will be wrong.\n",
|
|
g_clipMapFiles[fi], i-fispr[fi], (sprite[i].cstat&48)==32 ? "Scaling and flipping" : "X-flipping");
|
|
}
|
|
}
|
|
|
|
clipinfo[numclipmaps].picnum = pn;
|
|
|
|
// collect sectors
|
|
scnt = numclipsects;
|
|
sectq[numclipsects++] = k;
|
|
sectoidx[k] = numclipmaps;
|
|
|
|
clipinfo[numclipmaps].qbeg = scnt;
|
|
|
|
outersect = -1;
|
|
|
|
do
|
|
{
|
|
k = sectq[scnt];
|
|
|
|
for (w=sector[k].wallptr; w<sector[k].wallptr+sector[k].wallnum; w++)
|
|
{
|
|
ns = wall[w].nextsector;
|
|
if (ns>=0)
|
|
{
|
|
if (sectoidx[ns]==CM_NONE)
|
|
{
|
|
sectoidx[ns] = numclipmaps;
|
|
sectq[numclipsects++] = ns;
|
|
}
|
|
else if (sectoidx[ns]&CM_OUTER)
|
|
{
|
|
if (outersect>=0 && ns!=outersect)
|
|
{
|
|
for (fi = 0; fi < g_clipMapFilesNum; ++fi)
|
|
if (ns>=fisec[fi])
|
|
break;
|
|
initprintf("clip map \"%s\": error: encountered more than one outer sector (%d and %d)"
|
|
" for sprite %d.\n", g_clipMapFiles[fi], outersect-fisec[fi], ns-fisec[fi], i-fispr[fi]);
|
|
engineInitClipMaps();
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return 3;
|
|
}
|
|
|
|
outersect = ns;
|
|
sectoidx[outersect] |= numclipmaps;
|
|
}
|
|
else if (sectoidx[ns]!=numclipmaps)
|
|
{
|
|
for (fi = 0; fi < g_clipMapFilesNum; ++fi)
|
|
if (ns>=fisec[fi])
|
|
break;
|
|
initprintf("clip map \"%s\": error: encountered sector %d belonging to index %d"
|
|
" while collecting sectors for sprite %d (index %d).\n",
|
|
g_clipMapFiles[fi], ns-fisec[fi], sectoidx[ns], i-fispr[fi], numclipmaps);
|
|
engineInitClipMaps();
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return 4;
|
|
}
|
|
}
|
|
}
|
|
} while (++scnt < numclipsects);
|
|
|
|
if (outersect==-1)
|
|
{
|
|
initprintf("clip map: INTERNAL ERROR: outersect==-1!\n");
|
|
engineInitClipMaps();
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return 5;
|
|
}
|
|
|
|
sectq[numclipsects++] = outersect; // last is outer
|
|
clipinfo[numclipmaps].qend = numclipsects-1;
|
|
|
|
// normalize
|
|
maxdist = 0;
|
|
|
|
for (scnt=clipinfo[numclipmaps].qbeg; scnt<=clipinfo[numclipmaps].qend; scnt++)
|
|
{
|
|
k = sectq[scnt];
|
|
|
|
x = sprite[i].x;
|
|
y = sprite[i].y;
|
|
z = sprite[i].z;
|
|
|
|
sector[k].floorz -= z;
|
|
sector[k].ceilingz -= z;
|
|
|
|
if (scnt==clipinfo[numclipmaps].qbeg)
|
|
{
|
|
// backup sprite tags since we'll discard sprites later
|
|
sector[k].CM_XREPEAT = sprite[i].xrepeat;
|
|
sector[k].CM_YREPEAT = sprite[i].yrepeat;
|
|
sector[k].CM_XOFFSET = sprite[i].xoffset;
|
|
sector[k].CM_YOFFSET = sprite[i].yoffset;
|
|
sector[k].CM_CSTAT = sprite[i].cstat;
|
|
sector[k].CM_ANG = sprite[i].ang;
|
|
}
|
|
|
|
// backup floor and ceiling z
|
|
CM_FLOORZ(k) = sector[k].floorz;
|
|
CM_CEILINGZ(k) = sector[k].ceilingz;
|
|
|
|
for (w=sector[k].wallptr; w<sector[k].wallptr+sector[k].wallnum; w++)
|
|
{
|
|
wall[w].x -= x;
|
|
wall[w].y -= y;
|
|
|
|
if (scnt!=clipinfo[numclipmaps].qend)
|
|
{
|
|
if (CM_NOROT(i))
|
|
{
|
|
if (klabs(wall[w].x) > maxdist)
|
|
maxdist = klabs(wall[w].x);
|
|
if (klabs(wall[w].y) > maxdist)
|
|
maxdist = klabs(wall[w].y);
|
|
}
|
|
else
|
|
{
|
|
int32_t tmp = ksqrt(uhypsq(wall[w].x, wall[w].y));
|
|
if (tmp > maxdist)
|
|
maxdist = tmp;
|
|
}
|
|
}
|
|
|
|
// aliasing
|
|
if (wall[w].lotag>0 || wall[w].hitag>0)
|
|
{
|
|
int32_t ii;
|
|
|
|
if (wall[w].lotag>0 && wall[w].hitag>0)
|
|
{
|
|
if (wall[w].lotag > wall[w].hitag)
|
|
swapshort(&wall[w].lotag, &wall[w].hitag);
|
|
|
|
for (ii=wall[w].lotag; ii<wall[w].hitag; ii++)
|
|
tempictoidx[ii] = numclipmaps;
|
|
}
|
|
else if (wall[w].lotag>0)
|
|
{
|
|
if (wall[w].lotag<MAXTILES)
|
|
tempictoidx[wall[w].lotag] = numclipmaps;
|
|
}
|
|
else
|
|
{
|
|
if (wall[w].hitag<MAXTILES)
|
|
tempictoidx[wall[w].hitag] = numclipmaps;
|
|
}
|
|
}
|
|
|
|
CM_WALL_X(w) = wall[w].x;
|
|
CM_WALL_Y(w) = wall[w].y;
|
|
}
|
|
}
|
|
|
|
clipinfo[numclipmaps].maxdist = maxdist;
|
|
numclipmaps++;
|
|
}
|
|
}
|
|
|
|
// yes, too much copying, but better than ugly code
|
|
Bmemcpy(loadsector, sector, ournumsectors*sizeof(sectortype));
|
|
Bmemcpy(loadwall, wall, ournumwalls*sizeof(walltype));
|
|
|
|
// loadwallinv will contain all walls with inverted orientation for x/y-flip handling
|
|
loadwallinv = (uwalltype *) Xmalloc(ournumwalls*sizeof(walltype));
|
|
|
|
{
|
|
int32_t j, loopstart, loopend, numloopwalls;
|
|
|
|
// invert walls!
|
|
loopstart = 0;
|
|
for (j=0; j<ournumwalls; j++)
|
|
{
|
|
wall[j].nextsector = wall[j].nextwall = -1;
|
|
|
|
if (wall[j].point2 < j)
|
|
{
|
|
loopend = j+1;
|
|
numloopwalls = loopend-loopstart;
|
|
|
|
if (numloopwalls<3)
|
|
{
|
|
loopstart = loopend;
|
|
continue;
|
|
}
|
|
|
|
for (k=0; k<numloopwalls; k++)
|
|
{
|
|
wall[loopstart+k].x = loadwall[loopstart + (numloopwalls+1-k)%numloopwalls].x;
|
|
wall[loopstart+k].y = loadwall[loopstart + (numloopwalls+1-k)%numloopwalls].y;
|
|
|
|
CM_WALL_X(loopstart+k) = wall[loopstart+k].x;
|
|
CM_WALL_Y(loopstart+k) = wall[loopstart+k].y;
|
|
}
|
|
|
|
loopstart = loopend;
|
|
}
|
|
}
|
|
|
|
// reconstruct wall connections
|
|
for (i=0; i<ournumsectors; i++)
|
|
{
|
|
for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
|
|
checksectorpointer(j, i);
|
|
}
|
|
}
|
|
Bmemcpy(loadwallinv, wall, ournumwalls*sizeof(walltype));
|
|
|
|
clipmapinfo.numsectors = numsectors;
|
|
clipmapinfo.sector = loadsector;
|
|
clipmapinfo.numwalls = numwalls;
|
|
clipmapinfo.wall = loadwall;
|
|
|
|
for (i=0; i<MAXTILES; i++)
|
|
{
|
|
if (pictoidx[i]==-1 && tempictoidx[i]>=0)
|
|
pictoidx[i]=tempictoidx[i];
|
|
}
|
|
|
|
DO_FREE_AND_NULL(loadsprite);
|
|
DO_FREE_AND_NULL(tempictoidx);
|
|
|
|
// don't let other code be distracted by the temporary map we constructed
|
|
numsectors = 0;
|
|
numwalls = 0;
|
|
initspritelists();
|
|
|
|
if (lwcp > 0)
|
|
initprintf("Loaded clip map%s.\n", lwcp==1 ? "" : "s");
|
|
|
|
Xfree(fisec);
|
|
Xfree(fispr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int clipshape_idx_for_sprite(uspriteptr_t const curspr, int curidx)
|
|
{
|
|
// per-sprite init
|
|
curidx = (curidx < 0) ? pictoidx[curspr->picnum] : clipinfo[curidx].next;
|
|
|
|
while (curidx >= 0 && (curspr->cstat & 32) != (sector[sectq[clipinfo[curidx].qbeg]].CM_CSTAT & 32))
|
|
curidx = clipinfo[curidx].next;
|
|
|
|
return curidx;
|
|
}
|
|
#else
|
|
int32_t clipshape_idx_for_sprite(uspriteptr_t const curspr, int32_t curidx)
|
|
{
|
|
(void)curspr;
|
|
UNREFERENCED_PARAMETER(curidx);
|
|
return -1;
|
|
}
|
|
#endif // HAVE_CLIPSHAPE_FEATURE
|
|
////// //////
|
|
|
|
////////// CLIPMOVE //////////
|
|
|
|
int32_t clipmoveboxtracenum = 3;
|
|
|
|
//
|
|
// clipinsidebox
|
|
//
|
|
int clipinsidebox(vec2_t *vect, int wallnum, int walldist)
|
|
{
|
|
int const r = walldist << 1;
|
|
|
|
auto const wal1 = (uwallptr_t)&wall[wallnum];
|
|
auto const wal2 = (uwallptr_t)&wall[wal1->point2];
|
|
|
|
vec2_t const v1 = { wal1->x + walldist - vect->x, wal1->y + walldist - vect->y };
|
|
vec2_t v2 = { wal2->x + walldist - vect->x, wal2->y + walldist - vect->y };
|
|
|
|
if (((v1.x < 0) && (v2.x < 0)) || ((v1.y < 0) && (v2.y < 0)) || ((v1.x >= r) && (v2.x >= r)) || ((v1.y >= r) && (v2.y >= r)))
|
|
return 0;
|
|
|
|
v2.x -= v1.x; v2.y -= v1.y;
|
|
|
|
if (v2.x * (walldist - v1.y) >= v2.y * (walldist - v1.x)) // Front
|
|
{
|
|
v2.x *= ((v2.x > 0) ? (0 - v1.y) : (r - v1.y));
|
|
v2.y *= ((v2.y > 0) ? (r - v1.x) : (0 - v1.x));
|
|
return v2.x < v2.y;
|
|
}
|
|
|
|
v2.x *= ((v2.x > 0) ? (r - v1.y) : (0 - v1.y));
|
|
v2.y *= ((v2.y > 0) ? (0 - v1.x) : (r - v1.x));
|
|
return (v2.x >= v2.y) << 1;
|
|
}
|
|
|
|
|
|
//
|
|
// clipinsideboxline
|
|
//
|
|
int clipinsideboxline(int x, int y, int x1, int y1, int x2, int y2, int walldist)
|
|
{
|
|
int const r = walldist << 1;
|
|
|
|
x1 += walldist - x;
|
|
x2 += walldist - x;
|
|
|
|
if (((x1 < 0) && (x2 < 0)) || ((x1 >= r) && (x2 >= r)))
|
|
return 0;
|
|
|
|
y1 += walldist - y;
|
|
y2 += walldist - y;
|
|
|
|
if (((y1 < 0) && (y2 < 0)) || ((y1 >= r) && (y2 >= r)))
|
|
return 0;
|
|
|
|
x2 -= x1;
|
|
y2 -= y1;
|
|
|
|
if (x2 * (walldist - y1) >= y2 * (walldist - x1)) // Front
|
|
{
|
|
x2 *= ((x2 > 0) ? (0 - y1) : (r - y1));
|
|
y2 *= ((y2 > 0) ? (r - x1) : (0 - x1));
|
|
return x2 < y2;
|
|
}
|
|
|
|
x2 *= ((x2 > 0) ? (r - y1) : (0 - y1));
|
|
y2 *= ((y2 > 0) ? (0 - x1) : (r - x1));
|
|
return (x2 >= y2) << 1;
|
|
}
|
|
|
|
static int32_t clipmove_warned;
|
|
|
|
static inline void addclipsect(int const sectnum)
|
|
{
|
|
if (clipsectnum < MAXCLIPSECTORS)
|
|
{
|
|
bitmap_set(clipsectormap, sectnum);
|
|
clipsectorlist[clipsectnum++] = sectnum;
|
|
}
|
|
else
|
|
clipmove_warned |= 1;
|
|
}
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
int32_t clipsprite_try(uspriteptr_t const spr, int32_t xmin, int32_t ymin, int32_t xmax, int32_t ymax)
|
|
{
|
|
// try and see whether this sprite's picnum has sector-like clipping data
|
|
int32_t i = pictoidx[spr->picnum];
|
|
// handle sector-like floor sprites separately
|
|
while (i>=0 && (spr->cstat&32) != (clipmapinfo.sector[sectq[clipinfo[i].qbeg]].CM_CSTAT&32))
|
|
i = clipinfo[i].next;
|
|
|
|
if (i>=0)
|
|
{
|
|
int32_t maxcorrection = clipinfo[i].maxdist;
|
|
const int32_t k = sectq[clipinfo[i].qbeg];
|
|
|
|
if ((spr->cstat&CSTAT_SPRITE_ALIGNMENT)!=CSTAT_SPRITE_ALIGNMENT_FLOOR)
|
|
{
|
|
int32_t const tempint1 = clipmapinfo.sector[k].CM_XREPEAT;
|
|
maxcorrection = divideu32_noinline(maxcorrection * (int32_t) spr->xrepeat, tempint1);
|
|
}
|
|
else
|
|
{
|
|
int32_t const tempint1 = clipmapinfo.sector[k].CM_XREPEAT;
|
|
int32_t const tempint2 = clipmapinfo.sector[k].CM_YREPEAT;
|
|
maxcorrection = max(divideu32_noinline(maxcorrection * (int32_t) spr->xrepeat, tempint1),
|
|
divideu32_noinline(maxcorrection * (int32_t) spr->yrepeat, tempint2));
|
|
}
|
|
|
|
maxcorrection -= MAXCLIPDIST;
|
|
|
|
if ((spr->x < xmin - maxcorrection) || (spr->y < ymin - maxcorrection) ||
|
|
(spr->x > xmax + maxcorrection) || (spr->y > ymax + maxcorrection))
|
|
return 1;
|
|
|
|
if (clipspritenum < MAXCLIPNUM)
|
|
clipspritelist[clipspritenum++] = spr-(uspritetype *)sprite;
|
|
//initprintf("%d: clip sprite[%d]\n",clipspritenum,j);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// return: -1 if curspr has x-flip xor y-flip (in the horizontal map plane!), 1 else
|
|
int32_t clipsprite_initindex(int32_t curidx, uspriteptr_t const curspr, int32_t *clipsectcnt, const vec3_t *vect)
|
|
{
|
|
int32_t k, daz = curspr->z;
|
|
int32_t scalex, scaley, scalez, flipx, flipy;
|
|
int32_t flipmul=1;
|
|
|
|
const int32_t j = sectq[clipinfo[curidx].qbeg];
|
|
const int32_t tempint1 = sector[j].CM_XREPEAT;
|
|
const int32_t tempint2 = sector[j].CM_YREPEAT;
|
|
|
|
const int32_t rotang = (curspr->ang - sector[j].CM_ANG)&2047;
|
|
const int32_t dorot = !CM_NOROTS(j);
|
|
|
|
if ((curspr->cstat&CSTAT_SPRITE_ALIGNMENT)!=CSTAT_SPRITE_ALIGNMENT_FLOOR) // face/wall sprite
|
|
{
|
|
scalex = scaley = divscale22(curspr->xrepeat, tempint1);
|
|
scalez = divscale22(curspr->yrepeat, tempint2);
|
|
|
|
flipx = 1-((curspr->cstat&4)>>1);
|
|
flipy = 1;
|
|
}
|
|
else
|
|
{
|
|
scalex = divscale22(curspr->xrepeat, tempint1);
|
|
scaley = divscale22(curspr->yrepeat, tempint2);
|
|
scalez = scalex;
|
|
|
|
flipx = 1-((curspr->cstat&4)>>1);
|
|
flipy = 1-((curspr->cstat&8)>>2);
|
|
}
|
|
|
|
if (dorot)
|
|
{
|
|
flipmul = flipx*flipy;
|
|
if (flipmul==-1)
|
|
wall = (walltype *) loadwallinv;
|
|
}
|
|
|
|
if ((curspr->cstat&128) != (sector[j].CM_CSTAT&128))
|
|
daz += (((curspr->cstat&128)>>6)-1)*((tilesiz[curspr->picnum].y*curspr->yrepeat)<<1);
|
|
|
|
*clipsectcnt = clipsectnum = 0;
|
|
// init sectors for this index
|
|
for (k=clipinfo[curidx].qbeg; k<=clipinfo[curidx].qend; k++)
|
|
{
|
|
int32_t const j = sectq[k];
|
|
auto const sec = §or[j];
|
|
|
|
int32_t const startwall = sec->wallptr, endwall = startwall+sec->wallnum;
|
|
|
|
sec->floorz = daz + mulscale22(scalez, CM_FLOORZ(j));
|
|
sec->ceilingz = daz + mulscale22(scalez, CM_CEILINGZ(j));
|
|
//initprintf("sec %d: f=%d, c=%d\n", j, sec->floorz, sec->ceilingz);
|
|
|
|
for (int w=startwall; w<endwall; w++)
|
|
{
|
|
auto wal = (uwalltype *)(wall + w);
|
|
wal->x = mulscale22(scalex, CM_WALL_X(w));
|
|
wal->y = mulscale22(scaley, CM_WALL_Y(w));
|
|
|
|
if (dorot)
|
|
{
|
|
wal->x *= flipx;
|
|
wal->y *= flipy;
|
|
rotatepoint(zerovec, wal->pos, rotang, &wal->pos);
|
|
}
|
|
|
|
wal->x += curspr->x;
|
|
wal->y += curspr->y;
|
|
}
|
|
|
|
if (inside(vect->x, vect->y, j)==1)
|
|
addclipsect(j);
|
|
}
|
|
|
|
// add outer sector if not inside inner ones
|
|
if (clipsectnum==0)
|
|
addclipsect(sectq[k-1]);
|
|
|
|
return flipmul;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void addclipline(int32_t dax1, int32_t day1, int32_t dax2, int32_t day2, int16_t daoval, int nofix)
|
|
{
|
|
if (clipnum >= MAXCLIPNUM)
|
|
{
|
|
clipmove_warned |= 2;
|
|
return;
|
|
}
|
|
|
|
clipit[clipnum].x1 = dax1; clipit[clipnum].y1 = day1;
|
|
clipit[clipnum].x2 = dax2; clipit[clipnum].y2 = day2;
|
|
clipobjectval[clipnum] = daoval;
|
|
|
|
uint32_t const mask = pow2char[clipnum&7];
|
|
uint8_t &value = clipignore[clipnum>>3];
|
|
value = (value & ~mask) | (-nofix & mask);
|
|
|
|
clipnum++;
|
|
}
|
|
|
|
static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32_t gy, int32_t x1, int32_t y1, int32_t x2,
|
|
int32_t y2, int32_t *daxptr, int32_t *dayptr)
|
|
{
|
|
int32_t daz;
|
|
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
return;
|
|
|
|
if (rintersect(pos->x, pos->y, 0, gx, gy, 0, x1, y1, x2, y2, daxptr, dayptr, &daz) == -1)
|
|
{
|
|
*daxptr = pos->x;
|
|
*dayptr = pos->y;
|
|
}
|
|
}
|
|
|
|
int32_t getceilzofslope_old(int32_t sectnum, int32_t dax, int32_t day)
|
|
{
|
|
int32_t dx, dy, i, j;
|
|
|
|
if (!(sector[sectnum].ceilingstat&2)) return sector[sectnum].ceilingz;
|
|
j = sector[sectnum].wallptr;
|
|
dx = wall[wall[j].point2].x-wall[j].x;
|
|
dy = wall[wall[j].point2].y-wall[j].y;
|
|
i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return(sector[sectnum].ceilingz);
|
|
i = divscale20(sector[sectnum].ceilingheinum,i);
|
|
dx *= i; dy *= i;
|
|
return(sector[sectnum].ceilingz+dmulscale23(dx,day-wall[j].y,-dy,dax-wall[j].x));
|
|
}
|
|
|
|
int32_t getflorzofslope_old(int32_t sectnum, int32_t dax, int32_t day)
|
|
{
|
|
int32_t dx, dy, i, j;
|
|
|
|
if (!(sector[sectnum].floorstat&2)) return sector[sectnum].floorz;
|
|
j = sector[sectnum].wallptr;
|
|
dx = wall[wall[j].point2].x-wall[j].x;
|
|
dy = wall[wall[j].point2].y-wall[j].y;
|
|
i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return sector[sectnum].floorz;
|
|
i = divscale20(sector[sectnum].floorheinum,i);
|
|
dx *= i; dy *= i;
|
|
return(sector[sectnum].floorz+dmulscale23(dx,day-wall[j].y,-dy,dax-wall[j].x));
|
|
}
|
|
|
|
// Returns: should clip?
|
|
static int cliptestsector(int const dasect, int const nextsect, int32_t const flordist, int32_t const ceildist, vec2_t const pos, int32_t const posz)
|
|
{
|
|
Bassert((unsigned)dasect < (unsigned)numsectors && (unsigned)nextsect < (unsigned)numsectors);
|
|
|
|
auto const sec2 = (usectorptr_t)§or[nextsect];
|
|
|
|
switch (enginecompatibility_mode)
|
|
{
|
|
case ENGINECOMPATIBILITY_NONE:
|
|
break;
|
|
case ENGINECOMPATIBILITY_19950829:
|
|
{
|
|
int32_t daz = getflorzofslope_old(dasect, pos.x, pos.y);
|
|
int32_t daz2 = getflorzofslope_old(nextsect, pos.x, pos.y);
|
|
|
|
if (daz2 < daz && (sec2->floorstat&1) == 0)
|
|
if (posz >= daz2-(flordist-1)) return 1;
|
|
daz = getceilzofslope_old(dasect, pos.x, pos.y);
|
|
daz2 = getceilzofslope_old(nextsect, pos.x, pos.y);
|
|
if (daz2 > daz && (sec2->ceilingstat&1) == 0)
|
|
if (posz <= daz2+(ceildist-1)) return 1;
|
|
|
|
return 0;
|
|
}
|
|
default:
|
|
{
|
|
int32_t daz = getflorzofslope(dasect, pos.x, pos.y);
|
|
int32_t daz2 = getflorzofslope(nextsect, pos.x, pos.y);
|
|
|
|
if (daz2 < daz-(1<<8) && (sec2->floorstat&1) == 0)
|
|
if (posz >= daz2-(flordist-1)) return 1;
|
|
daz = getceilzofslope(dasect, pos.x, pos.y);
|
|
daz2 = getceilzofslope(nextsect, pos.x, pos.y);
|
|
if (daz2 > daz+(1<<8) && (sec2->ceilingstat&1) == 0)
|
|
if (posz <= daz2+(ceildist-1)) return 1;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int32_t daz2 = sec2->floorz;
|
|
int32_t dacz2 = sec2->ceilingz;
|
|
|
|
if ((sec2->floorstat|sec2->ceilingstat) & 2)
|
|
getcorrectzsofslope(nextsect, pos.x, pos.y, &dacz2, &daz2);
|
|
|
|
if (daz2 <= dacz2)
|
|
return 1;
|
|
|
|
auto const sec = (usectorptr_t)§or[dasect];
|
|
|
|
int32_t daz = sec->floorz;
|
|
int32_t dacz = sec->ceilingz;
|
|
|
|
if ((sec->floorstat|sec->ceilingstat) & 2)
|
|
getcorrectzsofslope(dasect, pos.x, pos.y, &dacz, &daz);
|
|
|
|
int32_t const sec2height = klabs(daz2-dacz2);
|
|
|
|
return ((klabs(daz-dacz) > sec2height && // clip if the current sector is taller and the next is too small
|
|
sec2height < (ceildist+(CLIPCURBHEIGHT<<1))) ||
|
|
|
|
((sec2->floorstat&1) == 0 && // parallaxed floor curbs don't clip
|
|
posz >= daz2-(flordist-1) && // also account for desired z distance tolerance
|
|
daz2 < daz-CLIPCURBHEIGHT) || // curbs less tall than 256 z units don't clip
|
|
|
|
((sec2->ceilingstat&1) == 0 &&
|
|
posz <= dacz2+(ceildist-1) &&
|
|
dacz2 > dacz /*+CLIPCURBHEIGHT*/)); // ceilings check the same conditions ^^^^^
|
|
}
|
|
|
|
int32_t clipmovex(vec3_t *pos, int16_t *sectnum,
|
|
int32_t xvect, int32_t yvect,
|
|
int32_t const walldist, int32_t const ceildist, int32_t const flordist, uint32_t const cliptype,
|
|
uint8_t const noslidep)
|
|
{
|
|
const int32_t oboxtracenum = clipmoveboxtracenum;
|
|
|
|
if (noslidep)
|
|
clipmoveboxtracenum = 1;
|
|
int32_t ret = clipmove(pos, sectnum, xvect, yvect,
|
|
walldist, ceildist, flordist, cliptype);
|
|
clipmoveboxtracenum = oboxtracenum;
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// raytrace (internal)
|
|
//
|
|
static inline int32_t cliptrace(vec2_t const pos, vec2_t * const goal)
|
|
{
|
|
int32_t hitwall = -1;
|
|
|
|
for (native_t z=clipnum-1; z>=0; z--)
|
|
{
|
|
vec2_t const p1 = { clipit[z].x1, clipit[z].y1 };
|
|
vec2_t const p2 = { clipit[z].x2, clipit[z].y2 };
|
|
vec2_t const area = { p2.x-p1.x, p2.y-p1.y };
|
|
|
|
int32_t topu = area.x*(pos.y-p1.y) - (pos.x-p1.x)*area.y;
|
|
|
|
if (topu <= 0 || area.x*(goal->y-p1.y) > (goal->x-p1.x)*area.y)
|
|
continue;
|
|
|
|
vec2_t const diff = { goal->x-pos.x, goal->y-pos.y };
|
|
|
|
if (diff.x*(p1.y-pos.y) > (p1.x-pos.x)*diff.y || diff.x*(p2.y-pos.y) <= (p2.x-pos.x)*diff.y)
|
|
continue;
|
|
|
|
int32_t const bot = diff.x*area.y - area.x*diff.y;
|
|
native_t cnt = 256;
|
|
|
|
if (!bot)
|
|
continue;
|
|
|
|
vec2_t n;
|
|
|
|
do
|
|
{
|
|
if (--cnt < 0)
|
|
{
|
|
*goal = pos;
|
|
return z;
|
|
}
|
|
|
|
n = { pos.x+scale(diff.x, topu, bot), pos.y+scale(diff.y, topu, bot) };
|
|
topu--;
|
|
} while (area.x*(n.y-p1.y) <= (n.x-p1.x)*area.y);
|
|
|
|
if (klabs(pos.x-n.x)+klabs(pos.y-n.y) < klabs(pos.x-goal->x)+klabs(pos.y-goal->y))
|
|
{
|
|
*goal = n;
|
|
hitwall = z;
|
|
}
|
|
}
|
|
|
|
return hitwall;
|
|
}
|
|
|
|
//
|
|
// keepaway (internal)
|
|
//
|
|
static inline void keepaway(int32_t *x, int32_t *y, int32_t w)
|
|
{
|
|
const int32_t x1 = clipit[w].x1, dx = clipit[w].x2-x1;
|
|
const int32_t y1 = clipit[w].y1, dy = clipit[w].y2-y1;
|
|
const int32_t ox = ksgn(-dy), oy = ksgn(dx);
|
|
char first = (klabs(dx) <= klabs(dy));
|
|
|
|
do
|
|
{
|
|
if (dx*(*y-y1) > (*x-x1)*dy)
|
|
return;
|
|
|
|
if (first == 0)
|
|
*x += ox;
|
|
else
|
|
*y += oy;
|
|
|
|
first ^= 1;
|
|
}
|
|
while (1);
|
|
}
|
|
|
|
static int get_floorspr_clipyou(vec2_t const v1, vec2_t const v2, vec2_t const v3, vec2_t const v4)
|
|
{
|
|
int clipyou = 0;
|
|
|
|
if ((v1.y^v2.y) < 0)
|
|
{
|
|
if ((v1.x^v2.x) < 0) clipyou ^= (v1.x*v2.y < v2.x*v1.y)^(v1.y<v2.y);
|
|
else if (v1.x >= 0) clipyou ^= 1;
|
|
}
|
|
if ((v2.y^v3.y) < 0)
|
|
{
|
|
if ((v2.x^v3.x) < 0) clipyou ^= (v2.x*v3.y < v3.x*v2.y)^(v2.y<v3.y);
|
|
else if (v2.x >= 0) clipyou ^= 1;
|
|
}
|
|
if ((v3.y^v4.y) < 0)
|
|
{
|
|
if ((v3.x^v4.x) < 0) clipyou ^= (v3.x*v4.y < v4.x*v3.y)^(v3.y<v4.y);
|
|
else if (v3.x >= 0) clipyou ^= 1;
|
|
}
|
|
if ((v4.y^v1.y) < 0)
|
|
{
|
|
if ((v4.x^v1.x) < 0) clipyou ^= (v4.x*v1.y < v1.x*v4.y)^(v4.y<v1.y);
|
|
else if (v4.x >= 0) clipyou ^= 1;
|
|
}
|
|
|
|
return clipyou;
|
|
}
|
|
|
|
static void clipupdatesector(vec2_t const pos, int16_t * const sectnum, int walldist)
|
|
{
|
|
if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE)
|
|
{
|
|
updatesector(pos.x, pos.y, sectnum);
|
|
return;
|
|
}
|
|
|
|
if (inside_p(pos.x, pos.y, *sectnum))
|
|
return;
|
|
|
|
int16_t nsecs = min<int16_t>(getsectordist(pos, *sectnum), INT16_MAX);
|
|
|
|
if (nsecs > (walldist + 8))
|
|
{
|
|
OSD_Printf(EDUKE32_PRETTY_FUNCTION ":%d shortest distance between origin point (%d, %d) and sector %d is %d. Sector may be corrupt!\n",
|
|
__LINE__, pos.x, pos.y, *sectnum, nsecs);
|
|
walldist = 0x7fff;
|
|
}
|
|
|
|
static int16_t sectlist[MAXSECTORS];
|
|
static uint8_t sectbitmap[(MAXSECTORS+7)>>3];
|
|
|
|
bfirst_search_init(sectlist, sectbitmap, &nsecs, MAXSECTORS, *sectnum);
|
|
|
|
for (int sectcnt = 0; sectcnt < nsecs; sectcnt++)
|
|
{
|
|
int const listsectnum = sectlist[sectcnt];
|
|
|
|
if (inside_p(pos.x, pos.y, listsectnum))
|
|
SET_AND_RETURN(*sectnum, listsectnum);
|
|
|
|
auto const sec = §or[listsectnum];
|
|
int const startwall = sec->wallptr;
|
|
int const endwall = sec->wallptr + sec->wallnum;
|
|
auto uwal = (uwallptr_t)&wall[startwall];
|
|
|
|
for (int j = startwall; j < endwall; j++, uwal++)
|
|
if (uwal->nextsector >= 0 && bitmap_test(clipsectormap, uwal->nextsector))
|
|
bfirst_search_try(sectlist, sectbitmap, &nsecs, uwal->nextsector);
|
|
}
|
|
|
|
bfirst_search_init(sectlist, sectbitmap, &nsecs, MAXSECTORS, *sectnum);
|
|
|
|
for (int sectcnt = 0; sectcnt < nsecs; sectcnt++)
|
|
{
|
|
int const listsectnum = sectlist[sectcnt];
|
|
|
|
if (inside_p(pos.x, pos.y, listsectnum))
|
|
{
|
|
// add sector to clipping list so the next call to clipupdatesector()
|
|
// finishes in the loop above this one
|
|
addclipsect(listsectnum);
|
|
SET_AND_RETURN(*sectnum, listsectnum);
|
|
}
|
|
|
|
auto const sec = §or[listsectnum];
|
|
int const startwall = sec->wallptr;
|
|
int const endwall = sec->wallptr + sec->wallnum;
|
|
auto uwal = (uwallptr_t)&wall[startwall];
|
|
|
|
// check floor curbs here?
|
|
|
|
for (int j = startwall; j < endwall; j++, uwal++)
|
|
if (uwal->nextsector >= 0 && getwalldist(pos, j) <= (walldist + 8))
|
|
bfirst_search_try(sectlist, sectbitmap, &nsecs, uwal->nextsector);
|
|
}
|
|
|
|
*sectnum = -1;
|
|
}
|
|
|
|
//
|
|
// clipmove
|
|
//
|
|
int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int32_t yvect,
|
|
int32_t const walldist, int32_t const ceildist, int32_t const flordist, uint32_t const cliptype)
|
|
{
|
|
if ((xvect|yvect) == 0 || *sectnum < 0)
|
|
return 0;
|
|
|
|
uspriteptr_t curspr=NULL; // non-NULL when handling sprite with sector-like clipping
|
|
|
|
int const initialsectnum = *sectnum;
|
|
|
|
int32_t const dawalclipmask = (cliptype & 65535); // CLIPMASK0 = 0x00010001
|
|
int32_t const dasprclipmask = (cliptype >> 16); // CLIPMASK1 = 0x01000040
|
|
|
|
vec2_t const move = { xvect, yvect };
|
|
vec2_t goal = { pos->x + (xvect >> 14), pos->y + (yvect >> 14) };
|
|
vec2_t const cent = { (pos->x + goal.x) >> 1, (pos->y + goal.y) >> 1 };
|
|
|
|
//Extra walldist for sprites on sector lines
|
|
vec2_t const diff = { goal.x - (pos->x), goal.y - (pos->y) };
|
|
int32_t const rad = clip_nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(diff.x, diff.y))) + MAXCLIPDIST + walldist + 8;
|
|
vec2_t const clipMin = { cent.x - rad, cent.y - rad };
|
|
vec2_t const clipMax = { cent.x + rad, cent.y + rad };
|
|
|
|
int clipshapeidx = -1;
|
|
int clipsectcnt = 0;
|
|
int clipspritecnt = 0;
|
|
|
|
clipsectorlist[0] = *sectnum;
|
|
|
|
clipsectnum = 1;
|
|
clipnum = 0;
|
|
clipspritenum = 0;
|
|
|
|
clipmove_warned = 0;
|
|
|
|
Bmemset(clipsectormap, 0, (numsectors+7)>>3);
|
|
bitmap_set(clipsectormap, *sectnum);
|
|
|
|
do
|
|
{
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (clipsectcnt>=clipsectnum)
|
|
{
|
|
// one bunch of sectors completed (either the very first
|
|
// one or a sector-like sprite one), prepare the next
|
|
|
|
//initprintf("init sprite %d\n", clipspritecnt);
|
|
if (!curspr)
|
|
{
|
|
// init sector-like sprites for clipping
|
|
origclipsectnum = clipsectnum;
|
|
Bmemcpy(origclipsectorlist, clipsectorlist, clipsectnum*sizeof(clipsectorlist[0]));
|
|
Bmemcpy(origclipsectormap, clipsectormap, (numsectors+7)>>3);
|
|
|
|
// replace sector and wall with clip map
|
|
engineSetClipMap(&origmapinfo, &clipmapinfo);
|
|
}
|
|
|
|
curspr = (uspriteptr_t)&sprite[clipspritelist[clipspritecnt]];
|
|
clipshapeidx = clipshape_idx_for_sprite(curspr, clipshapeidx);
|
|
|
|
if (clipshapeidx < 0)
|
|
{
|
|
clipspritecnt++;
|
|
continue;
|
|
}
|
|
|
|
clipsprite_initindex(clipshapeidx, curspr, &clipsectcnt, pos);
|
|
}
|
|
#endif
|
|
|
|
int const dasect = clipsectorlist[clipsectcnt++];
|
|
//if (curspr)
|
|
// initprintf("sprite %d/%d: sect %d/%d (%d)\n", clipspritecnt,clipspritenum, clipsectcnt,clipsectnum,dasect);
|
|
|
|
////////// Walls //////////
|
|
|
|
auto const sec = (usectorptr_t)§or[dasect];
|
|
int const startwall = sec->wallptr;
|
|
int const endwall = startwall + sec->wallnum;
|
|
auto wal = (uwallptr_t)&wall[startwall];
|
|
|
|
for (native_t j=startwall; j<endwall; j++, wal++)
|
|
{
|
|
auto const wal2 = (uwallptr_t)&wall[wal->point2];
|
|
|
|
if ((wal->x < clipMin.x && wal2->x < clipMin.x) || (wal->x > clipMax.x && wal2->x > clipMax.x) ||
|
|
(wal->y < clipMin.y && wal2->y < clipMin.y) || (wal->y > clipMax.y && wal2->y > clipMax.y))
|
|
continue;
|
|
|
|
vec2_t p1 = wal->pos;
|
|
vec2_t p2 = wal2->pos;
|
|
vec2_t d = { p2.x-p1.x, p2.y-p1.y };
|
|
|
|
if (d.x * (pos->y-p1.y) < (pos->x-p1.x) * d.y)
|
|
continue; //If wall's not facing you
|
|
|
|
vec2_t const r = { (d.y > 0) ? clipMax.x : clipMin.x, (d.x > 0) ? clipMin.y : clipMax.y };
|
|
vec2_t v = { d.x * (r.y - p1.y), d.y * (r.x - p1.x) };
|
|
|
|
if (v.x >= v.y)
|
|
continue;
|
|
|
|
int clipyou = 0;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
if (wal->nextsector>=0)
|
|
{
|
|
auto const sec2 = (usectorptr_t)§or[wal->nextsector];
|
|
|
|
clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y);
|
|
|
|
#define CLIPMV_SPR_F_DAZ2 getcorrectflorzofslope(wal->nextsector, v.x, v.y)
|
|
#define CLIPMV_SPR_F_BASEZ getcorrectflorzofslope(sectq[clipinfo[clipshapeidx].qend], v.x, v.y)
|
|
|
|
if ((sec2->floorstat&1) == 0)
|
|
{
|
|
if (CLIPMV_SPR_F_DAZ2-(flordist-1) <= pos->z && pos->z <= CLIPMV_SPR_F_BASEZ+(flordist-1))
|
|
clipyou = 1;
|
|
}
|
|
|
|
if (clipyou == 0)
|
|
{
|
|
#define CLIPMV_SPR_C_DAZ2 getcorrectceilzofslope(wal->nextsector, v.x, v.y)
|
|
#define CLIPMV_SPR_C_BASEZ getcorrectceilzofslope(sectq[clipinfo[clipshapeidx].qend], v.x, v.y)
|
|
|
|
if ((sec2->ceilingstat & 1) == 0)
|
|
{
|
|
if (CLIPMV_SPR_C_BASEZ-(ceildist-1) <= pos->z && pos->z <= CLIPMV_SPR_C_DAZ2+(ceildist-1))
|
|
clipyou = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (wal->nextsector < 0 || (wal->cstat&dawalclipmask))
|
|
{
|
|
clipyou = 1;
|
|
#ifdef YAX_ENABLE
|
|
int const cb = yax_getbunch(dasect, YAX_CEILING);
|
|
|
|
if (cb >= 0 && (sec->ceilingstat & yax_waltosecmask(dawalclipmask)) == 0)
|
|
{
|
|
int const ynw = yax_getnextwall(j, YAX_CEILING);
|
|
|
|
if (ynw >= 0 && wall[ynw].nextsector >= 0 && (wall[ynw].cstat & dawalclipmask) == 0)
|
|
{
|
|
clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y);
|
|
clipyou = cliptestsector(dasect, wall[ynw].nextsector, flordist, ceildist, v, pos->z);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else if (editstatus == 0)
|
|
{
|
|
clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y);
|
|
clipyou = cliptestsector(dasect, wal->nextsector, flordist, ceildist, v, pos->z);
|
|
}
|
|
|
|
// We're not interested in any sector reached by portal traversal that we're "inside" of.
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE && !curspr && dasect != initialsectnum
|
|
&& inside(pos->x, pos->y, dasect) == 1)
|
|
{
|
|
int k;
|
|
for (k=startwall; k<endwall; k++)
|
|
if (wall[k].nextsector == initialsectnum)
|
|
break;
|
|
if (k == endwall)
|
|
break;
|
|
}
|
|
|
|
if (clipyou)
|
|
{
|
|
int16_t const objtype = curspr ? (int16_t)(curspr - (uspritetype *)sprite) + 49152 : (int16_t)j + 32768;
|
|
|
|
//Add 2 boxes at endpoints
|
|
int32_t bsz = walldist; if (diff.x < 0) bsz = -bsz;
|
|
addclipline(p1.x-bsz, p1.y-bsz, p1.x-bsz, p1.y+bsz, objtype, false);
|
|
addclipline(p2.x-bsz, p2.y-bsz, p2.x-bsz, p2.y+bsz, objtype, false);
|
|
bsz = walldist; if (diff.y < 0) bsz = -bsz;
|
|
addclipline(p1.x+bsz, p1.y-bsz, p1.x-bsz, p1.y-bsz, objtype, false);
|
|
addclipline(p2.x+bsz, p2.y-bsz, p2.x-bsz, p2.y-bsz, objtype, false);
|
|
|
|
v.x = walldist; if (d.y > 0) v.x = -v.x;
|
|
v.y = walldist; if (d.x < 0) v.y = -v.y;
|
|
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE && d.x * (pos->y-p1.y-v.y) < (pos->x-p1.x-v.x) * d.y)
|
|
v.x >>= 1, v.y >>= 1;
|
|
|
|
addclipline(p1.x+v.x, p1.y+v.y, p2.x+v.x, p2.y+v.y, objtype, false);
|
|
}
|
|
else if (wal->nextsector>=0)
|
|
{
|
|
if (bitmap_test(clipsectormap, wal->nextsector) == 0)
|
|
addclipsect(wal->nextsector);
|
|
}
|
|
}
|
|
|
|
if (clipmove_warned & 1)
|
|
OSD_Printf("clipsectnum >= MAXCLIPSECTORS!\n");
|
|
|
|
if (clipmove_warned & 2)
|
|
OSD_Printf("clipnum >= MAXCLIPNUM!\n");
|
|
|
|
////////// Sprites //////////
|
|
|
|
if (dasprclipmask==0)
|
|
continue;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
continue; // next sector of this index
|
|
#endif
|
|
for (native_t j=headspritesect[dasect]; j>=0; j=nextspritesect[j])
|
|
{
|
|
auto const spr = (uspriteptr_t)&sprite[j];
|
|
const int32_t cstat = spr->cstat;
|
|
|
|
if ((cstat&dasprclipmask) == 0)
|
|
continue;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (clipsprite_try(spr, clipMin.x, clipMin.y, clipMax.x, clipMax.y))
|
|
continue;
|
|
#endif
|
|
vec2_t p1 = *(vec2_t const *)spr;
|
|
|
|
switch (cstat & (CSTAT_SPRITE_ALIGNMENT_WALL | CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
case CSTAT_SPRITE_ALIGNMENT_FACING:
|
|
if (p1.x >= clipMin.x && p1.x <= clipMax.x && p1.y >= clipMin.y && p1.y <= clipMax.y)
|
|
{
|
|
int32_t height, daz = spr->z+spriteheightofs(j, &height, 1);
|
|
|
|
if (pos->z > daz-height-flordist && pos->z < daz+ceildist)
|
|
{
|
|
int32_t bsz = (spr->clipdist << 2)+walldist;
|
|
if (diff.x < 0) bsz = -bsz;
|
|
addclipline(p1.x-bsz, p1.y-bsz, p1.x-bsz, p1.y+bsz, (int16_t)j+49152, false);
|
|
bsz = (spr->clipdist << 2)+walldist;
|
|
if (diff.y < 0) bsz = -bsz;
|
|
addclipline(p1.x+bsz, p1.y-bsz, p1.x-bsz, p1.y-bsz, (int16_t)j+49152, false);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_WALL:
|
|
{
|
|
int32_t height, daz = spr->z+spriteheightofs(j, &height, 1);
|
|
|
|
if (pos->z > daz-height-flordist && pos->z < daz+ceildist)
|
|
{
|
|
vec2_t p2;
|
|
|
|
get_wallspr_points(spr, &p1.x, &p2.x, &p1.y, &p2.y);
|
|
|
|
if (clipinsideboxline(cent.x, cent.y, p1.x, p1.y, p2.x, p2.y, rad) != 0)
|
|
{
|
|
vec2_t v = { mulscale14(sintable[(spr->ang+256+512) & 2047], walldist),
|
|
mulscale14(sintable[(spr->ang+256) & 2047], walldist) };
|
|
|
|
if ((p1.x-pos->x) * (p2.y-pos->y) >= (p2.x-pos->x) * (p1.y-pos->y)) // Front
|
|
addclipline(p1.x+v.x, p1.y+v.y, p2.x+v.y, p2.y-v.x, (int16_t)j+49152, false);
|
|
else
|
|
{
|
|
if ((cstat & 64) != 0)
|
|
continue;
|
|
addclipline(p2.x-v.x, p2.y-v.y, p1.x-v.y, p1.y+v.x, (int16_t)j+49152, false);
|
|
}
|
|
|
|
//Side blocker
|
|
if ((p2.x-p1.x) * (pos->x-p1.x)+(p2.y-p1.y) * (pos->y-p1.y) < 0)
|
|
addclipline(p1.x-v.y, p1.y+v.x, p1.x+v.x, p1.y+v.y, (int16_t)j+49152, true);
|
|
else if ((p1.x-p2.x) * (pos->x-p2.x)+(p1.y-p2.y) * (pos->y-p2.y) < 0)
|
|
addclipline(p2.x+v.y, p2.y-v.x, p2.x-v.x, p2.y-v.y, (int16_t)j+49152, true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
|
|
{
|
|
if (pos->z > spr->z-flordist && pos->z < spr->z+ceildist)
|
|
{
|
|
if ((cstat&64) != 0)
|
|
if ((pos->z > spr->z) == ((cstat&8)==0))
|
|
continue;
|
|
|
|
rxi[0] = p1.x;
|
|
ryi[0] = p1.y;
|
|
|
|
get_floorspr_points((uspriteptr_t) spr, 0, 0, &rxi[0], &rxi[1], &rxi[2], &rxi[3],
|
|
&ryi[0], &ryi[1], &ryi[2], &ryi[3]);
|
|
|
|
vec2_t v = { mulscale14(sintable[(spr->ang-256+512)&2047], walldist),
|
|
mulscale14(sintable[(spr->ang-256)&2047], walldist) };
|
|
|
|
if ((rxi[0]-pos->x) * (ryi[1]-pos->y) < (rxi[1]-pos->x) * (ryi[0]-pos->y))
|
|
{
|
|
if (clipinsideboxline(cent.x, cent.y, rxi[1], ryi[1], rxi[0], ryi[0], rad) != 0)
|
|
addclipline(rxi[1]-v.y, ryi[1]+v.x, rxi[0]+v.x, ryi[0]+v.y, (int16_t)j+49152, false);
|
|
}
|
|
else if ((rxi[2]-pos->x) * (ryi[3]-pos->y) < (rxi[3]-pos->x) * (ryi[2]-pos->y))
|
|
{
|
|
if (clipinsideboxline(cent.x, cent.y, rxi[3], ryi[3], rxi[2], ryi[2], rad) != 0)
|
|
addclipline(rxi[3]+v.y, ryi[3]-v.x, rxi[2]-v.x, ryi[2]-v.y, (int16_t)j+49152, false);
|
|
}
|
|
|
|
if ((rxi[1]-pos->x) * (ryi[2]-pos->y) < (rxi[2]-pos->x) * (ryi[1]-pos->y))
|
|
{
|
|
if (clipinsideboxline(cent.x, cent.y, rxi[2], ryi[2], rxi[1], ryi[1], rad) != 0)
|
|
addclipline(rxi[2]-v.x, ryi[2]-v.y, rxi[1]-v.y, ryi[1]+v.x, (int16_t)j+49152, false);
|
|
}
|
|
else if ((rxi[3]-pos->x) * (ryi[0]-pos->y) < (rxi[0]-pos->x) * (ryi[3]-pos->y))
|
|
{
|
|
if (clipinsideboxline(cent.x, cent.y, rxi[0], ryi[0], rxi[3], ryi[3], rad) != 0)
|
|
addclipline(rxi[0]+v.x, ryi[0]+v.y, rxi[3]+v.y, ryi[3]-v.x, (int16_t)j+49152, false);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (clipsectcnt < clipsectnum || clipspritecnt < clipspritenum);
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
// restore original map
|
|
engineSetClipMap(NULL, &origmapinfo);
|
|
|
|
clipsectnum = origclipsectnum;
|
|
Bmemcpy(clipsectorlist, origclipsectorlist, clipsectnum*sizeof(clipsectorlist[0]));
|
|
Bmemcpy(clipsectormap, origclipsectormap, (numsectors+7)>>3);
|
|
}
|
|
#endif
|
|
|
|
int32_t hitwalls[4], hitwall;
|
|
int32_t clipReturn = 0;
|
|
|
|
native_t cnt = clipmoveboxtracenum;
|
|
|
|
do
|
|
{
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
for (native_t i=clipnum-1;i>=0;--i)
|
|
{
|
|
if (!bitmap_test(clipignore, i) && clipinsideboxline(pos->x, pos->y, clipit[i].x1, clipit[i].y1, clipit[i].x2, clipit[i].y2, walldist))
|
|
{
|
|
vec2_t const vec = pos->vec2;
|
|
keepaway(&pos->x, &pos->y, i);
|
|
if (inside(pos->x,pos->y, *sectnum) != 1)
|
|
pos->vec2 = vec;
|
|
break;
|
|
}
|
|
}
|
|
|
|
vec2_t vec = goal;
|
|
|
|
if ((hitwall = cliptrace(pos->vec2, &vec)) >= 0)
|
|
{
|
|
vec2_t const clipr = { clipit[hitwall].x2 - clipit[hitwall].x1, clipit[hitwall].y2 - clipit[hitwall].y1 };
|
|
int64_t const templl = compat_maybe_truncate_to_int32((int64_t)clipr.x * clipr.x + (int64_t)clipr.y * clipr.y);
|
|
|
|
if (templl > 0)
|
|
{
|
|
int64_t const templl2 = compat_maybe_truncate_to_int32((int64_t)(goal.x-vec.x)*clipr.x + (int64_t)(goal.y-vec.y)*clipr.y);
|
|
int32_t const i = enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || ((llabs(templl2)>>11) < templl) ?
|
|
divscale64(templl2, templl, 20) : 0;
|
|
|
|
goal = { mulscale20(clipr.x, i)+vec.x, mulscale20(clipr.y, i)+vec.y };
|
|
}
|
|
|
|
int32_t tempint;
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
tempint = clipr.x*(move.x>>6)+clipr.y*(move.y>>6);
|
|
else
|
|
tempint = dmulscale6(clipr.x, move.x, clipr.y, move.y);
|
|
|
|
for (native_t i=cnt+1, j; i<=clipmoveboxtracenum; ++i)
|
|
{
|
|
j = hitwalls[i];
|
|
|
|
int32_t tempint2;
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
tempint2 = (clipit[j].x2-clipit[j].x1)*(move.x>>6)+(clipit[j].y2-clipit[j].y1)*(move.y>>6);
|
|
else
|
|
tempint2 = dmulscale6(clipit[j].x2-clipit[j].x1, move.x, clipit[j].y2-clipit[j].y1, move.y);
|
|
|
|
if ((tempint ^ tempint2) < 0)
|
|
{
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19961112)
|
|
updatesector(pos->x, pos->y, sectnum);
|
|
return clipReturn;
|
|
}
|
|
}
|
|
|
|
keepaway(&goal.x, &goal.y, hitwall);
|
|
xvect = (goal.x-vec.x)<<14;
|
|
yvect = (goal.y-vec.y)<<14;
|
|
|
|
if (cnt == clipmoveboxtracenum)
|
|
clipReturn = (uint16_t) clipobjectval[hitwall];
|
|
hitwalls[cnt] = hitwall;
|
|
}
|
|
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
clipupdatesector(vec, sectnum, rad);
|
|
|
|
pos->x = vec.x;
|
|
pos->y = vec.y;
|
|
cnt--;
|
|
} while ((xvect|yvect) != 0 && hitwall >= 0 && cnt > 0);
|
|
|
|
if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE)
|
|
{
|
|
for (native_t j=0; j<clipsectnum; j++)
|
|
if (inside(pos->x, pos->y, clipsectorlist[j]) == 1)
|
|
{
|
|
*sectnum = clipsectorlist[j];
|
|
return clipReturn;
|
|
}
|
|
|
|
int32_t tempint2, tempint1 = INT32_MAX;
|
|
*sectnum = -1;
|
|
for (native_t j=numsectors-1; j>=0; j--)
|
|
if (inside(pos->x, pos->y, j) == 1)
|
|
{
|
|
if (enginecompatibility_mode != ENGINECOMPATIBILITY_19950829 && (sector[j].ceilingstat&2))
|
|
tempint2 = getceilzofslope(j, pos->x, pos->y) - pos->z;
|
|
else
|
|
tempint2 = sector[j].ceilingz - pos->z;
|
|
|
|
if (tempint2 > 0)
|
|
{
|
|
if (tempint2 < tempint1)
|
|
{
|
|
*sectnum = j; tempint1 = tempint2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (enginecompatibility_mode != ENGINECOMPATIBILITY_19950829 && (sector[j].floorstat&2))
|
|
tempint2 = pos->z - getflorzofslope(j, pos->x, pos->y);
|
|
else
|
|
tempint2 = pos->z - sector[j].floorz;
|
|
|
|
if (tempint2 <= 0)
|
|
{
|
|
*sectnum = j;
|
|
return clipReturn;
|
|
}
|
|
if (tempint2 < tempint1)
|
|
{
|
|
*sectnum = j; tempint1 = tempint2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return clipReturn;
|
|
}
|
|
|
|
|
|
//
|
|
// pushmove
|
|
//
|
|
int pushmove(vec3_t *const vect, int16_t *const sectnum,
|
|
int32_t const walldist, int32_t const ceildist, int32_t const flordist, uint32_t const cliptype, bool clear /*= true*/)
|
|
{
|
|
int bad;
|
|
|
|
const int32_t dawalclipmask = (cliptype&65535);
|
|
// const int32_t dasprclipmask = (cliptype>>16);
|
|
|
|
if (*sectnum < 0)
|
|
return -1;
|
|
|
|
int32_t k = 32;
|
|
|
|
int dir = 1;
|
|
do
|
|
{
|
|
int32_t clipsectcnt = 0;
|
|
|
|
bad = 0;
|
|
|
|
if (clear)
|
|
{
|
|
clipsectorlist[0] = *sectnum;
|
|
clipsectnum = 1;
|
|
|
|
Bmemset(clipsectormap, 0, (numsectors + 7) >> 3);
|
|
bitmap_set(clipsectormap, *sectnum);
|
|
}
|
|
|
|
do
|
|
{
|
|
uwallptr_t wal;
|
|
int32_t startwall, endwall;
|
|
#if 0
|
|
// Push FACE sprites
|
|
for (i=headspritesect[clipsectorlist[clipsectcnt]]; i>=0; i=nextspritesect[i])
|
|
{
|
|
spr = &sprite[i];
|
|
if (((spr->cstat&48) != 0) && ((spr->cstat&48) != 48)) continue;
|
|
if ((spr->cstat&dasprclipmask) == 0) continue;
|
|
|
|
dax = (vect->x)-spr->x; day = (vect->y)-spr->y;
|
|
t = (spr->clipdist<<2)+walldist;
|
|
if ((klabs(dax) < t) && (klabs(day) < t))
|
|
{
|
|
daz = spr->z + spriteheightofs(i, &t, 1);
|
|
if (((vect->z) < daz+ceildist) && ((vect->z) > daz-t-flordist))
|
|
{
|
|
t = (spr->clipdist<<2)+walldist;
|
|
|
|
j = getangle(dax, day);
|
|
dx = (sintable[(j+512)&2047]>>11);
|
|
dy = (sintable[(j)&2047]>>11);
|
|
bad2 = 16;
|
|
do
|
|
{
|
|
vect->x = (vect->x) + dx; vect->y = (vect->y) + dy;
|
|
bad2--; if (bad2 == 0) break;
|
|
} while ((klabs((vect->x)-spr->x) < t) && (klabs((vect->y)-spr->y) < t));
|
|
bad = -1;
|
|
k--; if (k <= 0) return bad;
|
|
updatesector(vect->x, vect->y, sectnum);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
auto sec = (usectorptr_t)§or[clipsectorlist[clipsectcnt]];
|
|
if (dir > 0)
|
|
startwall = sec->wallptr, endwall = startwall + sec->wallnum;
|
|
else
|
|
endwall = sec->wallptr, startwall = endwall + sec->wallnum;
|
|
|
|
int i;
|
|
|
|
for (i=startwall, wal=(uwallptr_t)&wall[startwall]; i!=endwall; i+=dir, wal+=dir)
|
|
if (clipinsidebox(&vect->vec2, i, walldist-4) == 1)
|
|
{
|
|
int j = 0;
|
|
if (wal->nextsector < 0 || wal->cstat&dawalclipmask) j = 1;
|
|
else
|
|
{
|
|
int32_t daz2;
|
|
vec2_t closest;
|
|
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
closest = vect->vec2;
|
|
else
|
|
{
|
|
//Find closest point on wall (dax, day) to (vect->x, vect->y)
|
|
int32_t dax = wall[wal->point2].x-wal->x;
|
|
int32_t day = wall[wal->point2].y-wal->y;
|
|
int32_t daz = dax*((vect->x)-wal->x) + day*((vect->y)-wal->y);
|
|
int32_t t;
|
|
if (daz <= 0)
|
|
t = 0;
|
|
else
|
|
{
|
|
daz2 = dax*dax+day*day;
|
|
if (daz >= daz2) t = (1<<30); else t = divscale30(daz, daz2);
|
|
}
|
|
dax = wal->x + mulscale30(dax, t);
|
|
day = wal->y + mulscale30(day, t);
|
|
|
|
closest = { dax, day };
|
|
}
|
|
|
|
j = cliptestsector(clipsectorlist[clipsectcnt], wal->nextsector, flordist, ceildist, closest, vect->z);
|
|
}
|
|
|
|
if (j != 0)
|
|
{
|
|
j = getangle(wall[wal->point2].x-wal->x, wall[wal->point2].y-wal->y);
|
|
int32_t dx = (sintable[(j+1024)&2047]>>11);
|
|
int32_t dy = (sintable[(j+512)&2047]>>11);
|
|
int bad2 = 16;
|
|
do
|
|
{
|
|
vect->x = (vect->x) + dx; vect->y = (vect->y) + dy;
|
|
bad2--; if (bad2 == 0) break;
|
|
} while (clipinsidebox(&vect->vec2, i, walldist-4) != 0);
|
|
bad = -1;
|
|
k--; if (k <= 0) return bad;
|
|
clipupdatesector(vect->vec2, sectnum, walldist);
|
|
if (*sectnum < 0) return -1;
|
|
}
|
|
else if (bitmap_test(clipsectormap, wal->nextsector) == 0)
|
|
addclipsect(wal->nextsector);
|
|
}
|
|
|
|
clipsectcnt++;
|
|
} while (clipsectcnt < clipsectnum);
|
|
dir = -dir;
|
|
} while (bad != 0);
|
|
|
|
return bad;
|
|
}
|
|
|
|
//
|
|
// getzrange
|
|
//
|
|
void getzrange(const vec3_t *pos, int16_t sectnum,
|
|
int32_t *ceilz, int32_t *ceilhit, int32_t *florz, int32_t *florhit,
|
|
int32_t walldist, uint32_t cliptype)
|
|
{
|
|
if (sectnum < 0)
|
|
{
|
|
*ceilz = INT32_MIN; *ceilhit = -1;
|
|
*florz = INT32_MAX; *florhit = -1;
|
|
return;
|
|
}
|
|
|
|
int32_t clipsectcnt = 0;
|
|
|
|
#ifdef YAX_ENABLE
|
|
// YAX round, -1:center, 0:ceiling, 1:floor
|
|
int32_t mcf=-1;
|
|
#endif
|
|
|
|
uspriteptr_t curspr=NULL; // non-NULL when handling sprite with sector-like clipping
|
|
int32_t curidx=-1, clipspritecnt = 0;
|
|
|
|
//Extra walldist for sprites on sector lines
|
|
const int32_t extradist = walldist+MAXCLIPDIST+1;
|
|
const int32_t xmin = pos->x-extradist, ymin = pos->y-extradist;
|
|
const int32_t xmax = pos->x+extradist, ymax = pos->y+extradist;
|
|
|
|
const int32_t dawalclipmask = (cliptype&65535);
|
|
const int32_t dasprclipmask = (cliptype>>16);
|
|
|
|
vec2_t closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, sectnum, &closest);
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
{
|
|
*ceilz = getceilzofslope_old(sectnum,closest.x,closest.y);
|
|
*florz = getflorzofslope_old(sectnum,closest.x,closest.y);
|
|
}
|
|
else
|
|
getzsofslope(sectnum,closest.x,closest.y,ceilz,florz);
|
|
*ceilhit = sectnum+16384; *florhit = sectnum+16384;
|
|
|
|
#ifdef YAX_ENABLE
|
|
origclipsectorlist[0] = sectnum;
|
|
origclipsectnum = 1;
|
|
#endif
|
|
clipsectorlist[0] = sectnum;
|
|
clipsectnum = 1;
|
|
clipspritenum = 0;
|
|
Bmemset(clipsectormap, 0, (numsectors+7)>>3);
|
|
bitmap_set(clipsectormap, sectnum);
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (0)
|
|
{
|
|
beginagain:
|
|
// replace sector and wall with clip map
|
|
engineSetClipMap(&origmapinfo, &clipmapinfo);
|
|
clipsectcnt = clipsectnum; // should be a nop, "safety"...
|
|
}
|
|
#endif
|
|
|
|
#ifdef YAX_ENABLE
|
|
restart_grand:
|
|
#endif
|
|
do //Collect sectors inside your square first
|
|
{
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (clipsectcnt>=clipsectnum)
|
|
{
|
|
// one set of clip-sprite sectors completed, prepare the next
|
|
|
|
curspr = (uspriteptr_t)&sprite[clipspritelist[clipspritecnt]];
|
|
curidx = clipshape_idx_for_sprite(curspr, curidx);
|
|
|
|
if (curidx < 0)
|
|
{
|
|
// didn't find matching clipping sectors for sprite
|
|
clipspritecnt++;
|
|
continue;
|
|
}
|
|
|
|
clipsprite_initindex(curidx, curspr, &clipsectcnt, pos);
|
|
|
|
for (bssize_t i=0; i<clipsectnum; i++)
|
|
{
|
|
int const k = clipsectorlist[i];
|
|
|
|
if (k==sectq[clipinfo[curidx].qend])
|
|
continue;
|
|
|
|
int32_t daz, daz2;
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, k, &closest);
|
|
getzsofslope(k,closest.x,closest.y,&daz,&daz2);
|
|
|
|
int32_t fz, cz;
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, sectq[clipinfo[curidx].qend], &closest);
|
|
getzsofslope(sectq[clipinfo[curidx].qend],closest.x,closest.y,&cz,&fz);
|
|
|
|
const int hitwhat = (curspr-(uspritetype *)sprite)+49152;
|
|
|
|
if ((sector[k].ceilingstat&1)==0)
|
|
{
|
|
if (pos->z < cz && cz < *florz) { *florz = cz; *florhit = hitwhat; }
|
|
if (pos->z > daz && daz > *ceilz) { *ceilz = daz; *ceilhit = hitwhat; }
|
|
}
|
|
if ((sector[k].floorstat&1)==0)
|
|
{
|
|
if (pos->z < daz2 && daz2 < *florz) { *florz = daz2; *florhit = hitwhat; }
|
|
if (pos->z > fz && fz > *ceilz) { *ceilz = fz; *ceilhit = hitwhat; }
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
////////// Walls //////////
|
|
|
|
auto const startsec = (usectorptr_t)§or[clipsectorlist[clipsectcnt]];
|
|
const int startwall = startsec->wallptr;
|
|
const int endwall = startwall + startsec->wallnum;
|
|
|
|
for (bssize_t j=startwall; j<endwall; j++)
|
|
{
|
|
const int k = wall[j].nextsector;
|
|
|
|
if (k >= 0)
|
|
{
|
|
vec2_t const v1 = wall[j].pos;
|
|
vec2_t const v2 = wall[wall[j].point2].pos;
|
|
|
|
if ((v1.x < xmin && (v2.x < xmin)) || (v1.x > xmax && v2.x > xmax) ||
|
|
(v1.y < ymin && (v2.y < ymin)) || (v1.y > ymax && v2.y > ymax))
|
|
continue;
|
|
|
|
vec2_t const d = { v2.x-v1.x, v2.y-v1.y };
|
|
if (d.x*(pos->y-v1.y) < (pos->x-v1.x)*d.y) continue; //back
|
|
|
|
vec2_t da = { (d.x > 0) ? d.x*(ymin-v1.y) : d.x*(ymax-v1.y),
|
|
(d.y > 0) ? d.y*(xmax-v1.x) : d.y*(xmin-v1.x) };
|
|
|
|
if (da.x >= da.y)
|
|
continue;
|
|
|
|
if (wall[j].cstat&dawalclipmask) continue; // XXX?
|
|
auto const sec = (usectorptr_t)§or[k];
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
if (k==sectq[clipinfo[curidx].qend])
|
|
continue;
|
|
if ((sec->ceilingstat&1) && (sec->floorstat&1))
|
|
continue;
|
|
}
|
|
else
|
|
#endif
|
|
if (editstatus == 0)
|
|
{
|
|
if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue;
|
|
if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue;
|
|
}
|
|
|
|
if (bitmap_test(clipsectormap, k) == 0)
|
|
addclipsect(k);
|
|
|
|
if (((v1.x < xmin + MAXCLIPDIST) && (v2.x < xmin + MAXCLIPDIST)) ||
|
|
((v1.x > xmax - MAXCLIPDIST) && (v2.x > xmax - MAXCLIPDIST)) ||
|
|
((v1.y < ymin + MAXCLIPDIST) && (v2.y < ymin + MAXCLIPDIST)) ||
|
|
((v1.y > ymax - MAXCLIPDIST) && (v2.y > ymax - MAXCLIPDIST)))
|
|
continue;
|
|
|
|
if (d.x > 0) da.x += d.x*MAXCLIPDIST; else da.x -= d.x*MAXCLIPDIST;
|
|
if (d.y > 0) da.y -= d.y*MAXCLIPDIST; else da.y += d.y*MAXCLIPDIST;
|
|
if (da.x >= da.y)
|
|
continue;
|
|
#ifdef YAX_ENABLE
|
|
if (mcf==-1 && curspr==NULL)
|
|
origclipsectorlist[origclipsectnum++] = k;
|
|
#endif
|
|
//It actually got here, through all the continue's!!!
|
|
int32_t daz, daz2;
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, k, &closest);
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
{
|
|
daz = getceilzofslope_old(k, closest.x,closest.y);
|
|
daz2 = getflorzofslope_old(k, closest.x,closest.y);
|
|
}
|
|
else
|
|
getzsofslope(k, closest.x,closest.y, &daz,&daz2);
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
int32_t fz,cz, hitwhat=(curspr-(uspritetype *)sprite)+49152;
|
|
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, sectq[clipinfo[curidx].qend], &closest);
|
|
getzsofslope(sectq[clipinfo[curidx].qend],closest.x,closest.y,&cz,&fz);
|
|
|
|
if ((sec->ceilingstat&1)==0)
|
|
{
|
|
if (pos->z < cz && cz < *florz) { *florz = cz; *florhit = hitwhat; }
|
|
if (pos->z > daz && daz > *ceilz) { *ceilz = daz; *ceilhit = hitwhat; }
|
|
}
|
|
if ((sec->floorstat&1)==0)
|
|
{
|
|
if (pos->z < daz2 && daz2 < *florz) { *florz = daz2; *florhit = hitwhat; }
|
|
if (pos->z > fz && fz > *ceilz) { *ceilz = fz; *ceilhit = hitwhat; }
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef YAX_ENABLE
|
|
int16_t cb, fb;
|
|
yax_getbunches(k, &cb, &fb);
|
|
#endif
|
|
if (daz > *ceilz)
|
|
#ifdef YAX_ENABLE
|
|
if (mcf!=YAX_FLOOR && cb < 0)
|
|
#endif
|
|
*ceilz = daz, *ceilhit = k+16384;
|
|
|
|
if (daz2 < *florz)
|
|
#ifdef YAX_ENABLE
|
|
if (mcf!=YAX_CEILING && fb < 0)
|
|
#endif
|
|
*florz = daz2, *florhit = k+16384;
|
|
}
|
|
}
|
|
}
|
|
clipsectcnt++;
|
|
}
|
|
while (clipsectcnt < clipsectnum || clipspritecnt < clipspritenum);
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
engineSetClipMap(NULL, &origmapinfo); // restore original map
|
|
clipsectnum = clipspritenum = 0; // skip the next for loop and check afterwards
|
|
}
|
|
#endif
|
|
|
|
////////// Sprites //////////
|
|
|
|
if (dasprclipmask)
|
|
for (bssize_t i=0; i<clipsectnum; i++)
|
|
{
|
|
for (bssize_t j=headspritesect[clipsectorlist[i]]; j>=0; j=nextspritesect[j])
|
|
{
|
|
const int32_t cstat = sprite[j].cstat;
|
|
int32_t daz, daz2;
|
|
|
|
if (cstat&dasprclipmask)
|
|
{
|
|
int32_t clipyou = 0;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (clipsprite_try((uspriteptr_t)&sprite[j], xmin,ymin, xmax,ymax))
|
|
continue;
|
|
#endif
|
|
vec2_t v1 = sprite[j].pos.vec2;
|
|
|
|
switch (cstat & CSTAT_SPRITE_ALIGNMENT_MASK)
|
|
{
|
|
case CSTAT_SPRITE_ALIGNMENT_FACING:
|
|
{
|
|
int32_t k = walldist+(sprite[j].clipdist<<2)+1;
|
|
if ((klabs(v1.x-pos->x) <= k) && (klabs(v1.y-pos->y) <= k))
|
|
{
|
|
daz = sprite[j].z + spriteheightofs(j, &k, 1);
|
|
daz2 = daz - k;
|
|
clipyou = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_WALL:
|
|
{
|
|
vec2_t v2;
|
|
get_wallspr_points((uspriteptr_t)&sprite[j], &v1.x, &v2.x, &v1.y, &v2.y);
|
|
|
|
if (clipinsideboxline(pos->x,pos->y,v1.x,v1.y,v2.x,v2.y,walldist+1) != 0)
|
|
{
|
|
int32_t k;
|
|
daz = sprite[j].z + spriteheightofs(j, &k, 1);
|
|
daz2 = daz-k;
|
|
clipyou = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
|
|
{
|
|
daz = sprite[j].z; daz2 = daz;
|
|
|
|
if ((cstat&64) != 0 && (pos->z > daz) == ((cstat&8)==0))
|
|
continue;
|
|
|
|
vec2_t v2, v3, v4;
|
|
get_floorspr_points((uspriteptr_t) &sprite[j], pos->x, pos->y, &v1.x, &v2.x, &v3.x, &v4.x,
|
|
&v1.y, &v2.y, &v3.y, &v4.y);
|
|
|
|
vec2_t const da = { mulscale14(sintable[(sprite[j].ang - 256 + 512) & 2047], walldist + 4),
|
|
mulscale14(sintable[(sprite[j].ang - 256) & 2047], walldist + 4) };
|
|
|
|
v1.x += da.x; v2.x -= da.y; v3.x -= da.x; v4.x += da.y;
|
|
v1.y += da.y; v2.y += da.x; v3.y -= da.y; v4.y -= da.x;
|
|
|
|
clipyou = get_floorspr_clipyou(v1, v2, v3, v4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (clipyou != 0)
|
|
{
|
|
if ((pos->z > daz) && (daz > *ceilz
|
|
#ifdef YAX_ENABLE
|
|
|| (daz == *ceilz && yax_getbunch(clipsectorlist[i], YAX_CEILING)>=0)
|
|
#endif
|
|
))
|
|
{
|
|
*ceilz = daz;
|
|
*ceilhit = j+49152;
|
|
}
|
|
|
|
if ((pos->z < daz2) && (daz2 < *florz
|
|
#ifdef YAX_ENABLE
|
|
// can have a floor-sprite lying directly on the floor!
|
|
|| (daz2 == *florz && yax_getbunch(clipsectorlist[i], YAX_FLOOR)>=0)
|
|
#endif
|
|
))
|
|
{
|
|
*florz = daz2;
|
|
*florhit = j+49152;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (clipspritenum>0)
|
|
goto beginagain;
|
|
#endif
|
|
|
|
#ifdef YAX_ENABLE
|
|
if (numyaxbunches > 0)
|
|
{
|
|
int const dasecclipmask = yax_waltosecmask(dawalclipmask);
|
|
int16_t cb, fb;
|
|
|
|
yax_getbunches(sectnum, &cb, &fb);
|
|
|
|
mcf++;
|
|
clipsectcnt = 0; clipsectnum = 0;
|
|
|
|
int didchange = 0;
|
|
if (cb>=0 && mcf==0 && *ceilhit==sectnum+16384)
|
|
{
|
|
int i;
|
|
for (i=0; i<origclipsectnum; i++)
|
|
{
|
|
int const j = origclipsectorlist[i];
|
|
if (yax_getbunch(j, YAX_CEILING) >= 0)
|
|
if (sector[j].ceilingstat&dasecclipmask)
|
|
break;
|
|
}
|
|
|
|
if (i==origclipsectnum)
|
|
for (i=0; i<origclipsectnum; i++)
|
|
{
|
|
cb = yax_getbunch(origclipsectorlist[i], YAX_CEILING);
|
|
if (cb < 0)
|
|
continue;
|
|
|
|
for (bssize_t SECTORS_OF_BUNCH(cb,YAX_FLOOR, j))
|
|
if (inside(pos->x,pos->y, j)==1)
|
|
{
|
|
addclipsect(j);
|
|
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, j, &closest);
|
|
int const daz = getceilzofslope(j, closest.x, closest.y);
|
|
|
|
if (!didchange || daz > *ceilz)
|
|
didchange=1, *ceilhit = j+16384, *ceilz = daz;
|
|
}
|
|
}
|
|
|
|
if (clipsectnum==0)
|
|
mcf++;
|
|
}
|
|
else if (mcf==0)
|
|
mcf++;
|
|
|
|
didchange = 0;
|
|
if (fb>=0 && mcf==1 && *florhit==sectnum+16384)
|
|
{
|
|
int i=0;
|
|
for (; i<origclipsectnum; i++)
|
|
{
|
|
int const j = origclipsectorlist[i];
|
|
if (yax_getbunch(j, YAX_FLOOR) >= 0)
|
|
if (sector[j].floorstat&dasecclipmask)
|
|
break;
|
|
}
|
|
|
|
// (almost) same as above, but with floors...
|
|
if (i==origclipsectnum)
|
|
for (i=0; i<origclipsectnum; i++)
|
|
{
|
|
fb = yax_getbunch(origclipsectorlist[i], YAX_FLOOR);
|
|
if (fb < 0)
|
|
continue;
|
|
|
|
for (bssize_t SECTORS_OF_BUNCH(fb, YAX_CEILING, j))
|
|
if (inside(pos->x,pos->y, j)==1)
|
|
{
|
|
addclipsect(j);
|
|
|
|
closest = { pos->x, pos->y };
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
getsectordist(closest, j, &closest);
|
|
int const daz = getflorzofslope(j, closest.x,closest.y);
|
|
|
|
if (!didchange || daz < *florz)
|
|
didchange=1, *florhit = j+16384, *florz = daz;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (clipsectnum > 0)
|
|
{
|
|
// sector-like sprite re-init:
|
|
curidx = -1;
|
|
curspr = NULL;
|
|
clipspritecnt = 0; clipspritenum = 0;
|
|
|
|
goto restart_grand;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// intp: point of currently best (closest) intersection
|
|
int32_t try_facespr_intersect(uspriteptr_t const spr, vec3_t const in,
|
|
int32_t vx, int32_t vy, int32_t vz,
|
|
vec3_t * const intp, int32_t strictly_smaller_than_p)
|
|
{
|
|
vec3_t const sprpos = spr->pos;
|
|
|
|
int32_t const topt = vx * (sprpos.x - in.x) + vy * (sprpos.y - in.y);
|
|
|
|
if (topt <= 0) return 0;
|
|
|
|
int32_t const bot = vx * vx + vy * vy;
|
|
|
|
if (!bot) return 0;
|
|
|
|
vec3_t newpos = { 0, 0, in.z + scale(vz, topt, bot) };
|
|
int32_t siz;
|
|
int32_t const z1 = sprpos.z + spriteheightofsptr(spr, &siz, 1);
|
|
|
|
if (newpos.z < z1 - siz || newpos.z > z1)
|
|
return 0;
|
|
|
|
int32_t const topu = vx * (sprpos.y - in.y) - vy * (sprpos.x - in.x);
|
|
vec2_t const off = { scale(vx, topu, bot), scale(vy, topu, bot) };
|
|
int32_t const dist = off.x * off.x + off.y * off.y;
|
|
|
|
siz = tilesiz[spr->picnum].x * spr->xrepeat;
|
|
|
|
if (dist > mulscale7(siz, siz)) return 0;
|
|
|
|
newpos.vec2 = { in.x + scale(vx, topt, bot), in.y + scale(vy, topt, bot) };
|
|
|
|
if (klabs(newpos.x - in.x) + klabs(newpos.y - in.y) + strictly_smaller_than_p >
|
|
klabs(intp->x - in.x) + klabs(intp->y - in.y))
|
|
return 0;
|
|
|
|
*intp = newpos;
|
|
return 1;
|
|
}
|
|
|
|
static inline void hit_set(hitdata_t *hit, int32_t sectnum, int32_t wallnum, int32_t spritenum,
|
|
int32_t x, int32_t y, int32_t z)
|
|
{
|
|
hit->sect = sectnum;
|
|
hit->wall = wallnum;
|
|
hit->sprite = spritenum;
|
|
hit->pos.x = x;
|
|
hit->pos.y = y;
|
|
hit->pos.z = z;
|
|
}
|
|
|
|
static int32_t hitscan_hitsectcf=-1;
|
|
|
|
// stat, heinum, z: either ceiling- or floor-
|
|
// how: -1: behave like ceiling, 1: behave like floor
|
|
static int32_t hitscan_trysector(const vec3_t *sv, usectorptr_t sec, hitdata_t *hit,
|
|
int32_t vx, int32_t vy, int32_t vz,
|
|
uint16_t stat, int16_t heinum, int32_t z, int32_t how, const intptr_t *tmp)
|
|
{
|
|
int32_t x1 = INT32_MAX, y1, z1;
|
|
int32_t i;
|
|
|
|
if (stat&2)
|
|
{
|
|
auto const wal = (uwallptr_t)&wall[sec->wallptr];
|
|
auto const wal2 = (uwallptr_t)&wall[wal->point2];
|
|
int32_t j, dax=wal2->x-wal->x, day=wal2->y-wal->y;
|
|
|
|
i = nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue;
|
|
i = divscale15(heinum,i);
|
|
dax *= i; day *= i;
|
|
|
|
j = (vz<<8)-dmulscale15(dax,vy,-day,vx);
|
|
if (j != 0)
|
|
{
|
|
i = ((z - sv->z)<<8)+dmulscale15(dax,sv->y-wal->y,-day,sv->x-wal->x);
|
|
if (((i^j) >= 0) && ((klabs(i)>>1) < klabs(j)))
|
|
{
|
|
i = divscale30(i,j);
|
|
x1 = sv->x + mulscale30(vx,i);
|
|
y1 = sv->y + mulscale30(vy,i);
|
|
z1 = sv->z + mulscale30(vz,i);
|
|
}
|
|
}
|
|
}
|
|
else if ((how*vz > 0) && (how*sv->z <= how*z))
|
|
{
|
|
z1 = z; i = z1-sv->z;
|
|
if ((klabs(i)>>1) < vz*how)
|
|
{
|
|
i = divscale30(i,vz);
|
|
x1 = sv->x + mulscale30(vx,i);
|
|
y1 = sv->y + mulscale30(vy,i);
|
|
}
|
|
}
|
|
|
|
if ((x1 != INT32_MAX) && (klabs(x1-sv->x)+klabs(y1-sv->y) < klabs((hit->pos.x)-sv->x)+klabs((hit->pos.y)-sv->y)))
|
|
{
|
|
if (tmp==NULL)
|
|
{
|
|
if (inside(x1,y1,sec-(usectortype *)sector) == 1)
|
|
{
|
|
hit_set(hit, sec-(usectortype *)sector, -1, -1, x1, y1, z1);
|
|
hitscan_hitsectcf = (how+1)>>1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int32_t curidx=(int32_t)tmp[0];
|
|
auto const curspr=(uspritetype *)tmp[1];
|
|
const int32_t thislastsec = tmp[2];
|
|
|
|
if (!thislastsec)
|
|
{
|
|
if (inside(x1,y1,sec-(usectortype *)sector) == 1)
|
|
hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, x1, y1, z1);
|
|
}
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
else
|
|
{
|
|
for (i=clipinfo[curidx].qbeg; i<clipinfo[curidx].qend; i++)
|
|
{
|
|
if (inside(x1,y1,sectq[i]) == 1)
|
|
{
|
|
hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, x1, y1, z1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// hitscan
|
|
//
|
|
int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32_t vz,
|
|
hitdata_t *hit, uint32_t cliptype)
|
|
{
|
|
int32_t x1, y1=0, z1=0, x2, y2, intx, inty, intz;
|
|
int32_t i, k, daz;
|
|
int16_t tempshortcnt, tempshortnum;
|
|
|
|
uspriteptr_t curspr = NULL;
|
|
int32_t clipspritecnt, curidx=-1;
|
|
// tmp: { (int32_t)curidx, (spritetype *)curspr, (!=0 if outer sector) }
|
|
intptr_t tmp[3], *tmpptr=NULL;
|
|
#ifdef YAX_ENABLE
|
|
vec3_t newsv;
|
|
int32_t oldhitsect = -1, oldhitsect2 = -2;
|
|
#endif
|
|
const int32_t dawalclipmask = (cliptype&65535);
|
|
const int32_t dasprclipmask = (cliptype>>16);
|
|
|
|
hit->sect = -1; hit->wall = -1; hit->sprite = -1;
|
|
if (sectnum < 0)
|
|
return -1;
|
|
|
|
#ifdef YAX_ENABLE
|
|
restart_grand:
|
|
#endif
|
|
hit->pos.vec2 = hitscangoal;
|
|
|
|
clipsectorlist[0] = sectnum;
|
|
tempshortcnt = 0;
|
|
tempshortnum = 1;
|
|
clipspritecnt = clipspritenum = 0;
|
|
|
|
do
|
|
{
|
|
int32_t dasector, z, startwall, endwall;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (tempshortcnt >= tempshortnum)
|
|
{
|
|
// one bunch of sectors completed, prepare the next
|
|
if (!curspr)
|
|
engineSetClipMap(&origmapinfo, &clipmapinfo); // replace sector and wall with clip map
|
|
|
|
curspr = (uspriteptr_t)&sprite[clipspritelist[clipspritecnt]];
|
|
curidx = clipshape_idx_for_sprite(curspr, curidx);
|
|
|
|
if (curidx < 0)
|
|
{
|
|
clipspritecnt++;
|
|
continue;
|
|
}
|
|
|
|
tmp[0] = (intptr_t)curidx;
|
|
tmp[1] = (intptr_t)curspr;
|
|
tmpptr = tmp;
|
|
|
|
clipsprite_initindex(curidx, curspr, &i, sv); // &i is dummy
|
|
tempshortnum = (int16_t)clipsectnum;
|
|
tempshortcnt = 0;
|
|
}
|
|
#endif
|
|
dasector = clipsectorlist[tempshortcnt];
|
|
auto const sec = (usectorptr_t)§or[dasector];
|
|
|
|
i = 1;
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
{
|
|
if (dasector == sectq[clipinfo[curidx].qend])
|
|
{
|
|
i = -1;
|
|
tmp[2] = 1;
|
|
}
|
|
else tmp[2] = 0;
|
|
}
|
|
#endif
|
|
if (enginecompatibility_mode != ENGINECOMPATIBILITY_19950829)
|
|
{
|
|
if (hitscan_trysector(sv, sec, hit, vx,vy,vz, sec->ceilingstat, sec->ceilingheinum, sec->ceilingz, -i, tmpptr))
|
|
continue;
|
|
if (hitscan_trysector(sv, sec, hit, vx,vy,vz, sec->floorstat, sec->floorheinum, sec->floorz, i, tmpptr))
|
|
continue;
|
|
}
|
|
|
|
////////// Walls //////////
|
|
|
|
startwall = sec->wallptr; endwall = startwall + sec->wallnum;
|
|
for (z=startwall; z<endwall; z++)
|
|
{
|
|
auto const wal = (uwallptr_t)&wall[z];
|
|
auto const wal2 = (uwallptr_t)&wall[wal->point2];
|
|
|
|
int const nextsector = wal->nextsector;
|
|
|
|
if (curspr && nextsector<0) continue;
|
|
|
|
x1 = wal->x; y1 = wal->y; x2 = wal2->x; y2 = wal2->y;
|
|
|
|
if (compat_maybe_truncate_to_int32((coord_t)(x1-sv->x)*(y2-sv->y))
|
|
< compat_maybe_truncate_to_int32((coord_t)(x2-sv->x)*(y1-sv->y))) continue;
|
|
if (rintersect(sv->x,sv->y,sv->z, vx,vy,vz, x1,y1, x2,y2, &intx,&inty,&intz) == -1) continue;
|
|
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
{
|
|
if (vz != 0)
|
|
if ((intz <= sec->ceilingz) || (intz >= sec->floorz))
|
|
if (klabs(intx-sv->x)+klabs(inty-sv->y) < klabs(hit->pos.x-sv->x)+klabs(hit->pos.y-sv->y))
|
|
{
|
|
//x1,y1,z1 are temp variables
|
|
if (vz > 0) z1 = sec->floorz; else z1 = sec->ceilingz;
|
|
x1 = sv->x + scale(z1-sv->z,vx,vz);
|
|
y1 = sv->y + scale(z1-sv->z,vy,vz);
|
|
if (inside(x1,y1,dasector) == 1)
|
|
{
|
|
hit_set(hit, dasector, -1, -1, x1, y1, z1);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else if (klabs(intx-sv->x)+klabs(inty-sv->y) >= klabs((hit->pos.x)-sv->x)+klabs((hit->pos.y)-sv->y))
|
|
continue;
|
|
|
|
if (!curspr)
|
|
{
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
|
|
{
|
|
if ((nextsector < 0) || (wal->cstat&dawalclipmask))
|
|
{
|
|
if ((klabs(intx-sv->x)+klabs(inty-sv->y) < klabs(hit->pos.x-sv->x)+klabs(hit->pos.y-sv->y)))
|
|
hit_set(hit, dasector, z, -1, intx, inty, intz);
|
|
continue;
|
|
}
|
|
|
|
if (intz <= sector[nextsector].ceilingz || intz >= sector[nextsector].floorz)
|
|
{
|
|
if ((klabs(intx-sv->x)+klabs(inty-sv->y) < klabs(hit->pos.x-sv->x)+klabs(hit->pos.y-sv->y)))
|
|
hit_set(hit, dasector, z, -1, intx, inty, intz);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((nextsector < 0) || (wal->cstat&dawalclipmask))
|
|
{
|
|
hit_set(hit, dasector, z, -1, intx, inty, intz);
|
|
continue;
|
|
}
|
|
|
|
int32_t daz2;
|
|
getzsofslope(nextsector,intx,inty,&daz,&daz2);
|
|
if (intz <= daz || intz >= daz2)
|
|
{
|
|
hit_set(hit, dasector, z, -1, intx, inty, intz);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
else
|
|
{
|
|
if (wal->cstat&dawalclipmask)
|
|
{
|
|
hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, intx, inty, intz);
|
|
continue;
|
|
}
|
|
|
|
int32_t cz, fz, daz2;
|
|
getzsofslope(nextsector,intx,inty,&daz,&daz2);
|
|
getzsofslope(sectq[clipinfo[curidx].qend],intx,inty,&cz,&fz);
|
|
// ceil cz daz daz2 fz floor
|
|
if ((cz <= intz && intz <= daz) || (daz2 <= intz && intz <= fz))
|
|
{
|
|
hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, intx, inty, intz);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
int zz;
|
|
for (zz = tempshortnum - 1; zz >= 0; zz--)
|
|
if (clipsectorlist[zz] == nextsector) break;
|
|
if (zz < 0) clipsectorlist[tempshortnum++] = nextsector;
|
|
}
|
|
|
|
////////// Sprites //////////
|
|
|
|
if (dasprclipmask==0)
|
|
continue;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
continue;
|
|
#endif
|
|
for (z=headspritesect[dasector]; z>=0; z=nextspritesect[z])
|
|
{
|
|
auto const spr = (uspriteptr_t)&sprite[z];
|
|
uint32_t const cstat = spr->cstat;
|
|
#ifdef USE_OPENGL
|
|
if (!hitallsprites)
|
|
#endif
|
|
if ((cstat&dasprclipmask) == 0)
|
|
continue;
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
// try and see whether this sprite's picnum has sector-like clipping data
|
|
i = pictoidx[spr->picnum];
|
|
// handle sector-like floor sprites separately
|
|
while (i>=0 && (cstat&32) != (clipmapinfo.sector[sectq[clipinfo[i].qbeg]].CM_CSTAT&32))
|
|
i = clipinfo[i].next;
|
|
if (i>=0 && clipspritenum<MAXCLIPNUM)
|
|
{
|
|
clipspritelist[clipspritenum++] = z;
|
|
continue;
|
|
}
|
|
#endif
|
|
x1 = spr->x; y1 = spr->y; z1 = spr->z;
|
|
switch (cstat&CSTAT_SPRITE_ALIGNMENT)
|
|
{
|
|
case 0:
|
|
{
|
|
if (try_facespr_intersect(spr, *sv, vx, vy, vz, &hit->pos, 0))
|
|
{
|
|
hit->sect = dasector;
|
|
hit->wall = -1;
|
|
hit->sprite = z;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_WALL:
|
|
{
|
|
int32_t ucoefup16;
|
|
int32_t tilenum = spr->picnum;
|
|
|
|
get_wallspr_points(spr, &x1, &x2, &y1, &y2);
|
|
|
|
if ((cstat&64) != 0) //back side of 1-way sprite
|
|
if (compat_maybe_truncate_to_int32((coord_t)(x1-sv->x)*(y2-sv->y))
|
|
< compat_maybe_truncate_to_int32((coord_t)(x2-sv->x)*(y1-sv->y))) continue;
|
|
|
|
ucoefup16 = rintersect(sv->x,sv->y,sv->z,vx,vy,vz,x1,y1,x2,y2,&intx,&inty,&intz);
|
|
if (ucoefup16 == -1) continue;
|
|
|
|
if (klabs(intx-sv->x)+klabs(inty-sv->y) > klabs((hit->pos.x)-sv->x)+klabs((hit->pos.y)-sv->y))
|
|
continue;
|
|
|
|
daz = spr->z + spriteheightofs(z, &k, 1);
|
|
if (intz > daz-k && intz < daz)
|
|
{
|
|
if (picanm[tilenum].sf&PICANM_TEXHITSCAN_BIT)
|
|
{
|
|
tileUpdatePicnum(&tilenum, 0);
|
|
|
|
if (tileLoad(tilenum))
|
|
{
|
|
// daz-intz > 0 && daz-intz < k
|
|
int32_t xtex = mulscale16(ucoefup16, tilesiz[tilenum].x);
|
|
int32_t vcoefup16 = 65536-divscale16(daz-intz, k);
|
|
int32_t ytex = mulscale16(vcoefup16, tilesiz[tilenum].y);
|
|
|
|
auto texel = (tilePtr(tilenum) + tilesiz[tilenum].y*xtex + ytex);
|
|
if (*texel == 255)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
hit_set(hit, dasector, -1, z, intx, inty, intz);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
|
|
{
|
|
int32_t x3, y3, x4, y4, zz;
|
|
intz = z1;
|
|
|
|
if (vz == 0 || ((intz-sv->z)^vz) < 0) continue;
|
|
|
|
if ((cstat&64) != 0)
|
|
if ((sv->z > intz) == ((cstat&8)==0)) continue;
|
|
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
|
|
{
|
|
// Abyss crash prevention code ((intz-sv->z)*zx overflowing a 8-bit word)
|
|
// PK: the reason for the crash is not the overflowing (even if it IS a problem;
|
|
// signed overflow is undefined behavior in C), but rather the idiv trap when
|
|
// the resulting quotient doesn't fit into a *signed* 32-bit integer.
|
|
zz = (uint32_t)(intz-sv->z) * vx;
|
|
intx = sv->x+scale(zz,1,vz);
|
|
zz = (uint32_t)(intz-sv->z) * vy;
|
|
inty = sv->y+scale(zz,1,vz);
|
|
}
|
|
else
|
|
{
|
|
intx = sv->x+scale(intz-sv->z,vx,vz);
|
|
inty = sv->y+scale(intz-sv->z,vy,vz);
|
|
}
|
|
|
|
if (klabs(intx-sv->x)+klabs(inty-sv->y) > klabs((hit->pos.x)-sv->x)+klabs((hit->pos.y)-sv->y))
|
|
continue;
|
|
|
|
get_floorspr_points((uspriteptr_t)spr, intx, inty, &x1, &x2, &x3, &x4,
|
|
&y1, &y2, &y3, &y4);
|
|
|
|
if (get_floorspr_clipyou({x1, y1}, {x2, y2}, {x3, y3}, {x4, y4}))
|
|
hit_set(hit, dasector, -1, z, intx, inty, intz);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (++tempshortcnt < tempshortnum || clipspritecnt < clipspritenum);
|
|
|
|
#ifdef HAVE_CLIPSHAPE_FEATURE
|
|
if (curspr)
|
|
engineSetClipMap(NULL, &origmapinfo);
|
|
#endif
|
|
|
|
#ifdef YAX_ENABLE
|
|
if (numyaxbunches == 0 || editstatus)
|
|
return 0;
|
|
|
|
if (hit->sprite==-1 && hit->wall==-1 && hit->sect!=oldhitsect
|
|
&& hit->sect != oldhitsect2) // 'ping-pong' infloop protection
|
|
{
|
|
if (hit->sect == -1 && oldhitsect >= 0)
|
|
{
|
|
// this is bad: we didn't hit anything after going through a ceiling/floor
|
|
Bmemcpy(&hit->pos, &newsv, sizeof(vec3_t));
|
|
hit->sect = oldhitsect;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 1st, 2nd, ... ceil/floor hit
|
|
// hit->sect is >=0 because if oldhitsect's init and check above
|
|
if (SECTORFLD(hit->sect,stat, hitscan_hitsectcf)&yax_waltosecmask(dawalclipmask))
|
|
return 0;
|
|
|
|
i = yax_getneighborsect(hit->pos.x, hit->pos.y, hit->sect, hitscan_hitsectcf);
|
|
if (i >= 0)
|
|
{
|
|
Bmemcpy(&newsv, &hit->pos, sizeof(vec3_t));
|
|
sectnum = i;
|
|
sv = &newsv;
|
|
|
|
oldhitsect2 = oldhitsect;
|
|
oldhitsect = hit->sect;
|
|
hit->sect = -1;
|
|
|
|
// sector-like sprite re-init:
|
|
curspr = 0;
|
|
curidx = -1;
|
|
tmpptr = NULL;
|
|
|
|
goto restart_grand;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|