raze/source/build/src/clip.cpp

857 lines
31 KiB
C++
Raw Normal View History

// "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 "build.h"
#include "clip.h"
#include "printf.h"
#include "gamefuncs.h"
#include "coreactor.h"
enum { MAXCLIPDIST = 1024 };
2020-04-11 22:04:02 +00:00
static int clipnum;
static linetype clipit[MAXCLIPNUM];
static int32_t clipsectnum, origclipsectnum, clipspritenum;
int clipsectorlist[MAXCLIPSECTORS];
static int origclipsectorlist[MAXCLIPSECTORS];
static CollisionBase clipobjectval[MAXCLIPNUM];
static uint8_t clipignore[(MAXCLIPNUM+7)>>3];
static int32_t rxi[8], ryi[8];
BitArray clipsectormap;
int32_t quickloadboard=0;
////////// CLIPMOVE //////////
inline uint8_t bitmap_test(uint8_t const* const ptr, int const n) { return ptr[n >> 3] & (1 << (n & 7)); }
// x1, y1: in/out
// rest x/y: out
static inline void get_wallspr_points(DCoreActor* actor, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2)
{
DVector2 out[2];
GetWallSpritePosition(&actor->spr, DVector2(*x1 * inttoworld, *y1 * inttoworld), out);
*x1 = int(out[0].X * worldtoint); *y1 = int(out[0].Y * worldtoint);
*x2 = int(out[1].X * worldtoint); *y2 = int(out[1].Y * worldtoint);
}
// x1, y1: in/out
// rest x/y: out
static inline void get_floorspr_points(DCoreActor *spr, int32_t px, int32_t py,
int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4,
int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4)
{
DVector2 out[4];
// very messed up interface... :(
GetFlatSpritePosition(spr, DVector2((*x1 - px) * inttoworld, (*y1 - py) * inttoworld), out);
*x1 = int(out[0].X * worldtoint); *y1 = int(out[0].Y * worldtoint);
*x2 = int(out[1].X * worldtoint); *y2 = int(out[1].Y * worldtoint);
*x3 = int(out[2].X * worldtoint); *y3 = int(out[2].Y * worldtoint);
*x4 = int(out[3].X * worldtoint); *y4 = int(out[3].Y * worldtoint);
}
inline int32_t spriteGetZOfSlope(const spritetypebase* tspr, int dax, int day, int heinum)
{
return spriteGetZOfSlopef(tspr, DVector2(dax * inttoworld, day * inttoworld), heinum) * zworldtoint;
}
//
// clipinsideboxline
//
static int clipinsideboxline(int x, int y, int x1, int y1, int x2, int y2, int walldist)
{
return (int)IsCloseToLine(DVector2(x * inttoworld, y * inttoworld), DVector2(x1 * inttoworld, y1 * inttoworld), DVector2(x2 * inttoworld, y2 * inttoworld), walldist * inttoworld);
}
static int32_t clipmove_warned;
static inline void addclipsect(int const sectnum)
{
if (clipsectnum < MAXCLIPSECTORS)
{
clipsectormap.Set(sectnum);
clipsectorlist[clipsectnum++] = sectnum;
}
else
clipmove_warned |= 1;
}
static void addclipline(int32_t dax1, int32_t day1, int32_t dax2, int32_t day2, const CollisionBase& 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;
2021-03-24 19:37:20 +00:00
uint32_t const mask = (1 << (clipnum&7));
uint8_t &value = clipignore[clipnum>>3];
value = (value & ~mask) | (-nofix & mask);
clipnum++;
}
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 ||
2021-12-22 09:40:26 +00:00
rintersect(pos->X, pos->Y, 0, gx, gy, 0, x1, y1, x2, y2, daxptr, dayptr, &daz) == -1)
{
2021-12-22 09:36:09 +00:00
*daxptr = pos->X;
2021-12-22 09:40:26 +00:00
*dayptr = pos->Y;
}
}
// 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)
{
assert(validSectorIndex(dasect) && validSectorIndex(nextsect));
2021-12-30 10:45:58 +00:00
auto const sec2 = &sector[nextsect];
switch (enginecompatibility_mode)
{
case ENGINECOMPATIBILITY_NONE:
break;
default:
{
int32_t daz = int_getflorzofslopeptr(&sector[dasect], pos.X, pos.Y);
int32_t daz2 = int_getflorzofslopeptr(sec2, pos.X, pos.Y);
if (daz2 < daz-(1<<8) && (sec2->floorstat & CSTAT_SECTOR_SKY) == 0)
if (posz >= daz2-(flordist-1)) return 1;
daz = int_getceilzofslopeptr(&sector[dasect], pos.X, pos.Y);
daz2 = int_getceilzofslopeptr(sec2, pos.X, pos.Y);
if (daz2 > daz+(1<<8) && (sec2->ceilingstat & CSTAT_SECTOR_SKY) == 0)
if (posz <= daz2+(ceildist-1)) return 1;
return 0;
}
}
double daz2 = sec2->floorz;
double dacz2 = sec2->ceilingz;
if ((sec2->floorstat|sec2->ceilingstat) & CSTAT_SECTOR_SLOPE)
2021-12-22 09:28:51 +00:00
getcorrectzsofslope(nextsect, pos.X, pos.Y, &dacz2, &daz2);
if (daz2 <= dacz2)
return 1;
2021-12-30 10:45:58 +00:00
auto const sec = &sector[dasect];
double daz = sec->floorz;
double dacz = sec->ceilingz;
if ((sec->floorstat|sec->ceilingstat) & CSTAT_SECTOR_SLOPE)
2021-12-22 09:28:51 +00:00
getcorrectzsofslope(dasect, pos.X, pos.Y, &dacz, &daz);
double const sec2height = abs(daz2-dacz2);
double fposz = posz * zinttoworld;
double fceildist = ceildist * zinttoworld;
double fflordist = flordist * zinttoworld;
return ((abs(daz-dacz) > sec2height && // clip if the current sector is taller and the next is too small
sec2height < (fceildist + (CLIPCURBHEIGHT * 2))) ||
((sec2->floorstat & CSTAT_SECTOR_SKY) == 0 && // parallaxed floor curbs don't clip
fposz >= daz2-(fflordist-1) && // also account for desired z distance tolerance
daz2 < daz-CLIPCURBHEIGHT) || // curbs less tall than 256 z units don't clip
((sec2->ceilingstat & CSTAT_SECTOR_SKY) == 0 &&
fposz <= dacz2+(fceildist-1) &&
dacz2 > dacz+CLIPCURBHEIGHT)); // ceilings check the same conditions ^^^^^
}
//
// raytrace (internal)
//
static inline int32_t cliptrace(vec2_t const pos, vec2_t * const goal)
{
int32_t hitwall = -1;
2021-05-11 23:29:18 +00:00
for (int 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 };
2021-12-22 09:28:51 +00:00
vec2_t const area = { p2.X-p1.X, p2.Y-p1.Y };
2021-12-22 09:28:51 +00:00
int32_t topu = area.X*(pos.Y-p1.Y) - (pos.X-p1.X)*area.Y;
2021-12-22 09:28:51 +00:00
if (topu <= 0 || area.X*(goal->Y-p1.Y) > (goal->X-p1.X)*area.Y)
continue;
2021-12-22 09:28:51 +00:00
vec2_t const diff = { goal->X-pos.X, goal->Y-pos.Y };
2021-12-22 09:28:51 +00:00
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;
2021-12-22 09:28:51 +00:00
int32_t const bot = diff.X*area.Y - area.X*diff.Y;
int cnt = 256;
if (!bot)
continue;
vec2_t n;
do
{
if (--cnt < 0)
{
*goal = pos;
return z;
}
2021-12-22 09:28:51 +00:00
n = { pos.X+Scale(diff.X, topu, bot), pos.Y+Scale(diff.Y, topu, bot) };
topu--;
2021-12-22 09:28:51 +00:00
} while (area.X*(n.Y-p1.Y) <= (n.X-p1.X)*area.Y);
2021-12-22 09:28:51 +00:00
if (abs(pos.X-n.X)+abs(pos.Y-n.Y) < abs(pos.X-goal->X)+abs(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 = Sgn(-dy), oy = Sgn(dx);
uint8_t first = (abs(dx) <= abs(dy));
do
{
if (dx*(*y-y1) > (*x-x1)*dy)
return;
if (first == 0)
*x += ox;
else
*y += oy;
first ^= 1;
}
while (1);
}
static int32_t getwalldist(vec2_t const in, int const wallnum)
{
auto dvec = NearestPointOnWall(in.X * maptoworld, in.Y * maptoworld, &wall[wallnum]);
vec2_t closest = { int(dvec.X * worldtoint), int(dvec.Y * worldtoint) };
return abs(closest.X - in.X) + abs(closest.Y - in.Y);
}
static void clipupdatesector(vec2_t const pos, int * const sectnum, int walldist)
{
2021-12-22 09:28:51 +00:00
if (inside_p(pos.X, pos.Y, *sectnum))
return;
double nsecs = SquareDistToSector(pos.X * inttoworld, pos.Y * inttoworld, &sector[*sectnum]);
double wd = (walldist + 8) * inttoworld; wd *= wd;
if (nsecs > wd)
{
walldist = 0x7fff;
}
{
2021-12-21 09:51:41 +00:00
BFSSearch search(sector.Size(), *sectnum);
2021-11-16 23:13:34 +00:00
for (unsigned listsectnum; (listsectnum = search.GetNext()) != BFSSearch::EOL;)
{
2021-12-22 09:28:51 +00:00
if (inside_p(pos.X, pos.Y, listsectnum))
2021-11-16 23:13:34 +00:00
{
*sectnum = listsectnum;
return;
}
2021-11-16 23:13:34 +00:00
for (auto& wal : wallsofsector(listsectnum))
{
if (wal.nextsector >= 0 && clipsectormap[wal.nextsector])
2021-11-16 23:13:34 +00:00
search.Add(wal.nextsector);
}
}
}
{
2021-12-21 09:51:41 +00:00
BFSSearch search(sector.Size(), *sectnum);
2021-11-16 23:13:34 +00:00
for (unsigned listsectnum; (listsectnum = search.GetNext()) != BFSSearch::EOL;)
{
2021-12-22 09:28:51 +00:00
if (inside_p(pos.X, pos.Y, listsectnum))
2021-11-16 23:13:34 +00:00
{
*sectnum = listsectnum;
return;
}
for (auto& wal : wallsofsector(listsectnum))
{
if (wal.nextsector >= 0 && getwalldist(pos, wallnum(&wal)) <= (walldist + 8))
search.Add(wal.nextsector);
}
}
}
*sectnum = -1;
}
//
// clipmove
//
CollisionBase clipmove_(vec3_t * const pos, int * const sectnum, int32_t xvect, int32_t yvect,
int32_t const walldist, int32_t const ceildist, int32_t const flordist, uint32_t const cliptype, int clipmoveboxtracenum)
{
CollisionBase b{};
if ((xvect|yvect) == 0 || *sectnum < 0)
return b;
DCoreActor* curspr=NULL; // non-NULL when handling sprite with sector-like clipping
int const initialsectnum = *sectnum;
2021-12-18 14:36:50 +00:00
int32_t const dawalclipmask = (cliptype & 65535); // CLIPMASK0 = 0x00010001 (in desperate need of getting fixed!)
int32_t const dasprclipmask = (cliptype >> 16); // CLIPMASK1 = 0x01000040
vec2_t const move = { xvect, yvect };
2021-12-22 09:40:26 +00:00
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
2021-12-22 09:40:26 +00:00
vec2_t const diff = { goal.X - (pos->X), goal.Y - (pos->Y) };
2021-12-22 09:28:51 +00:00
int32_t const rad = ksqrt(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 clipsectcnt = 0;
int clipspritecnt = 0;
clipsectorlist[0] = *sectnum;
clipsectnum = 1;
clipnum = 0;
clipspritenum = 0;
clipmove_warned = 0;
clipsectormap.Zero();
clipsectormap.Set(*sectnum);
do
{
int const dasect = clipsectorlist[clipsectcnt++];
//if (curspr)
// Printf("sprite %d/%d: sect %d/%d (%d)\n", clipspritecnt,clipspritenum, clipsectcnt,clipsectnum,dasect);
////////// Walls //////////
2021-12-30 10:45:58 +00:00
auto const sec = &sector[dasect];
int const startwall = sec->wallptr;
int const endwall = startwall + sec->wallnum;
2021-12-30 10:38:34 +00:00
auto wal = &wall[startwall];
for (int j=startwall; j<endwall; j++, wal++)
{
2021-12-30 10:38:34 +00:00
auto const wal2 = wal->point2Wall();
2022-09-16 17:11:57 +00:00
vec2_t p1 = wal->wall_int_pos();
vec2_t p2 = wal2->wall_int_pos();
2022-09-16 17:11:57 +00:00
if ((p1.X < clipMin.X && p2.X < clipMin.X) || (p1.X > clipMax.X && p2.X > clipMax.X) ||
(p1.Y < clipMin.Y && p2.Y < clipMin.Y) || (p1.Y > clipMax.Y && p2.Y > clipMax.Y))
continue;
2021-12-22 09:28:51 +00:00
vec2_t d = { p2.X-p1.X, p2.Y-p1.Y };
2021-12-22 09:40:26 +00:00
if (d.X * (pos->Y-p1.Y) < (pos->X-p1.X) * d.Y)
continue; //If wall's not facing you
2021-12-22 09:28:51 +00:00
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) };
2021-12-22 09:28:51 +00:00
if (v.X >= v.Y)
continue;
int clipyou = 0;
2021-12-18 14:36:50 +00:00
if (wal->nextsector < 0 || (wal->cstat & EWallFlags::FromInt(dawalclipmask)))
{
clipyou = 1;
}
else
{
2021-12-22 09:28:51 +00:00
clipmove_tweak_pos(pos, diff.X, diff.Y, p1.X, p1.Y, p2.X, p2.Y, &v.X, &v.Y);
2021-12-22 09:41:47 +00:00
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
2021-12-22 09:40:26 +00:00
&& inside(pos->X, pos->Y, sec) == 1)
{
int k;
for (k=startwall; k<endwall; k++)
if (wall[k].nextsector == initialsectnum)
break;
if (k == endwall)
break;
}
if (clipyou)
{
CollisionBase objtype;
if (curspr) objtype.setSprite(curspr);
else objtype.setWall(j);
//Add 2 boxes at endpoints
2021-12-22 09:26:51 +00:00
int32_t bsz = walldist; if (diff.X < 0) bsz = -bsz;
2021-12-22 09:28:51 +00:00
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);
2021-12-22 09:28:51 +00:00
v.X = walldist; if (d.Y > 0) v.X = -v.X;
v.Y = walldist; if (d.X < 0) v.Y = -v.Y;
2021-12-22 09:40:26 +00:00
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE && d.X * (pos->Y-p1.Y-v.Y) < (pos->X-p1.X-v.X) * d.Y)
2021-12-22 09:28:51 +00:00
v.X >>= 1, v.Y >>= 1;
2021-12-22 09:28:51 +00:00
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 (!clipsectormap[wal->nextsector])
addclipsect(wal->nextsector);
}
}
if (clipmove_warned & 1)
Printf("clipsectnum >= MAXCLIPSECTORS!\n");
if (clipmove_warned & 2)
Printf("clipnum >= MAXCLIPNUM!\n");
////////// Sprites //////////
if (dasprclipmask==0)
continue;
TSectIterator<DCoreActor> it(dasect);
while (auto actor = it.Next())
{
const int32_t cstat = actor->spr.cstat;
if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND) continue;
if ((cstat&dasprclipmask) == 0)
continue;
auto p1 = actor->int_pos().vec2;
CollisionBase obj;
obj.setSprite(actor);
switch (cstat & (CSTAT_SPRITE_ALIGNMENT_MASK))
{
case CSTAT_SPRITE_ALIGNMENT_FACING:
2021-12-22 09:28:51 +00:00
if (p1.X >= clipMin.X && p1.X <= clipMax.X && p1.Y >= clipMin.Y && p1.Y <= clipMax.Y)
{
int32_t height, daz = actor->int_pos().Z + actor->GetOffsetAndHeight(height);
2021-12-22 09:41:47 +00:00
if (pos->Z > daz-height-flordist && pos->Z < daz+ceildist)
{
2022-09-09 16:07:44 +00:00
int32_t bsz = (actor->int_clipdist())+walldist;
2021-12-22 09:26:51 +00:00
if (diff.X < 0) bsz = -bsz;
2021-12-22 09:28:51 +00:00
addclipline(p1.X-bsz, p1.Y-bsz, p1.X-bsz, p1.Y+bsz, obj, false);
2022-09-09 16:07:44 +00:00
bsz = (actor->int_clipdist())+walldist;
2021-12-22 09:28:51 +00:00
if (diff.Y < 0) bsz = -bsz;
addclipline(p1.X+bsz, p1.Y-bsz, p1.X-bsz, p1.Y-bsz, obj, false);
}
}
break;
case CSTAT_SPRITE_ALIGNMENT_WALL:
{
int32_t height, daz = actor->int_pos().Z + actor->GetOffsetAndHeight(height);
2021-12-22 09:41:47 +00:00
if (pos->Z > daz-height-flordist && pos->Z < daz+ceildist)
{
vec2_t p2;
get_wallspr_points(actor, &p1.X, &p2.X, &p1.Y, &p2.Y);
2021-12-22 09:28:51 +00:00
if (clipinsideboxline(cent.X, cent.Y, p1.X, p1.Y, p2.X, p2.Y, rad) != 0)
{
2022-08-16 21:15:49 +00:00
vec2_t v = { MulScale(bcos(actor->int_ang() + 256), walldist, 14),
MulScale(bsin(actor->int_ang() + 256), walldist, 14) };
2021-12-22 09:40:26 +00:00
if ((p1.X-pos->X) * (p2.Y-pos->Y) >= (p2.X-pos->X) * (p1.Y-pos->Y)) // Front
2021-12-22 09:28:51 +00:00
addclipline(p1.X+v.X, p1.Y+v.Y, p2.X+v.Y, p2.Y-v.X, obj, false);
else
{
if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0)
continue;
2021-12-22 09:28:51 +00:00
addclipline(p2.X-v.X, p2.Y-v.Y, p1.X-v.Y, p1.Y+v.X, obj, false);
}
//Side blocker
2021-12-22 09:40:26 +00:00
if ((p2.X-p1.X) * (pos->X-p1.X)+(p2.Y-p1.Y) * (pos->Y-p1.Y) < 0)
2021-12-22 09:28:51 +00:00
addclipline(p1.X-v.Y, p1.Y+v.X, p1.X+v.X, p1.Y+v.Y, obj, true);
2021-12-22 09:40:26 +00:00
else if ((p1.X-p2.X) * (pos->X-p2.X)+(p1.Y-p2.Y) * (pos->Y-p2.Y) < 0)
2021-12-22 09:28:51 +00:00
addclipline(p2.X+v.Y, p2.Y-v.X, p2.X-v.X, p2.Y-v.Y, obj, true);
}
}
break;
}
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
case CSTAT_SPRITE_ALIGNMENT_SLOPE:
{
int heinum = spriteGetSlope(actor);
int sz = spriteGetZOfSlope(&actor->spr, pos->X, pos->Y, heinum);
2021-12-22 09:41:47 +00:00
if (pos->Z > sz - flordist && pos->Z < sz + ceildist)
{
if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0)
2021-12-22 09:41:47 +00:00
if ((pos->Z > sz) == ((cstat & CSTAT_SPRITE_YFLIP)==0))
continue;
2021-12-22 09:26:51 +00:00
rxi[0] = p1.X;
2021-12-22 09:28:51 +00:00
ryi[0] = p1.Y;
get_floorspr_points(actor, 0, 0, &rxi[0], &rxi[1], &rxi[2], &rxi[3],
&ryi[0], &ryi[1], &ryi[2], &ryi[3]);
2022-08-16 21:15:49 +00:00
vec2_t v = { MulScale(bcos(actor->int_ang() - 256), walldist, 14),
MulScale(bsin(actor->int_ang() - 256), walldist, 14) };
2021-12-22 09:40:26 +00:00
if ((rxi[0]-pos->X) * (ryi[1]-pos->Y) < (rxi[1]-pos->X) * (ryi[0]-pos->Y))
{
2021-12-22 09:28:51 +00:00
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, obj, false);
}
2021-12-22 09:40:26 +00:00
else if ((rxi[2]-pos->X) * (ryi[3]-pos->Y) < (rxi[3]-pos->X) * (ryi[2]-pos->Y))
{
2021-12-22 09:28:51 +00:00
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, obj, false);
}
2021-12-22 09:40:26 +00:00
if ((rxi[1]-pos->X) * (ryi[2]-pos->Y) < (rxi[2]-pos->X) * (ryi[1]-pos->Y))
{
2021-12-22 09:28:51 +00:00
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, obj, false);
}
2021-12-22 09:40:26 +00:00
else if ((rxi[3]-pos->X) * (ryi[0]-pos->Y) < (rxi[0]-pos->X) * (ryi[3]-pos->Y))
{
2021-12-22 09:28:51 +00:00
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, obj, false);
}
}
if (heinum == 0)
continue;
// the rest is for slope sprites only.
const int32_t tilenum = actor->spr.picnum;
2022-08-16 21:15:49 +00:00
const int32_t cosang = bcos(actor->int_ang());
const int32_t sinang = bsin(actor->int_ang());
vec2_t const span = { tileWidth(tilenum), tileHeight(tilenum) };
vec2_t const repeat = { actor->spr.xrepeat, actor->spr.yrepeat };
vec2_t adjofs = { tileLeftOffset(tilenum), tileTopOffset(tilenum) };
if (actor->spr.cstat & CSTAT_SPRITE_XFLIP)
2021-12-22 09:26:51 +00:00
adjofs.X = -adjofs.X;
if (actor->spr.cstat & CSTAT_SPRITE_YFLIP)
2021-12-22 09:28:51 +00:00
adjofs.Y = -adjofs.Y;
2021-12-22 09:26:51 +00:00
int32_t const centerx = ((span.X >> 1) + adjofs.X) * repeat.X;
2021-12-22 09:28:51 +00:00
int32_t const centery = ((span.Y >> 1) + adjofs.Y) * repeat.Y;
2021-12-22 09:26:51 +00:00
int32_t const rspanx = span.X * repeat.X;
2021-12-22 09:28:51 +00:00
int32_t const rspany = span.Y * repeat.Y;
int32_t const ratio = ksqrt(heinum * heinum + SLOPEVAL_FACTOR * SLOPEVAL_FACTOR);
2021-12-22 09:41:47 +00:00
int32_t zz[3] = { pos->Z, pos->Z + flordist, pos->Z - ceildist };
for (int k = 0; k < 3; k++)
{
int32_t jj = DivScale(actor->int_pos().Z - zz[k], heinum, 18);
int32_t jj2 = MulScale(jj, ratio, 12);
if (jj2 > (centery << 8) || jj2 < ((centery - rspany) << 8))
continue;
int32_t x1 = actor->int_pos().X + MulScale(sinang, centerx, 16) + MulScale(jj, cosang, 24);
int32_t y1 = actor->int_pos().Y - MulScale(cosang, centerx, 16) + MulScale(jj, sinang, 24);
int32_t x2 = x1 - MulScale(sinang, rspanx, 16);
int32_t y2 = y1 + MulScale(cosang, rspanx, 16);
2022-08-16 21:15:49 +00:00
vec2_t const v = { MulScale(bcos(actor->int_ang() - 256), walldist, 14),
MulScale(bsin(actor->int_ang() - 256), walldist, 14) };
2021-12-22 09:28:51 +00:00
if (clipinsideboxline(cent.X, cent.Y, x1, y1, x2, y2, rad) != 0)
{
2021-12-22 09:40:26 +00:00
if ((x1 - pos->X) * (y2 - pos->Y) >= (x2 - pos->X) * (y1 - pos->Y))
{
2021-12-22 09:28:51 +00:00
addclipline(x1 + v.X, y1 + v.Y, x2 + v.Y, y2 - v.X, obj, false);
}
else
{
if ((cstat & CSTAT_SPRITE_ONE_SIDE) != 0)
continue;
2021-12-22 09:28:51 +00:00
addclipline(x2 - v.X, y2 - v.Y, x1 - v.Y, y1 + v.X, obj, false);
}
}
}
break;
}
}
}
} while (clipsectcnt < clipsectnum || clipspritecnt < clipspritenum);
int32_t hitwalls[4], hitwall;
CollisionBase clipReturn{};
2021-05-11 23:29:18 +00:00
int cnt = clipmoveboxtracenum;
do
{
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE && (xvect|yvect))
{
2021-05-11 23:29:18 +00:00
for (int i=clipnum-1;i>=0;--i)
{
2021-12-22 09:40:26 +00:00
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;
2021-12-22 09:40:26 +00:00
keepaway(&pos->X, &pos->Y, i);
if (inside_p(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 };
// clamp to the max value we can utilize without reworking the scaling below
// this works around the overflow issue that affects dukedc2.map
2021-12-22 09:28:51 +00:00
int32_t const templl = (int32_t)clamp<int64_t>(compat_maybe_truncate_to_int32((int64_t)clipr.X * clipr.X + (int64_t)clipr.Y * clipr.Y), INT32_MIN, INT32_MAX);
2020-02-11 17:26:58 +00:00
if (templl > 0)
{
// I don't know if this one actually overflows or not, but I highly doubt it hurts to check
int32_t const templl2
2021-12-22 09:28:51 +00:00
= (int32_t)clamp<int64_t>(compat_maybe_truncate_to_int32((int64_t)(goal.X - vec.X) * clipr.X + (int64_t)(goal.Y - vec.Y) * clipr.Y), INT32_MIN, INT32_MAX);
int32_t const i = (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || (abs(templl2)>>11) < templl) ?
2021-05-11 23:29:18 +00:00
(int)DivScaleL(templl2, templl, 20) : 0;
2021-12-22 09:28:51 +00:00
goal = { MulScale(clipr.X, i, 20)+vec.X, MulScale(clipr.Y, i, 20)+vec.Y };
}
int32_t tempint;
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
2021-12-22 09:28:51 +00:00
tempint = clipr.X*(move.X>>6)+clipr.Y*(move.Y>>6);
else
2021-12-22 09:28:51 +00:00
tempint = DMulScale(clipr.X, move.X, clipr.Y, move.Y, 6);
for (int i=cnt+1, j; i<=clipmoveboxtracenum; ++i)
{
j = hitwalls[i];
int32_t tempint2;
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
2021-12-22 09:28:51 +00:00
tempint2 = (clipit[j].x2-clipit[j].x1)*(move.X>>6)+(clipit[j].y2-clipit[j].y1)*(move.Y>>6);
else
2021-12-22 09:28:51 +00:00
tempint2 = DMulScale(clipit[j].x2-clipit[j].x1, move.X, clipit[j].y2-clipit[j].y1, move.Y, 6);
if ((tempint ^ tempint2) < 0)
{
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19961112)
{
auto sectp = &sector[*sectnum];
updatesector(DVector2(pos->X * inttoworld, pos->Y * inttoworld), &sectp);
*sectnum = sectp ? ::sectnum(sectp) : -1;
}
return clipReturn;
}
}
2021-12-22 09:28:51 +00:00
keepaway(&goal.X, &goal.Y, hitwall);
2021-12-22 09:26:51 +00:00
xvect = (goal.X-vec.X)<<14;
2021-12-22 09:28:51 +00:00
yvect = (goal.Y-vec.Y)<<14;
if (cnt == clipmoveboxtracenum)
clipReturn = clipobjectval[hitwall];
hitwalls[cnt] = hitwall;
}
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
{
clipupdatesector(vec, sectnum, rad);
}
2021-12-22 09:36:09 +00:00
pos->X = vec.X;
2021-12-22 09:40:26 +00:00
pos->Y = vec.Y;
cnt--;
} while ((xvect|yvect) != 0 && hitwall >= 0 && cnt > 0);
if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE)
{
for (int j=0; j<clipsectnum; j++)
2021-12-22 09:40:26 +00:00
if (inside_p(pos->X, pos->Y, clipsectorlist[j]) == 1)
{
*sectnum = clipsectorlist[j];
return clipReturn;
}
int32_t tempint2, tempint1 = INT32_MAX;
*sectnum = -1;
2021-12-21 09:51:41 +00:00
for (int j = (int)sector.Size() - 1; j >= 0; j--)
{
auto sect = &sector[j];
2021-12-22 09:40:26 +00:00
if (inside(pos->X, pos->Y, sect) == 1)
{
if (enginecompatibility_mode != ENGINECOMPATIBILITY_19950829 && (sect->ceilingstat & CSTAT_SECTOR_SLOPE))
tempint2 = int_getceilzofslopeptr(sect, pos->X, pos->Y) - pos->Z;
else
tempint2 = int(sect->ceilingz * zworldtoint) - pos->Z;
if (tempint2 > 0)
{
if (tempint2 < tempint1)
{
*sectnum = (int16_t)j;
2021-05-11 23:29:18 +00:00
tempint1 = tempint2;
}
}
else
{
if (enginecompatibility_mode != ENGINECOMPATIBILITY_19950829 && (sect->ceilingstat & CSTAT_SECTOR_SLOPE))
tempint2 = pos->Z - int_getflorzofslopeptr(sect, pos->X, pos->Y);
else
tempint2 = pos->Z - int(sect->floorz * zworldtoint);
if (tempint2 <= 0)
{
2021-05-11 23:29:18 +00:00
*sectnum = (int16_t)j;
return clipReturn;
}
if (tempint2 < tempint1)
{
2021-05-11 23:29:18 +00:00
*sectnum = (int16_t)j;
tempint1 = tempint2;
}
}
}
}
}
return clipReturn;
}
//
// pushmove
//
int pushmove_(vec3_t *const vect, int *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)
{
2020-01-26 13:26:08 +00:00
if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE && *sectnum < 0)
return 0;
clipsectorlist[0] = *sectnum;
clipsectnum = 1;
clipsectormap.Zero();
clipsectormap.Set(*sectnum);
}
do
{
2021-12-30 10:38:34 +00:00
const walltype* wal;
int32_t startwall, endwall;
2021-12-30 10:45:58 +00:00
auto sec = &sector[clipsectorlist[clipsectcnt]];
if (dir > 0)
startwall = sec->wallptr, endwall = startwall + sec->wallnum;
else
endwall = sec->wallptr, startwall = endwall + sec->wallnum - 1;
int i;
2021-12-30 10:38:34 +00:00
for (i=startwall, wal=&wall[startwall]; i!=endwall; i+=dir, wal+=dir)
if (IsCloseToWall(DVector2(vect->X * inttoworld, vect->Y * inttoworld), &wall[i], (walldist-4) * inttoworld) == EClose::InFront)
{
int j = 0;
2021-12-18 14:36:50 +00:00
if (wal->nextsector < 0 || wal->cstat & EWallFlags::FromInt(dawalclipmask)) j = 1;
else
{
auto pvect = NearestPointOnWall(vect->X * inttoworld, vect->Y * inttoworld, wal);
vec2_t closest = { int(pvect.X * worldtoint), int(pvect.Y * worldtoint) };
2021-12-22 09:41:47 +00:00
j = cliptestsector(clipsectorlist[clipsectcnt], wal->nextsector, flordist, ceildist, closest, vect->Z);
}
if (j != 0)
{
DAngle jj = wal->delta().Angle();
int32_t dx = -int(jj.Sin() * 8);
int32_t dy = int(jj.Cos() * 8);
int bad2 = 16;
do
{
2021-12-22 09:40:26 +00:00
vect->X = (vect->X) + dx; vect->Y = (vect->Y) + dy;
bad2--; if (bad2 == 0) break;
} while (IsCloseToWall(DVector2(vect->X * inttoworld, vect->Y * inttoworld), &wall[i], (walldist - 4) * inttoworld) != EClose::Outside);
bad = -1;
k--; if (k <= 0) return bad;
clipupdatesector(vect->vec2, sectnum, walldist);
if (*sectnum < 0) return -1;
}
else if (!clipsectormap[wal->nextsector])
addclipsect(wal->nextsector);
}
clipsectcnt++;
} while (clipsectcnt < clipsectnum);
dir = -dir;
} while (bad != 0);
return bad;
}