From a36377111c77e8b5f02273d7d7d4fdb2cd870700 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 21 Mar 2021 14:48:35 +0100 Subject: [PATCH] - engine-side portal setup for Blood and RRRA. --- source/build/include/buildtypes.h | 14 +-- source/core/rendering/hw_entrypoint.cpp | 1 + source/core/rendering/render.h | 66 ++++++++++++ source/core/savegamehelp.cpp | 2 + source/games/blood/src/mirrors.cpp | 18 +++- source/games/duke/src/premap.cpp | 127 ++++++++++++++++++++++-- 6 files changed, 212 insertions(+), 16 deletions(-) diff --git a/source/build/include/buildtypes.h b/source/build/include/buildtypes.h index 649b22bbc..52b82791a 100644 --- a/source/build/include/buildtypes.h +++ b/source/build/include/buildtypes.h @@ -36,15 +36,13 @@ enum { PORTAL_SECTOR_FLOOR = 1, PORTAL_SECTOR_CEILING = 2, - PORTAL_SECTOR_FLOOR_REFLECT = 4, - PORTAL_SECTOR_CEILING_REFLECT = 8, + PORTAL_SECTOR_FLOOR_REFLECT = 3, + PORTAL_SECTOR_CEILING_REFLECT = 4, + PORTAL_WALL_VIEW = 5, + PORTAL_WALL_MIRROR = 6, + PORTAL_SECTOR_GEOMETRY = 7, }; -enum -{ - PORTAL_WALL_VIEW = 1, - PORTAL_WALL_MIRROR = 2, -}; //40 bytes struct sectortype @@ -68,6 +66,7 @@ struct sectortype uint8_t dirty; float ceilingxpan_, ceilingypan_, floorxpan_, floorypan_; uint8_t portalflags; + uint8_t portalnum; int ceilingxpan() const { return int(ceilingxpan_); } int ceilingypan() const { return int(ceilingypan_); } @@ -120,6 +119,7 @@ struct walltype float xpan_, ypan_; angle_t clipangle; uint8_t portalflags; + uint8_t portalnum; int xpan() const { return int(xpan_); } int ypan() const { return int(ypan_); } diff --git a/source/core/rendering/hw_entrypoint.cpp b/source/core/rendering/hw_entrypoint.cpp index 829e508d6..1f27eb26f 100644 --- a/source/core/rendering/hw_entrypoint.cpp +++ b/source/core/rendering/hw_entrypoint.cpp @@ -54,6 +54,7 @@ EXTERN_CVAR(Bool, cl_capfps) PalEntry GlobalMapFog; float GlobalFogDensity; +TArray allPortals; #if 0 diff --git a/source/core/rendering/render.h b/source/core/rendering/render.h index d164c9094..721b33683 100644 --- a/source/core/rendering/render.h +++ b/source/core/rendering/render.h @@ -3,3 +3,69 @@ void render_drawrooms(spritetype* playersprite, const vec3_t& position, int sectnum, fixed_t q16angle, fixed_t q16horizon, float rollang); +struct PortalDesc +{ + int type; + int dx, dy, dz; + TArray targets; +}; + +extern TArray allPortals; + +inline void portalClear() +{ + allPortals.Clear(); +} + +inline int portalAdd(int type, int target, int dx = 0, int dy = 0, int dz = 0) +{ + auto& pt = allPortals[allPortals.Reserve(1)]; + pt.type = type; + pt.targets.Push(target); + pt.dx = dx; + pt.dy = dy; + pt.dz = dz; + return allPortals.Size() - 1; +} + +// merges portals in adjoining sectors. AFAIK the only one of this kind is in Blood's E4M9, which with the original code is very glitchy. +inline void mergePortals() +{ + Printf("Have %d portals\n", allPortals.Size()); + bool didsomething = true; + while (didsomething) + { + didsomething = false; + for (unsigned i = 0; i < allPortals.Size(); i++) + { + auto& pt1 = allPortals[i]; + if (pt1.type == PORTAL_SECTOR_CEILING || pt1.type == PORTAL_SECTOR_FLOOR) + { + for (unsigned j = i + 1; j < allPortals.Size(); j++) + { + auto& pt2 = allPortals[j]; + if (pt1.type != pt2.type || pt1.dx != pt2.dx || pt1.dy != pt2.dy || pt1.dz != pt2.dz) continue; + for (unsigned s = 0; s < pt1.targets.Size(); s++) + { + for (unsigned t = 0; t < pt2.targets.Size(); t++) + { + if (findwallbetweensectors(pt1.targets[s], pt2.targets[t]) >= 0) + { + pt1.targets.Append(pt2.targets); + pt2.targets.Reset(); + pt2.type = -1; + for (int n = 0; n < numsectors; n++) + { + Printf("Merged %d and %d\n", i, j); + if (sector[n].portalnum == j) sector[n].portalnum = i; + didsomething = true; + break; + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index f92d1a2c1..3c5b0ca71 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -555,6 +555,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sectortype &c, sectort ("hitag", c.hitag, def->hitag) ("extra", c.extra, def->extra) ("portalflags", c.portalflags, def->portalflags) + ("portalnum", c.portalnum, def->portalnum) .EndObject(); } return arc; @@ -582,6 +583,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, walltype &c, walltype ("hitag", c.hitag, def->hitag) ("extra", c.extra, def->extra) ("portalflags", c.portalflags, def->portalflags) + ("portalnum", c.portalnum, def->portalnum) .EndObject(); } return arc; diff --git a/source/games/blood/src/mirrors.cpp b/source/games/blood/src/mirrors.cpp index 8dfe0d9fa..794cfe469 100644 --- a/source/games/blood/src/mirrors.cpp +++ b/source/games/blood/src/mirrors.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "savegamehelp.h" #include "blood.h" +#include "render.h" BEGIN_BLD_NS @@ -42,6 +43,7 @@ void InitMirrors(void) mirrorcnt = 0; tileDelete(504); + portalClear(); for (int i = 0; i < 16; i++) { @@ -57,7 +59,7 @@ void InitMirrors(void) if (wall[i].extra > 0 && GetWallType(i) == kWallStack) { wall[i].overpicnum = nTile; - wall[i].portalflags = PORTAL_WALL_VIEW; + mirror[mirrorcnt].wallnum = i; mirror[mirrorcnt].type = 0; wall[i].cstat |= 32; @@ -78,8 +80,15 @@ void InitMirrors(void) } } if (j < 0) - I_Error("wall[%d] has no matching wall link! (data=%d)\n", i, tmp); - mirrorcnt++; + { + Printf(PRINT_HIGH, "wall[%d] has no matching wall link! (data=%d)\n", i, tmp); + } + else + { + mirrorcnt++; + wall[i].portalflags = PORTAL_WALL_VIEW; + wall[i].portalnum = portalAdd(PORTAL_WALL_VIEW, j); + } } continue; } @@ -117,6 +126,7 @@ void InitMirrors(void) mirror[mirrorcnt].link = j; sector[i].floorpicnum = 4080+mirrorcnt; sector[i].portalflags = PORTAL_SECTOR_FLOOR; + sector[i].portalnum = portalAdd(PORTAL_SECTOR_FLOOR, j, mirror[mirrorcnt].dx, mirror[mirrorcnt].dy, mirror[mirrorcnt].dz); mirrorcnt++; mirror[mirrorcnt].type = 1; mirror[mirrorcnt].dx = sprite[nLink].x-sprite[nLink2].x; @@ -126,10 +136,12 @@ void InitMirrors(void) mirror[mirrorcnt].link = i; sector[j].ceilingpicnum = 4080+mirrorcnt; sector[j].portalflags = PORTAL_SECTOR_CEILING; + sector[i].portalnum = portalAdd(PORTAL_SECTOR_CEILING, j, mirror[mirrorcnt].dx, mirror[mirrorcnt].dy, mirror[mirrorcnt].dz); mirrorcnt++; } } mirrorsector = numsectors; + mergePortals(); #if 1 // The new backend won't need this shit anymore. for (int i = 0; i < 4; i++) { diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index b9a4a3b22..97c0d65e7 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "automap.h" #include "dukeactor.h" #include "interpolate.h" +#include "render.h" BEGIN_DUKE_NS @@ -838,6 +839,124 @@ void newgame(MapRecord* map, int sk, func completion) else completion1(false); } +//--------------------------------------------------------------------------- +// +// the setup here is very, very sloppy, because mappings are not 1:1. +// Each portal can have multiple sectors, and even extends to unmarked +// neighboring sectors if they got the portal tile as floor or ceiling +// +//--------------------------------------------------------------------------- + +static void SpawnPortals() +{ + for (int i = 0; i < numwalls; i++) + { + if (wall[i].overpicnum == TILE_MIRROR) wall[i].portalflags |= PORTAL_WALL_MIRROR; + } + + portalClear(); + int tag; + if (!isRR()) tag = 40; + else if (isRRRA()) tag = 150; + else return; + + TArray processedTags; + DukeStatIterator it(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = &act->s; + if (spr->picnum == SECTOREFFECTOR && spr->lotag == tag) + { + if (processedTags.Find(spr->hitag) == processedTags.Size()) + { + DukeStatIterator it2(STAT_RAROR); + while (auto act2 = it2.Next()) + { + auto spr2 = &act2->s; + if (spr2->picnum == SECTOREFFECTOR && spr2->lotag == tag + 1 && spr2->hitag == spr->hitag) + { + if (processedTags.Find(spr->hitag) == processedTags.Size()) + { + int s1 = spr->sectnum, s2 = spr2->sectnum; + sector[s1].portalflags = PORTAL_SECTOR_FLOOR; + sector[s2].portalflags = PORTAL_SECTOR_CEILING; + sector[s1].portalnum = portalAdd(PORTAL_SECTOR_FLOOR, s2, spr2->x - spr->x, spr2->y - spr->y, spr->hitag); + sector[s2].portalnum = portalAdd(PORTAL_SECTOR_CEILING, s1, spr->x - spr2->x, spr->y - spr2->y, spr->hitag); + processedTags.Push(spr->hitag); + } + else + { + for (auto& p : allPortals) + { + if (p.type == PORTAL_SECTOR_FLOOR && p.dz == spr->hitag) + { + p.targets.Push(spr2->sectnum); + } + } + } + } + } + } + else + { + for (auto& p : allPortals) + { + if (p.type == PORTAL_SECTOR_CEILING && p.dz == spr->hitag) + { + p.targets.Push(spr->sectnum); + } + } + } + } + } + // Unfortunately the above still isn't enough. We got to do one more check to add stuff to the portals. + // There is one map where a sector neighboring a portal is not marked as part of the portal itself. + for (int i = 0; i < numsectors; i++) + { + if (sector[i].floorpicnum == FOF && sector[i].portalflags != PORTAL_SECTOR_FLOOR) + { + for (auto& pt : allPortals) + { + if (pt.type == PORTAL_SECTOR_CEILING) + { + for (auto& t : pt.targets) + { + if (findwallbetweensectors(i, t) >= 0) + { + sector[i].portalflags = PORTAL_SECTOR_FLOOR; + sector[i].portalnum = uint8_t(&pt - allPortals.Data()); + pt.targets.Push(i); + goto nexti; + } + } + } + } + } + else if (sector[i].ceilingpicnum == FOF && sector[i].portalflags != PORTAL_SECTOR_CEILING) + { + for (auto& pt : allPortals) + { + if (pt.type == PORTAL_SECTOR_FLOOR) + { + for (auto& t : pt.targets) + { + if (findwallbetweensectors(i, t) >= 0) + { + sector[i].portalflags = PORTAL_SECTOR_CEILING; + sector[i].portalnum = uint8_t(&pt - allPortals.Data()); + pt.targets.Push(i); + goto nexti; + } + } + } + } + } + nexti:; + } + for (auto& p : allPortals) p.dz = 0; + mergePortals(); +} + //--------------------------------------------------------------------------- // // @@ -853,12 +972,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) } engineLoadBoard(mi->fileName, isShareware(), &p->pos, &lbang, &p->cursectnum); - for(int i = 0; i < numsectors; i++) - { - if (sector[i].ceilingpicnum == FOF) sector[i].portalflags |= PORTAL_SECTOR_CEILING; - if (sector[i].floorpicnum == FOF) sector[i].portalflags |= PORTAL_SECTOR_FLOOR; - if (wall[i].overpicnum == TILE_MIRROR) wall[i].portalflags |= PORTAL_WALL_MIRROR; - } currentLevel = mi; SECRET_SetMapName(mi->DisplayName(), mi->name); @@ -877,6 +990,8 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) if (isRR()) prelevel_r(gamemode); else prelevel_d(gamemode); + SpawnPortals(); + if (isRRRA() && mi->levelNumber == levelnum(2, 0)) { for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)