diff --git a/package/common/buildlic.txt b/package/common/buildlic.txt index 3a3985bb6..2751ea106 100644 --- a/package/common/buildlic.txt +++ b/package/common/buildlic.txt @@ -1,71 +1,71 @@ -BUILD SOURCE CODE LICENSE TERMS: 06/20/2000 - -[1] I give you permission to make modifications to my Build source and - distribute it, BUT: - -[2] Any derivative works based on my Build source may be distributed ONLY - through the INTERNET. - -[3] Distribution of any derivative works MUST be done completely FREE of - charge - no commercial exploitation whatsoever. - -[4] Anything you distribute which uses a part of my Build Engine source - code MUST include: - - [A] The following message somewhere in the archive: - - // "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. - - [B] This text file "BUILDLIC.TXT" along with it. - - [C] Any source files that you modify must include this message as well: - - // This file has been modified from Ken Silverman's original release - -[5] The use of the Build Engine for commercial purposes will require an - appropriate license arrangement with me. Contact information is - on my web site. - -[6] I take no responsibility for damage to your system. - -[7] Technical support: Before contacting me with questions, please read - and do ALL of the following! - - [A] Look through ALL of my text files. There are 7 of them (including this - one). I like to think that I wrote them for a reason. You will find - many of your answers in the history section of BUILD.TXT and - BUILD2.TXT (they're located inside SRC.ZIP). - - [B] If that doesn't satisfy you, then try going to: - - "http://www.advsys.net/ken/buildsrc" - - where I will maintain a Build Source Code FAQ (or perhaps I might - just provide a link to a good FAQ). - - [C] I am willing to respond to questions, but ONLY if they come at a rate - that I can handle. - - PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS! - - As my line of defense, I will post my current policy about - answering Build source questions (right below the E-mail address - on my web site.) You can check there to see if I'm getting - overloaded with questions or not. - - If I'm too busy, it might say something like this: - - I'm too busy to answer Build source questions right now. - Sorry, but don't expect a reply from me any time soon. - - If I'm open for Build source questions, please state your question - clearly and don't include any unsolicited attachments unless - they're really small (like less than 50k). Assume that I have - a 28.8k modem. Also, don't leave out important details just - to make your question appear shorter - making me guess what - you're asking doesn't save me time! - ----------------------------------------------------------------------------- --Ken S. (official web site: http://www.advsys.net/ken) +BUILD SOURCE CODE LICENSE TERMS: 06/20/2000 + +[1] I give you permission to make modifications to my Build source and + distribute it, BUT: + +[2] Any derivative works based on my Build source may be distributed ONLY + through the INTERNET. + +[3] Distribution of any derivative works MUST be done completely FREE of + charge - no commercial exploitation whatsoever. + +[4] Anything you distribute which uses a part of my Build Engine source + code MUST include: + + [A] The following message somewhere in the archive: + + // "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. + + [B] This text file "BUILDLIC.TXT" along with it. + + [C] Any source files that you modify must include this message as well: + + // This file has been modified from Ken Silverman's original release + +[5] The use of the Build Engine for commercial purposes will require an + appropriate license arrangement with me. Contact information is + on my web site. + +[6] I take no responsibility for damage to your system. + +[7] Technical support: Before contacting me with questions, please read + and do ALL of the following! + + [A] Look through ALL of my text files. There are 7 of them (including this + one). I like to think that I wrote them for a reason. You will find + many of your answers in the history section of BUILD.TXT and + BUILD2.TXT (they're located inside SRC.ZIP). + + [B] If that doesn't satisfy you, then try going to: + + "http://www.advsys.net/ken/buildsrc" + + where I will maintain a Build Source Code FAQ (or perhaps I might + just provide a link to a good FAQ). + + [C] I am willing to respond to questions, but ONLY if they come at a rate + that I can handle. + + PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS! + + As my line of defense, I will post my current policy about + answering Build source questions (right below the E-mail address + on my web site.) You can check there to see if I'm getting + overloaded with questions or not. + + If I'm too busy, it might say something like this: + + I'm too busy to answer Build source questions right now. + Sorry, but don't expect a reply from me any time soon. + + If I'm open for Build source questions, please state your question + clearly and don't include any unsolicited attachments unless + they're really small (like less than 50k). Assume that I have + a 28.8k modem. Also, don't leave out important details just + to make your question appear shorter - making me guess what + you're asking doesn't save me time! + +---------------------------------------------------------------------------- +-Ken S. (official web site: http://www.advsys.net/ken) diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index 39381d336..825b3b531 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -1,483 +1,483 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 2021 Christoph Oelckers & Mitchell Richters - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -*/ -//------------------------------------------------------------------------- - -#include "gamefuncs.h" -#include "gamestruct.h" -#include "intvec.h" -#include "coreactor.h" -#include "interpolate.h" - -//--------------------------------------------------------------------------- -// -// Unified chasecam function for all games. -// -//--------------------------------------------------------------------------- - -int cameradist, cameraclock; - -bool calcChaseCamPos(int* px, int* py, int* pz, DCoreActor* act, sectortype** psect, binangle ang, fixedhoriz horiz, double const smoothratio) -{ - HitInfoBase hitinfo; - binangle daang; - int newdist; - - if (!*psect) return false; - // Calculate new pos to shoot backwards, using averaged values from the big three. - int nx = gi->chaseCamX(ang); - int ny = gi->chaseCamY(ang); - int nz = gi->chaseCamZ(horiz); - - auto bakcstat = act->spr.cstat; - act->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL; - updatesectorz(*px, *py, *pz, psect); - hitscan({ *px, *py, *pz }, *psect, { nx, ny, nz }, hitinfo, CLIPMASK1); - act->spr.cstat = bakcstat; - - int hx = hitinfo.hitpos.X - *px; - int hy = hitinfo.hitpos.Y - *py; - - if (*psect == nullptr) - { - return false; - } - - // If something is in the way, make pp->camera_dist lower if necessary - if (abs(nx) + abs(ny) > abs(hx) + abs(hy)) - { - if (hitinfo.hitWall != nullptr) - { - // Push you a little bit off the wall - *psect = hitinfo.hitSector; - daang = bvectangbam(hitinfo.hitWall->point2Wall()->pos.X - hitinfo.hitWall->pos.X, - hitinfo.hitWall->point2Wall()->pos.Y - hitinfo.hitWall->pos.Y); - newdist = nx * daang.bsin() + ny * -daang.bcos(); - - if (abs(nx) > abs(ny)) - hx -= MulScale(nx, newdist, 28); - else - hy -= MulScale(ny, newdist, 28); - } - else if (hitinfo.hitActor == nullptr) - { - // Push you off the ceiling/floor - *psect = hitinfo.hitSector; - - if (abs(nx) > abs(ny)) - hx -= (nx >> 5); - else - hy -= (ny >> 5); - } - else - { - // If you hit a sprite that's not a wall sprite - try again. - auto hit = hitinfo.hitActor; - - if (!(hit->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) - { - bakcstat = hit->spr.cstat; - hit->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); - calcChaseCamPos(px, py, pz, act, psect, ang, horiz, smoothratio); - hit->spr.cstat = bakcstat; - return false; - } - else - { - // same as wall calculation. - daang = buildang(act->spr.ang - 512); - newdist = nx * daang.bsin() + ny * -daang.bcos(); - - if (abs(nx) > abs(ny)) - hx -= MulScale(nx, newdist, 28); - else - hy -= MulScale(ny, newdist, 28); - } - } - - if (abs(nx) > abs(ny)) - newdist = DivScale(hx, nx, 16); - else - newdist = DivScale(hy, ny, 16); - - if (newdist < cameradist) - cameradist = newdist; - } - - // Actually move you! (Camerdist is 65536 if nothing is in the way) - *px += MulScale(nx, cameradist, 16); - *py += MulScale(ny, cameradist, 16); - *pz += MulScale(nz, cameradist, 16); - - // Caculate clock using GameTicRate so it increases the same rate on all speed computers. - int myclock = PlayClock + MulScale(120 / GameTicRate, int(smoothratio), 16); - if (cameraclock == INT_MIN) - { - // Third person view was just started. - cameraclock = myclock; - } - - // Slowly increase cameradist until it reaches 65536. - cameradist = min(cameradist + ((myclock - cameraclock) << 10), 65536); - cameraclock = myclock; - - // Make sure psectnum is correct. - updatesectorz(*px, *py, *pz, psect); - - return true; -} - -//========================================================================== -// -// consolidated slope calculation -// -//========================================================================== - -void calcSlope(const sectortype* sec, float xpos, float ypos, float* pceilz, float* pflorz) -{ - int bits = 0; - if (pceilz) - { - bits |= sec->ceilingstat; - *pceilz = float(sec->ceilingz); - } - if (pflorz) - { - bits |= sec->floorstat; - *pflorz = float(sec->floorz); - } - - if ((bits & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE) - { - auto wal = sec->firstWall(); - int len = wal->Length(); - if (len != 0) - { - float fac = (wal->deltax() * (float(ypos - wal->pos.Y)) - wal->deltay() * (float(xpos - wal->pos.X))) * (1.f / 256.f) / len; - if (pceilz && sec->ceilingstat & CSTAT_SECTOR_SLOPE) *pceilz += (sec->ceilingheinum * fac); - if (pflorz && sec->floorstat & CSTAT_SECTOR_SLOPE) *pflorz += (sec->floorheinum * fac); - } - } -} - -//========================================================================== -// -// for the renderer (Polymost variants are in polymost.cpp) -// -//========================================================================== - -void PlanesAtPoint(const sectortype* sec, float dax, float day, float* pceilz, float* pflorz) -{ - calcSlope(sec, dax, day, pceilz, pflorz); - if (pceilz) *pceilz *= -(1 / 256.f); - if (pflorz) *pflorz *= -(1 / 256.f); -} - -//========================================================================== -// -// for the games (these are not inlined so that they can inline calcSlope) -// -//========================================================================== - -int getceilzofslopeptr(const sectortype* sec, int dax, int day) -{ - float z; - calcSlope(sec, dax, day, &z, nullptr); - return int(z); -} - -int getflorzofslopeptr(const sectortype* sec, int dax, int day) -{ - float z; - calcSlope(sec, dax, day, nullptr, &z); - return int(z); -} - -void getzsofslopeptr(const sectortype* sec, int dax, int day, int* ceilz, int* florz) -{ - float c, f; - calcSlope(sec, dax, day, &c, &f); - *ceilz = int(c); - *florz = int(f); -} - -//========================================================================== -// -// -// -//========================================================================== - -int getslopeval(sectortype* sect, int x, int y, int z, int basez) -{ - auto wal = sect->firstWall(); - auto delta = wal->delta(); - int i = (y - wal->pos.Y) * delta.X - (x - wal->pos.X) * delta.Y; - return i == 0? 0 : Scale((z - basez) << 8, wal->Length(), i); -} - - -//========================================================================== -// -// Calculate the position of a wall sprite in the world -// -//========================================================================== - -void GetWallSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, bool render) -{ - auto tex = tileGetTexture(spr->picnum); - - int width, leftofs; - if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) - { - width = TileFiles.tiledata[spr->picnum].hiofs.xsize; - leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset); - } - else - { - width = (int)tex->GetDisplayWidth(); - leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset); - } - - int x = bsin(spr->ang) * spr->xrepeat; - int y = -bcos(spr->ang) * spr->xrepeat; - - int xoff = leftofs; - if (spr->cstat & CSTAT_SPRITE_XFLIP) xoff = -xoff; - int origin = (width >> 1) + xoff; - - out[0].X = pos.X - MulScale(x, origin, 16); - out[0].Y = pos.Y - MulScale(y, origin, 16); - out[1].X = out[0].X + MulScale(x, width, 16); - out[1].Y = out[0].Y + MulScale(y, width, 16); -} - - -//========================================================================== -// -// Calculate the position of a wall sprite in the world -// -//========================================================================== - -void TGetFlatSpritePosition(const spritetypebase* spr, vec2_t pos, vec2_t* out, int* outz, int heinum, bool render) -{ - auto tex = tileGetTexture(spr->picnum); - - int width, height, leftofs, topofs; - int ratio = ksqrt(heinum * heinum + 4096 * 4096); - - int xo = heinum ? 0 : spr->xoffset; - int yo = heinum ? 0 : spr->yoffset; - - if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) - { - width = TileFiles.tiledata[spr->picnum].hiofs.xsize * spr->xrepeat; - height = TileFiles.tiledata[spr->picnum].hiofs.ysize * spr->yrepeat; - leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + xo) * spr->xrepeat; - topofs = (TileFiles.tiledata[spr->picnum].hiofs.yoffs + yo) * spr->yrepeat; - } - else - { - width = (int)tex->GetDisplayWidth() * spr->xrepeat; - height = (int)tex->GetDisplayHeight() * spr->yrepeat; - leftofs = ((int)tex->GetDisplayLeftOffset() + xo) * spr->xrepeat; - topofs = ((int)tex->GetDisplayTopOffset() + yo) * spr->yrepeat; - } - - if (spr->cstat & CSTAT_SPRITE_XFLIP) leftofs = -leftofs; - if (spr->cstat & CSTAT_SPRITE_YFLIP) topofs = -topofs; - - int sprcenterx = (width >> 1) + leftofs; - int sprcentery = (height >> 1) + topofs; - - int cosang = bcos(spr->ang); - int sinang = bsin(spr->ang); - int cosangslope = DivScale(cosang, ratio, 12); - int sinangslope = DivScale(sinang, ratio, 12); - - out[0].X = pos.X + DMulScale(sinang, sprcenterx, cosangslope, sprcentery, 16); - out[0].Y = pos.Y + DMulScale(sinangslope, sprcentery, -cosang, sprcenterx, 16); - - out[1].X = out[0].X - MulScale(sinang, width, 16); - out[1].Y = out[0].Y + MulScale(cosang, width, 16); - - vec2_t sub = { MulScale(cosangslope, height, 16), MulScale(sinangslope, height, 16) }; - out[2] = out[1] - sub; - out[3] = out[0] - sub; - if (outz) - { - if (!heinum) outz[3] = outz[2] = outz[1] = outz[0] = 0; - else - { - for (int i = 0; i < 4; i++) - { - int spos = DMulScale(-sinang, out[i].Y - spr->pos.Y, -cosang, out[i].X - spr->pos.X, 4); - outz[i] = MulScale(heinum, spos, 18); - } - } - } -} - -void GetFlatSpritePosition(DCoreActor* actor, vec2_t pos, vec2_t* out, bool render) -{ - TGetFlatSpritePosition(&actor->spr, pos, out, nullptr, spriteGetSlope(actor), render); -} - -void GetFlatSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, int* outz, bool render) -{ - TGetFlatSpritePosition(spr, pos, out, outz, tspriteGetSlope(spr), render); -} - -//========================================================================== -// -// Check if some walls are set to use rotated textures. -// Ideally this should just have been done with texture rotation, -// but the effects on the render code would be too severe due to the alignment mess. -// -//========================================================================== - -void checkRotatedWalls() -{ - for (auto& w : wall) - { - if (w.cstat & CSTAT_WALL_ROTATE_90) - { - auto& tile = RotTile(w.picnum + animateoffs(w.picnum, 16384)); - - if (tile.newtile == -1 && tile.owner == -1) - { - auto owner = w.picnum + animateoffs(w.picnum, 16384); - - tile.newtile = TileFiles.tileCreateRotated(owner); - assert(tile.newtile != -1); - - RotTile(tile.newtile).owner = w.picnum + animateoffs(w.picnum, 16384); - - } - } - } -} - -//========================================================================== -// -// check if two sectors share a wall connection -// -//========================================================================== - -bool sectorsConnected(int sect1, int sect2) -{ - for (auto& wal : wallsofsector(sect1)) - { - if (wal.nextsector == sect2) return true; - } - return false; -} - -//========================================================================== -// -// -// -//========================================================================== - -void dragpoint(walltype* startwall, int newx, int newy) -{ - vertexscan(startwall, [&](walltype* wal) - { - wal->move(newx, newy); - wal->sectorp()->exflags |= SECTOREX_DRAGGED; - }); -} - -//========================================================================== -// -// -// -//========================================================================== - -tspritetype* renderAddTsprite(tspritetype* tsprite, int& spritesortcnt, DCoreActor* actor) -{ - validateTSpriteSize(tsprite, spritesortcnt); - - if (spritesortcnt >= MAXSPRITESONSCREEN) return nullptr; - auto tspr = &tsprite[spritesortcnt++]; - - tspr->pos = actor->spr.pos; - tspr->cstat = actor->spr.cstat; - tspr->picnum = actor->spr.picnum; - tspr->shade = actor->spr.shade; - tspr->pal = actor->spr.pal; - tspr->clipdist = 0; - tspr->blend = actor->spr.blend; - tspr->xrepeat = actor->spr.xrepeat; - tspr->yrepeat = actor->spr.yrepeat; - tspr->xoffset = actor->spr.xoffset; - tspr->yoffset = actor->spr.yoffset; - tspr->sectp = actor->spr.sectp; - tspr->statnum = actor->spr.statnum; - tspr->ang = actor->spr.ang; - tspr->xvel = actor->spr.xvel; - tspr->yvel = actor->spr.yvel; - tspr->zvel = actor->spr.zvel; - tspr->lotag = actor->spr.lotag; - tspr->hitag = actor->spr.hitag; - tspr->extra = actor->spr.extra; - tspr->time = actor->time; - tspr->ownerActor = actor; - - // need to copy the slope sprite flag around because for tsprites the bit combination means 'voxel'. - if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == CSTAT_SPRITE_ALIGNMENT_SLOPE) - { - tspr->cstat &= ~CSTAT_SPRITE_ALIGNMENT_WALL; - tspr->clipdist |= TSPR_SLOPESPRITE; - } - - return tspr; -} - - -//========================================================================== -// -// vector serializers -// -//========================================================================== - -FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def) -{ - if (arc.isWriting() && def && !memcmp(&c, def, sizeof(c))) return arc; - if (arc.BeginObject(key)) - { - arc("x", c.X, def ? &def->X : nullptr) - ("y", c.Y, def ? &def->Y : nullptr) - .EndObject(); - } - return arc; -} - -FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def) -{ - if (arc.isWriting() && def && !memcmp(&c, def, sizeof(c))) return arc; - if (arc.BeginObject(key)) - { - arc("x", c.X, def ? &def->X : nullptr) - ("y", c.Y, def ? &def->Y : nullptr) - ("z", c.Z, def ? &def->Z : nullptr) - .EndObject(); - } - return arc; -} +//------------------------------------------------------------------------- +/* +Copyright (C) 2021 Christoph Oelckers & Mitchell Richters + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ +//------------------------------------------------------------------------- + +#include "gamefuncs.h" +#include "gamestruct.h" +#include "intvec.h" +#include "coreactor.h" +#include "interpolate.h" + +//--------------------------------------------------------------------------- +// +// Unified chasecam function for all games. +// +//--------------------------------------------------------------------------- + +int cameradist, cameraclock; + +bool calcChaseCamPos(int* px, int* py, int* pz, DCoreActor* act, sectortype** psect, binangle ang, fixedhoriz horiz, double const smoothratio) +{ + HitInfoBase hitinfo; + binangle daang; + int newdist; + + if (!*psect) return false; + // Calculate new pos to shoot backwards, using averaged values from the big three. + int nx = gi->chaseCamX(ang); + int ny = gi->chaseCamY(ang); + int nz = gi->chaseCamZ(horiz); + + auto bakcstat = act->spr.cstat; + act->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL; + updatesectorz(*px, *py, *pz, psect); + hitscan({ *px, *py, *pz }, *psect, { nx, ny, nz }, hitinfo, CLIPMASK1); + act->spr.cstat = bakcstat; + + int hx = hitinfo.hitpos.X - *px; + int hy = hitinfo.hitpos.Y - *py; + + if (*psect == nullptr) + { + return false; + } + + // If something is in the way, make pp->camera_dist lower if necessary + if (abs(nx) + abs(ny) > abs(hx) + abs(hy)) + { + if (hitinfo.hitWall != nullptr) + { + // Push you a little bit off the wall + *psect = hitinfo.hitSector; + daang = bvectangbam(hitinfo.hitWall->point2Wall()->pos.X - hitinfo.hitWall->pos.X, + hitinfo.hitWall->point2Wall()->pos.Y - hitinfo.hitWall->pos.Y); + newdist = nx * daang.bsin() + ny * -daang.bcos(); + + if (abs(nx) > abs(ny)) + hx -= MulScale(nx, newdist, 28); + else + hy -= MulScale(ny, newdist, 28); + } + else if (hitinfo.hitActor == nullptr) + { + // Push you off the ceiling/floor + *psect = hitinfo.hitSector; + + if (abs(nx) > abs(ny)) + hx -= (nx >> 5); + else + hy -= (ny >> 5); + } + else + { + // If you hit a sprite that's not a wall sprite - try again. + auto hit = hitinfo.hitActor; + + if (!(hit->spr.cstat & CSTAT_SPRITE_ALIGNMENT_WALL)) + { + bakcstat = hit->spr.cstat; + hit->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); + calcChaseCamPos(px, py, pz, act, psect, ang, horiz, smoothratio); + hit->spr.cstat = bakcstat; + return false; + } + else + { + // same as wall calculation. + daang = buildang(act->spr.ang - 512); + newdist = nx * daang.bsin() + ny * -daang.bcos(); + + if (abs(nx) > abs(ny)) + hx -= MulScale(nx, newdist, 28); + else + hy -= MulScale(ny, newdist, 28); + } + } + + if (abs(nx) > abs(ny)) + newdist = DivScale(hx, nx, 16); + else + newdist = DivScale(hy, ny, 16); + + if (newdist < cameradist) + cameradist = newdist; + } + + // Actually move you! (Camerdist is 65536 if nothing is in the way) + *px += MulScale(nx, cameradist, 16); + *py += MulScale(ny, cameradist, 16); + *pz += MulScale(nz, cameradist, 16); + + // Caculate clock using GameTicRate so it increases the same rate on all speed computers. + int myclock = PlayClock + MulScale(120 / GameTicRate, int(smoothratio), 16); + if (cameraclock == INT_MIN) + { + // Third person view was just started. + cameraclock = myclock; + } + + // Slowly increase cameradist until it reaches 65536. + cameradist = min(cameradist + ((myclock - cameraclock) << 10), 65536); + cameraclock = myclock; + + // Make sure psectnum is correct. + updatesectorz(*px, *py, *pz, psect); + + return true; +} + +//========================================================================== +// +// consolidated slope calculation +// +//========================================================================== + +void calcSlope(const sectortype* sec, float xpos, float ypos, float* pceilz, float* pflorz) +{ + int bits = 0; + if (pceilz) + { + bits |= sec->ceilingstat; + *pceilz = float(sec->ceilingz); + } + if (pflorz) + { + bits |= sec->floorstat; + *pflorz = float(sec->floorz); + } + + if ((bits & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE) + { + auto wal = sec->firstWall(); + int len = wal->Length(); + if (len != 0) + { + float fac = (wal->deltax() * (float(ypos - wal->pos.Y)) - wal->deltay() * (float(xpos - wal->pos.X))) * (1.f / 256.f) / len; + if (pceilz && sec->ceilingstat & CSTAT_SECTOR_SLOPE) *pceilz += (sec->ceilingheinum * fac); + if (pflorz && sec->floorstat & CSTAT_SECTOR_SLOPE) *pflorz += (sec->floorheinum * fac); + } + } +} + +//========================================================================== +// +// for the renderer (Polymost variants are in polymost.cpp) +// +//========================================================================== + +void PlanesAtPoint(const sectortype* sec, float dax, float day, float* pceilz, float* pflorz) +{ + calcSlope(sec, dax, day, pceilz, pflorz); + if (pceilz) *pceilz *= -(1 / 256.f); + if (pflorz) *pflorz *= -(1 / 256.f); +} + +//========================================================================== +// +// for the games (these are not inlined so that they can inline calcSlope) +// +//========================================================================== + +int getceilzofslopeptr(const sectortype* sec, int dax, int day) +{ + float z; + calcSlope(sec, dax, day, &z, nullptr); + return int(z); +} + +int getflorzofslopeptr(const sectortype* sec, int dax, int day) +{ + float z; + calcSlope(sec, dax, day, nullptr, &z); + return int(z); +} + +void getzsofslopeptr(const sectortype* sec, int dax, int day, int* ceilz, int* florz) +{ + float c, f; + calcSlope(sec, dax, day, &c, &f); + *ceilz = int(c); + *florz = int(f); +} + +//========================================================================== +// +// +// +//========================================================================== + +int getslopeval(sectortype* sect, int x, int y, int z, int basez) +{ + auto wal = sect->firstWall(); + auto delta = wal->delta(); + int i = (y - wal->pos.Y) * delta.X - (x - wal->pos.X) * delta.Y; + return i == 0? 0 : Scale((z - basez) << 8, wal->Length(), i); +} + + +//========================================================================== +// +// Calculate the position of a wall sprite in the world +// +//========================================================================== + +void GetWallSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, bool render) +{ + auto tex = tileGetTexture(spr->picnum); + + int width, leftofs; + if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) + { + width = TileFiles.tiledata[spr->picnum].hiofs.xsize; + leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset); + } + else + { + width = (int)tex->GetDisplayWidth(); + leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset); + } + + int x = bsin(spr->ang) * spr->xrepeat; + int y = -bcos(spr->ang) * spr->xrepeat; + + int xoff = leftofs; + if (spr->cstat & CSTAT_SPRITE_XFLIP) xoff = -xoff; + int origin = (width >> 1) + xoff; + + out[0].X = pos.X - MulScale(x, origin, 16); + out[0].Y = pos.Y - MulScale(y, origin, 16); + out[1].X = out[0].X + MulScale(x, width, 16); + out[1].Y = out[0].Y + MulScale(y, width, 16); +} + + +//========================================================================== +// +// Calculate the position of a wall sprite in the world +// +//========================================================================== + +void TGetFlatSpritePosition(const spritetypebase* spr, vec2_t pos, vec2_t* out, int* outz, int heinum, bool render) +{ + auto tex = tileGetTexture(spr->picnum); + + int width, height, leftofs, topofs; + int ratio = ksqrt(heinum * heinum + 4096 * 4096); + + int xo = heinum ? 0 : spr->xoffset; + int yo = heinum ? 0 : spr->yoffset; + + if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) + { + width = TileFiles.tiledata[spr->picnum].hiofs.xsize * spr->xrepeat; + height = TileFiles.tiledata[spr->picnum].hiofs.ysize * spr->yrepeat; + leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + xo) * spr->xrepeat; + topofs = (TileFiles.tiledata[spr->picnum].hiofs.yoffs + yo) * spr->yrepeat; + } + else + { + width = (int)tex->GetDisplayWidth() * spr->xrepeat; + height = (int)tex->GetDisplayHeight() * spr->yrepeat; + leftofs = ((int)tex->GetDisplayLeftOffset() + xo) * spr->xrepeat; + topofs = ((int)tex->GetDisplayTopOffset() + yo) * spr->yrepeat; + } + + if (spr->cstat & CSTAT_SPRITE_XFLIP) leftofs = -leftofs; + if (spr->cstat & CSTAT_SPRITE_YFLIP) topofs = -topofs; + + int sprcenterx = (width >> 1) + leftofs; + int sprcentery = (height >> 1) + topofs; + + int cosang = bcos(spr->ang); + int sinang = bsin(spr->ang); + int cosangslope = DivScale(cosang, ratio, 12); + int sinangslope = DivScale(sinang, ratio, 12); + + out[0].X = pos.X + DMulScale(sinang, sprcenterx, cosangslope, sprcentery, 16); + out[0].Y = pos.Y + DMulScale(sinangslope, sprcentery, -cosang, sprcenterx, 16); + + out[1].X = out[0].X - MulScale(sinang, width, 16); + out[1].Y = out[0].Y + MulScale(cosang, width, 16); + + vec2_t sub = { MulScale(cosangslope, height, 16), MulScale(sinangslope, height, 16) }; + out[2] = out[1] - sub; + out[3] = out[0] - sub; + if (outz) + { + if (!heinum) outz[3] = outz[2] = outz[1] = outz[0] = 0; + else + { + for (int i = 0; i < 4; i++) + { + int spos = DMulScale(-sinang, out[i].Y - spr->pos.Y, -cosang, out[i].X - spr->pos.X, 4); + outz[i] = MulScale(heinum, spos, 18); + } + } + } +} + +void GetFlatSpritePosition(DCoreActor* actor, vec2_t pos, vec2_t* out, bool render) +{ + TGetFlatSpritePosition(&actor->spr, pos, out, nullptr, spriteGetSlope(actor), render); +} + +void GetFlatSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, int* outz, bool render) +{ + TGetFlatSpritePosition(spr, pos, out, outz, tspriteGetSlope(spr), render); +} + +//========================================================================== +// +// Check if some walls are set to use rotated textures. +// Ideally this should just have been done with texture rotation, +// but the effects on the render code would be too severe due to the alignment mess. +// +//========================================================================== + +void checkRotatedWalls() +{ + for (auto& w : wall) + { + if (w.cstat & CSTAT_WALL_ROTATE_90) + { + auto& tile = RotTile(w.picnum + animateoffs(w.picnum, 16384)); + + if (tile.newtile == -1 && tile.owner == -1) + { + auto owner = w.picnum + animateoffs(w.picnum, 16384); + + tile.newtile = TileFiles.tileCreateRotated(owner); + assert(tile.newtile != -1); + + RotTile(tile.newtile).owner = w.picnum + animateoffs(w.picnum, 16384); + + } + } + } +} + +//========================================================================== +// +// check if two sectors share a wall connection +// +//========================================================================== + +bool sectorsConnected(int sect1, int sect2) +{ + for (auto& wal : wallsofsector(sect1)) + { + if (wal.nextsector == sect2) return true; + } + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + +void dragpoint(walltype* startwall, int newx, int newy) +{ + vertexscan(startwall, [&](walltype* wal) + { + wal->move(newx, newy); + wal->sectorp()->exflags |= SECTOREX_DRAGGED; + }); +} + +//========================================================================== +// +// +// +//========================================================================== + +tspritetype* renderAddTsprite(tspritetype* tsprite, int& spritesortcnt, DCoreActor* actor) +{ + validateTSpriteSize(tsprite, spritesortcnt); + + if (spritesortcnt >= MAXSPRITESONSCREEN) return nullptr; + auto tspr = &tsprite[spritesortcnt++]; + + tspr->pos = actor->spr.pos; + tspr->cstat = actor->spr.cstat; + tspr->picnum = actor->spr.picnum; + tspr->shade = actor->spr.shade; + tspr->pal = actor->spr.pal; + tspr->clipdist = 0; + tspr->blend = actor->spr.blend; + tspr->xrepeat = actor->spr.xrepeat; + tspr->yrepeat = actor->spr.yrepeat; + tspr->xoffset = actor->spr.xoffset; + tspr->yoffset = actor->spr.yoffset; + tspr->sectp = actor->spr.sectp; + tspr->statnum = actor->spr.statnum; + tspr->ang = actor->spr.ang; + tspr->xvel = actor->spr.xvel; + tspr->yvel = actor->spr.yvel; + tspr->zvel = actor->spr.zvel; + tspr->lotag = actor->spr.lotag; + tspr->hitag = actor->spr.hitag; + tspr->extra = actor->spr.extra; + tspr->time = actor->time; + tspr->ownerActor = actor; + + // need to copy the slope sprite flag around because for tsprites the bit combination means 'voxel'. + if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == CSTAT_SPRITE_ALIGNMENT_SLOPE) + { + tspr->cstat &= ~CSTAT_SPRITE_ALIGNMENT_WALL; + tspr->clipdist |= TSPR_SLOPESPRITE; + } + + return tspr; +} + + +//========================================================================== +// +// vector serializers +// +//========================================================================== + +FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def) +{ + if (arc.isWriting() && def && !memcmp(&c, def, sizeof(c))) return arc; + if (arc.BeginObject(key)) + { + arc("x", c.X, def ? &def->X : nullptr) + ("y", c.Y, def ? &def->Y : nullptr) + .EndObject(); + } + return arc; +} + +FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def) +{ + if (arc.isWriting() && def && !memcmp(&c, def, sizeof(c))) return arc; + if (arc.BeginObject(key)) + { + arc("x", c.X, def ? &def->X : nullptr) + ("y", c.Y, def ? &def->Y : nullptr) + ("z", c.Z, def ? &def->Z : nullptr) + .EndObject(); + } + return arc; +} diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 2b46eddda..2d0773f34 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -1,406 +1,406 @@ -#pragma once - -#include "gamecontrol.h" -#include "binaryangle.h" -#include "build.h" -#include "coreactor.h" - -// breadth first search, this gets used multiple times throughout the engine, mainly for iterating over sectors. -// Only works on indices, this has no knowledge of the actual objects being looked at. -// All objects of this type operate on the same shared store. Interleaved use is not allowed, nested use is fine. -class BFSSearch -{ - static inline TArray store; - - unsigned bitpos; - unsigned startpos; - unsigned curpos; - -public: - enum { EOL = ~0u }; - BFSSearch(unsigned datasize, unsigned startnode) - { - bitpos = store.Size(); - unsigned bitsize = (datasize + 31) >> 5; - store.Reserve(bitsize); - memset(&store[bitpos], 0, bitsize*4); - - startpos = store.Size(); - curpos = startpos; - Set(startnode); - store.Push(startnode); - } - - // This allows this object to just work as a bit array - // which is useful for using its shared storage. - BFSSearch(unsigned datasize) - { - bitpos = store.Size(); - unsigned bitsize = (datasize + 31) >> 5; - store.Reserve(bitsize); - memset(&store[bitpos], 0, bitsize * 4); - } - - ~BFSSearch() - { - store.Clamp(bitpos); - } - - bool Check(unsigned index) const - { - return !!(store[bitpos + (index >> 5)] & (1 << (index & 31))); - } - - void Set(unsigned index) - { - store[bitpos + (index >> 5)] |= (1 << (index & 31)); - } - - -private: -public: - unsigned GetNext() - { - curpos++; - if (curpos <= store.Size()) - return store[curpos-1]; - else - return ~0; - } - - void Rewind() - { - curpos = startpos; - } - - void Add(unsigned elem) - { - if (!Check(elem)) - { - Set(elem); - store.Push(elem); - } - } -}; - -class BFSSectorSearch : public BFSSearch -{ -public: - - BFSSectorSearch(const sectortype* startnode) : BFSSearch(sector.Size(), sector.IndexOf(startnode)) - { - } - - bool Check(const sectortype* index) const - { - return BFSSearch::Check(sector.IndexOf(index)); - } - - void Set(const sectortype* index) - { - BFSSearch::Set(sector.IndexOf(index)); - } - - sectortype* GetNext() - { - unsigned ret = BFSSearch::GetNext(); - return ret == EOL? nullptr : §or[ret]; - } - - void Add(sectortype* elem) - { - BFSSearch::Add(sector.IndexOf(elem)); - } -}; - -//========================================================================== -// -// scans all vertices equivalent with a given spot and performs some work on them. -// -//========================================================================== - -template -void vertexscan(walltype* startwall, func mark) -{ - BFSSearch walbitmap(wall.Size()); - - // first pass: scan the the next-in-loop of the partner - auto wal = startwall; - do - { - mark(wal); - walbitmap.Set(wall.IndexOf(wal)); - if (wal->nextwall < 0) break; - wal = wal->nextWall()->point2Wall(); - } while (!walbitmap.Check(wall.IndexOf(wal))); - - // second pass: scan the partner of the previous-in-loop. - wal = startwall; - while (true) - { - auto thelastwall = wal->lastWall(); - // thelastwall can be null here if the map is bogus. - if (!thelastwall || !thelastwall->twoSided()) break; - - wal = thelastwall->nextWall(); - if (walbitmap.Check(wall.IndexOf(wal))) break; - mark(wal); - walbitmap.Set(wall.IndexOf(wal)); - } -} - - -extern int cameradist, cameraclock; - -void loaddefinitionsfile(const char* fn, bool cumulative = false, bool maingrp = false); - -bool calcChaseCamPos(int* px, int* py, int* pz, DCoreActor* pspr, sectortype** psectnum, binangle ang, fixedhoriz horiz, double const smoothratio); - -void PlanesAtPoint(const sectortype* sec, float dax, float day, float* ceilz, float* florz); - -int getslopeval(sectortype* sect, int x, int y, int z, int planez); - - - -void setWallSectors(); -void GetWallSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, bool render = false); -void GetFlatSpritePosition(DCoreActor* spr, vec2_t pos, vec2_t* out, bool render = false); -void GetFlatSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, int* outz = nullptr, bool render = false); -void checkRotatedWalls(); -bool sectorsConnected(int sect1, int sect2); -void dragpoint(walltype* wal, int newx, int newy); - -// y is negated so that the orientation is the same as in GZDoom, in order to use its utilities. -// The render code should NOT use Build coordinates for anything! - -inline double RenderX(int x) -{ - return x * (1 / 16.); -} - -inline double RenderY(int y) -{ - return y * (1 / -16.); -} - -inline double WallStartX(int wallnum) -{ - return wall[wallnum].pos.X * (1 / 16.); -} - -inline double WallStartY(int wallnum) -{ - return wall[wallnum].pos.Y * (1 / -16.); -} - -inline double WallEndX(int wallnum) -{ - return wall[wallnum].point2Wall()->pos.X * (1 / 16.); -} - -inline double WallEndY(int wallnum) -{ - return wall[wallnum].point2Wall()->pos.Y * (1 / -16.); -} - -inline double WallStartX(const walltype* wallnum) -{ - return wallnum->pos.X * (1 / 16.); -} - -inline double WallStartY(const walltype* wallnum) -{ - return wallnum->pos.Y * (1 / -16.); -} - -inline DVector2 WallStart(const walltype* wallnum) -{ - return { WallStartX(wallnum), WallStartY(wallnum) }; -} - -inline double WallEndX(const walltype* wallnum) -{ - return wallnum->point2Wall()->pos.X * (1 / 16.); -} - -inline double WallEndY(const walltype* wallnum) -{ - return wallnum->point2Wall()->pos.Y * (1 / -16.); -} - -inline DVector2 WallEnd(const walltype* wallnum) -{ - return { WallEndX(wallnum), WallEndY(wallnum) }; -} - -inline DVector2 WallDelta(const walltype* wallnum) -{ - return WallEnd(wallnum) - WallStart(wallnum); -} - -inline double PointOnLineSide(double x, double y, double linex, double liney, double deltax, double deltay) -{ - return (x - linex) * deltay - (y - liney) * deltax; -} - -inline double PointOnLineSide(const DVector2 &pos, const walltype *line) -{ - return (pos.X - WallStartX(line)) * WallDelta(line).Y - (pos.Y - WallStartY(line)) * WallDelta(line).X; -} - -template -inline double PointOnLineSide(const TVector2& pos, const TVector2& linestart, const TVector2& lineend) -{ - return (pos.X - linestart.X) * (lineend.Y - linestart.Y) - (pos.Y - linestart.Y) * (lineend.X - linestart.X); -} - -extern int numshades; - -// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior. -inline int shadeToLight(int shade) -{ - shade = clamp(shade, 0, numshades - 1); - int light = Scale(numshades - 1 - shade, 255, numshades - 1); - return PalEntry(255, light, light, light); -} - -inline void copyfloorpal(tspritetype* spr, const sectortype* sect) -{ - if (!lookups.noFloorPal(sect->floorpal)) spr->pal = sect->floorpal; -} - -inline void spriteSetSlope(DCoreActor* actor, int heinum) -{ - if (actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR) - { - actor->spr.xoffset = heinum & 255; - actor->spr.yoffset = (heinum >> 8) & 255; - actor->spr.cstat = (actor->spr.cstat & ~CSTAT_SPRITE_ALIGNMENT_MASK) | (heinum != 0 ? CSTAT_SPRITE_ALIGNMENT_SLOPE : CSTAT_SPRITE_ALIGNMENT_FLOOR); - } -} - -inline int spriteGetSlope(DCoreActor* actor) -{ - return ((actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_MASK) != CSTAT_SPRITE_ALIGNMENT_SLOPE) ? 0 : uint8_t(actor->spr.xoffset) + (uint8_t(actor->spr.yoffset) << 8); -} - -// same stuff, different flag... -inline int tspriteGetSlope(const tspritetype* spr) -{ - return !(spr->clipdist & TSPR_SLOPESPRITE) ? 0 : uint8_t(spr->xoffset) + (int8_t(spr->yoffset) << 8); -} - -inline int32_t tspriteGetZOfSlope(const tspritetype* tspr, int dax, int day) -{ - int heinum = tspriteGetSlope(tspr); - if (heinum == 0) return tspr->pos.Z; - - int const j = DMulScale(bsin(tspr->ang + 1024), day - tspr->pos.Y, -bsin(tspr->ang + 512), dax - tspr->pos.X, 4); - return tspr->pos.Z + MulScale(heinum, j, 18); -} - - -inline int I_GetBuildTime() -{ - return I_GetTime(120); -} - -inline int32_t getangle(walltype* wal) -{ - return getangle( - wal->point2Wall()->pos.X - wal->pos.X, - wal->point2Wall()->pos.Y - wal->pos.Y); -} - -inline TArrayView wallsofsector(const sectortype* sec) -{ - return TArrayView(sec->firstWall(), sec->wallnum); -} - -inline TArrayView wallsofsector(int sec) -{ - return wallsofsector(§or[sec]); -} - -// these are mainly meant as refactoring aids to mark function calls to work on. -inline int wallnum(const walltype* wal) -{ - return wall.IndexOf(wal); -} - -inline int sectnum(const sectortype* sect) -{ - return sector.IndexOf(sect); -} - -inline double SquareDist(double lx1, double ly1, double lx2, double ly2) -{ - double dx = lx2 - lx1; - double dy = ly2 - ly1; - return dx * dx + dy * dy; -} - -inline DVector2 NearestPointLine(double px, double py, const walltype* wal) -{ - double lx1 = wal->pos.X; - double ly1 = wal->pos.Y; - double lx2 = wal->point2Wall()->pos.X; - double ly2 = wal->point2Wall()->pos.Y; - - double wall_length = SquareDist(lx1, ly1, lx2, ly2); - - if (wall_length == 0) return { lx1, ly1 }; - - double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; - double xx = lx1 + t * (lx2 - lx1); - double yy = ly1 + t * (ly2 - ly1); - return { xx, yy }; -} - -inline double SquareDistToWall(double px, double py, const walltype* wal, DVector2* point = nullptr) -{ - double lx1 = wal->pos.X; - double ly1 = wal->pos.Y; - double lx2 = wal->point2Wall()->pos.X; - double ly2 = wal->point2Wall()->pos.Y; - - double wall_length = SquareDist(lx1, ly1, lx2, ly2); - - if (wall_length == 0) return SquareDist(px, py, lx1, ly1); - - double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; - t = clamp(t, 0., 1.); - double xx = lx1 + t * (lx2 - lx1); - double yy = ly1 + t * (ly2 - ly1); - if (point) *point = { xx, yy }; - return SquareDist(px, py, xx, yy); -} - -inline double SquareDistToLine(double px, double py, double lx1, double ly1, double lx2, double ly2) -{ - double wall_length = SquareDist(lx1, ly1, lx2, ly2); - - if (wall_length == 0) return SquareDist(px, py, lx1, ly1); - - double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; - t = clamp(t, 0., 1.); - double xx = lx1 + t * (lx2 - lx1); - double yy = ly1 + t * (ly2 - ly1); - return SquareDist(px, py, xx, yy); -} - -inline void alignceilslope(sectortype* sect, int x, int y, int z) -{ - sect->setceilingslope(getslopeval(sect, x, y, z, sect->ceilingz)); -} - -inline void alignflorslope(sectortype* sect, int x, int y, int z) -{ - sect->setfloorslope(getslopeval(sect, x, y, z, sect->floorz)); -} -inline void updatesectorneighbor(int32_t const x, int32_t const y, sectortype* * const sect, int32_t maxDistance = MAXUPDATESECTORDIST) -{ - int sectno = *sect? sector.IndexOf(*sect) : -1; - updatesectorneighbor(x, y, §no, maxDistance); - *sect = sectno < 0? nullptr : §or[sectno]; -} +#pragma once + +#include "gamecontrol.h" +#include "binaryangle.h" +#include "build.h" +#include "coreactor.h" + +// breadth first search, this gets used multiple times throughout the engine, mainly for iterating over sectors. +// Only works on indices, this has no knowledge of the actual objects being looked at. +// All objects of this type operate on the same shared store. Interleaved use is not allowed, nested use is fine. +class BFSSearch +{ + static inline TArray store; + + unsigned bitpos; + unsigned startpos; + unsigned curpos; + +public: + enum { EOL = ~0u }; + BFSSearch(unsigned datasize, unsigned startnode) + { + bitpos = store.Size(); + unsigned bitsize = (datasize + 31) >> 5; + store.Reserve(bitsize); + memset(&store[bitpos], 0, bitsize*4); + + startpos = store.Size(); + curpos = startpos; + Set(startnode); + store.Push(startnode); + } + + // This allows this object to just work as a bit array + // which is useful for using its shared storage. + BFSSearch(unsigned datasize) + { + bitpos = store.Size(); + unsigned bitsize = (datasize + 31) >> 5; + store.Reserve(bitsize); + memset(&store[bitpos], 0, bitsize * 4); + } + + ~BFSSearch() + { + store.Clamp(bitpos); + } + + bool Check(unsigned index) const + { + return !!(store[bitpos + (index >> 5)] & (1 << (index & 31))); + } + + void Set(unsigned index) + { + store[bitpos + (index >> 5)] |= (1 << (index & 31)); + } + + +private: +public: + unsigned GetNext() + { + curpos++; + if (curpos <= store.Size()) + return store[curpos-1]; + else + return ~0; + } + + void Rewind() + { + curpos = startpos; + } + + void Add(unsigned elem) + { + if (!Check(elem)) + { + Set(elem); + store.Push(elem); + } + } +}; + +class BFSSectorSearch : public BFSSearch +{ +public: + + BFSSectorSearch(const sectortype* startnode) : BFSSearch(sector.Size(), sector.IndexOf(startnode)) + { + } + + bool Check(const sectortype* index) const + { + return BFSSearch::Check(sector.IndexOf(index)); + } + + void Set(const sectortype* index) + { + BFSSearch::Set(sector.IndexOf(index)); + } + + sectortype* GetNext() + { + unsigned ret = BFSSearch::GetNext(); + return ret == EOL? nullptr : §or[ret]; + } + + void Add(sectortype* elem) + { + BFSSearch::Add(sector.IndexOf(elem)); + } +}; + +//========================================================================== +// +// scans all vertices equivalent with a given spot and performs some work on them. +// +//========================================================================== + +template +void vertexscan(walltype* startwall, func mark) +{ + BFSSearch walbitmap(wall.Size()); + + // first pass: scan the the next-in-loop of the partner + auto wal = startwall; + do + { + mark(wal); + walbitmap.Set(wall.IndexOf(wal)); + if (wal->nextwall < 0) break; + wal = wal->nextWall()->point2Wall(); + } while (!walbitmap.Check(wall.IndexOf(wal))); + + // second pass: scan the partner of the previous-in-loop. + wal = startwall; + while (true) + { + auto thelastwall = wal->lastWall(); + // thelastwall can be null here if the map is bogus. + if (!thelastwall || !thelastwall->twoSided()) break; + + wal = thelastwall->nextWall(); + if (walbitmap.Check(wall.IndexOf(wal))) break; + mark(wal); + walbitmap.Set(wall.IndexOf(wal)); + } +} + + +extern int cameradist, cameraclock; + +void loaddefinitionsfile(const char* fn, bool cumulative = false, bool maingrp = false); + +bool calcChaseCamPos(int* px, int* py, int* pz, DCoreActor* pspr, sectortype** psectnum, binangle ang, fixedhoriz horiz, double const smoothratio); + +void PlanesAtPoint(const sectortype* sec, float dax, float day, float* ceilz, float* florz); + +int getslopeval(sectortype* sect, int x, int y, int z, int planez); + + + +void setWallSectors(); +void GetWallSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, bool render = false); +void GetFlatSpritePosition(DCoreActor* spr, vec2_t pos, vec2_t* out, bool render = false); +void GetFlatSpritePosition(const tspritetype* spr, vec2_t pos, vec2_t* out, int* outz = nullptr, bool render = false); +void checkRotatedWalls(); +bool sectorsConnected(int sect1, int sect2); +void dragpoint(walltype* wal, int newx, int newy); + +// y is negated so that the orientation is the same as in GZDoom, in order to use its utilities. +// The render code should NOT use Build coordinates for anything! + +inline double RenderX(int x) +{ + return x * (1 / 16.); +} + +inline double RenderY(int y) +{ + return y * (1 / -16.); +} + +inline double WallStartX(int wallnum) +{ + return wall[wallnum].pos.X * (1 / 16.); +} + +inline double WallStartY(int wallnum) +{ + return wall[wallnum].pos.Y * (1 / -16.); +} + +inline double WallEndX(int wallnum) +{ + return wall[wallnum].point2Wall()->pos.X * (1 / 16.); +} + +inline double WallEndY(int wallnum) +{ + return wall[wallnum].point2Wall()->pos.Y * (1 / -16.); +} + +inline double WallStartX(const walltype* wallnum) +{ + return wallnum->pos.X * (1 / 16.); +} + +inline double WallStartY(const walltype* wallnum) +{ + return wallnum->pos.Y * (1 / -16.); +} + +inline DVector2 WallStart(const walltype* wallnum) +{ + return { WallStartX(wallnum), WallStartY(wallnum) }; +} + +inline double WallEndX(const walltype* wallnum) +{ + return wallnum->point2Wall()->pos.X * (1 / 16.); +} + +inline double WallEndY(const walltype* wallnum) +{ + return wallnum->point2Wall()->pos.Y * (1 / -16.); +} + +inline DVector2 WallEnd(const walltype* wallnum) +{ + return { WallEndX(wallnum), WallEndY(wallnum) }; +} + +inline DVector2 WallDelta(const walltype* wallnum) +{ + return WallEnd(wallnum) - WallStart(wallnum); +} + +inline double PointOnLineSide(double x, double y, double linex, double liney, double deltax, double deltay) +{ + return (x - linex) * deltay - (y - liney) * deltax; +} + +inline double PointOnLineSide(const DVector2 &pos, const walltype *line) +{ + return (pos.X - WallStartX(line)) * WallDelta(line).Y - (pos.Y - WallStartY(line)) * WallDelta(line).X; +} + +template +inline double PointOnLineSide(const TVector2& pos, const TVector2& linestart, const TVector2& lineend) +{ + return (pos.X - linestart.X) * (lineend.Y - linestart.Y) - (pos.Y - linestart.Y) * (lineend.X - linestart.X); +} + +extern int numshades; + +// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior. +inline int shadeToLight(int shade) +{ + shade = clamp(shade, 0, numshades - 1); + int light = Scale(numshades - 1 - shade, 255, numshades - 1); + return PalEntry(255, light, light, light); +} + +inline void copyfloorpal(tspritetype* spr, const sectortype* sect) +{ + if (!lookups.noFloorPal(sect->floorpal)) spr->pal = sect->floorpal; +} + +inline void spriteSetSlope(DCoreActor* actor, int heinum) +{ + if (actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR) + { + actor->spr.xoffset = heinum & 255; + actor->spr.yoffset = (heinum >> 8) & 255; + actor->spr.cstat = (actor->spr.cstat & ~CSTAT_SPRITE_ALIGNMENT_MASK) | (heinum != 0 ? CSTAT_SPRITE_ALIGNMENT_SLOPE : CSTAT_SPRITE_ALIGNMENT_FLOOR); + } +} + +inline int spriteGetSlope(DCoreActor* actor) +{ + return ((actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_MASK) != CSTAT_SPRITE_ALIGNMENT_SLOPE) ? 0 : uint8_t(actor->spr.xoffset) + (uint8_t(actor->spr.yoffset) << 8); +} + +// same stuff, different flag... +inline int tspriteGetSlope(const tspritetype* spr) +{ + return !(spr->clipdist & TSPR_SLOPESPRITE) ? 0 : uint8_t(spr->xoffset) + (int8_t(spr->yoffset) << 8); +} + +inline int32_t tspriteGetZOfSlope(const tspritetype* tspr, int dax, int day) +{ + int heinum = tspriteGetSlope(tspr); + if (heinum == 0) return tspr->pos.Z; + + int const j = DMulScale(bsin(tspr->ang + 1024), day - tspr->pos.Y, -bsin(tspr->ang + 512), dax - tspr->pos.X, 4); + return tspr->pos.Z + MulScale(heinum, j, 18); +} + + +inline int I_GetBuildTime() +{ + return I_GetTime(120); +} + +inline int32_t getangle(walltype* wal) +{ + return getangle( + wal->point2Wall()->pos.X - wal->pos.X, + wal->point2Wall()->pos.Y - wal->pos.Y); +} + +inline TArrayView wallsofsector(const sectortype* sec) +{ + return TArrayView(sec->firstWall(), sec->wallnum); +} + +inline TArrayView wallsofsector(int sec) +{ + return wallsofsector(§or[sec]); +} + +// these are mainly meant as refactoring aids to mark function calls to work on. +inline int wallnum(const walltype* wal) +{ + return wall.IndexOf(wal); +} + +inline int sectnum(const sectortype* sect) +{ + return sector.IndexOf(sect); +} + +inline double SquareDist(double lx1, double ly1, double lx2, double ly2) +{ + double dx = lx2 - lx1; + double dy = ly2 - ly1; + return dx * dx + dy * dy; +} + +inline DVector2 NearestPointLine(double px, double py, const walltype* wal) +{ + double lx1 = wal->pos.X; + double ly1 = wal->pos.Y; + double lx2 = wal->point2Wall()->pos.X; + double ly2 = wal->point2Wall()->pos.Y; + + double wall_length = SquareDist(lx1, ly1, lx2, ly2); + + if (wall_length == 0) return { lx1, ly1 }; + + double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; + double xx = lx1 + t * (lx2 - lx1); + double yy = ly1 + t * (ly2 - ly1); + return { xx, yy }; +} + +inline double SquareDistToWall(double px, double py, const walltype* wal, DVector2* point = nullptr) +{ + double lx1 = wal->pos.X; + double ly1 = wal->pos.Y; + double lx2 = wal->point2Wall()->pos.X; + double ly2 = wal->point2Wall()->pos.Y; + + double wall_length = SquareDist(lx1, ly1, lx2, ly2); + + if (wall_length == 0) return SquareDist(px, py, lx1, ly1); + + double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; + t = clamp(t, 0., 1.); + double xx = lx1 + t * (lx2 - lx1); + double yy = ly1 + t * (ly2 - ly1); + if (point) *point = { xx, yy }; + return SquareDist(px, py, xx, yy); +} + +inline double SquareDistToLine(double px, double py, double lx1, double ly1, double lx2, double ly2) +{ + double wall_length = SquareDist(lx1, ly1, lx2, ly2); + + if (wall_length == 0) return SquareDist(px, py, lx1, ly1); + + double t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1)) / wall_length; + t = clamp(t, 0., 1.); + double xx = lx1 + t * (lx2 - lx1); + double yy = ly1 + t * (ly2 - ly1); + return SquareDist(px, py, xx, yy); +} + +inline void alignceilslope(sectortype* sect, int x, int y, int z) +{ + sect->setceilingslope(getslopeval(sect, x, y, z, sect->ceilingz)); +} + +inline void alignflorslope(sectortype* sect, int x, int y, int z) +{ + sect->setfloorslope(getslopeval(sect, x, y, z, sect->floorz)); +} +inline void updatesectorneighbor(int32_t const x, int32_t const y, sectortype* * const sect, int32_t maxDistance = MAXUPDATESECTORDIST) +{ + int sectno = *sect? sector.IndexOf(*sect) : -1; + updatesectorneighbor(x, y, §no, maxDistance); + *sect = sectno < 0? nullptr : §or[sectno]; +}