mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-21 11:31:41 +00:00
The main loading block is now completely in MapLoader, except for the compatibility script handler
This commit is contained in:
parent
01d258910b
commit
b7365fb70f
12 changed files with 921 additions and 874 deletions
|
@ -1074,6 +1074,7 @@ set (PCH_SOURCES
|
|||
hwrenderer/utility/hw_vrmodes.cpp
|
||||
maploader/mapdata.cpp
|
||||
maploader/maploader.cpp
|
||||
maploader/maploader_misc.cpp
|
||||
maploader/maploader_glnodes.cpp
|
||||
maploader/maploader_bsp.cpp
|
||||
maploader/maploader_blockmap.cpp
|
||||
|
|
|
@ -96,6 +96,7 @@ struct FLevelLocals
|
|||
TArray<FSectorPortalGroup *> portalGroups;
|
||||
TArray<FLinePortalSpan> linePortalSpans;
|
||||
FSectionContainer sections;
|
||||
|
||||
|
||||
int NumMapSections;
|
||||
|
||||
|
|
|
@ -44,6 +44,32 @@ enum
|
|||
MISSING_TEXTURE_WARN_LIMIT = 20
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SummarizeMissingTextures
|
||||
//
|
||||
// Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
|
||||
// times.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapLoader::SummarizeMissingTextures(const FMissingTextureTracker &missing)
|
||||
{
|
||||
FMissingTextureTracker::ConstIterator it(missing);
|
||||
FMissingTextureTracker::ConstPair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
if (pair->Value.Count > MISSING_TEXTURE_WARN_LIMIT)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Missing texture '"
|
||||
TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
|
||||
"' is used %d more times\n",
|
||||
pair->Key.GetChars(), pair->Value.Count - MISSING_TEXTURE_WARN_LIMIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// P_LoadVertexes
|
||||
|
@ -1326,3 +1352,154 @@ void MapLoader::LoadSideDefs2 (MapData *map, FMissingTextureTracker &missingtex)
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// P_LoadThings
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static uint16_t MakeSkill(int flags)
|
||||
{
|
||||
uint16_t res = 0;
|
||||
if (flags & 1) res |= 1+2;
|
||||
if (flags & 2) res |= 4;
|
||||
if (flags & 4) res |= 8+16;
|
||||
return res;
|
||||
}
|
||||
|
||||
void MapLoader::LoadThings (MapData * map)
|
||||
{
|
||||
int lumplen = map->Size(ML_THINGS);
|
||||
int numthings = lumplen / sizeof(mapthing_t);
|
||||
|
||||
TArray<uint8_t> mtp;
|
||||
map->Read(ML_THINGS, mtp.Data());
|
||||
auto mt = (mapthing_t*)mtp.Data();
|
||||
|
||||
MapThingsConverted.Resize(numthings);
|
||||
FMapThing *mti = &MapThingsConverted[0];
|
||||
|
||||
// [RH] ZDoom now uses Hexen-style maps as its native format.
|
||||
// Since this is the only place where Doom-style Things are ever
|
||||
// referenced, we translate them into a Hexen-style thing.
|
||||
for (int i=0 ; i < numthings; i++, mt++)
|
||||
{
|
||||
// [RH] At this point, monsters unique to Doom II were weeded out
|
||||
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
|
||||
// handle these and more cases better, so we just pass it
|
||||
// everything and let it decide what to do with them.
|
||||
|
||||
// [RH] Need to translate the spawn flags to Hexen format.
|
||||
short flags = LittleShort(mt->options);
|
||||
|
||||
memset (&mti[i], 0, sizeof(mti[i]));
|
||||
|
||||
mti[i].Gravity = 1;
|
||||
mti[i].Conversation = 0;
|
||||
mti[i].SkillFilter = MakeSkill(flags);
|
||||
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
|
||||
mti[i].RenderStyle = STYLE_Count;
|
||||
mti[i].Alpha = -1;
|
||||
mti[i].Health = 1;
|
||||
mti[i].FloatbobPhase = -1;
|
||||
|
||||
mti[i].pos.X = LittleShort(mt->x);
|
||||
mti[i].pos.Y = LittleShort(mt->y);
|
||||
mti[i].angle = LittleShort(mt->angle);
|
||||
mti[i].EdNum = LittleShort(mt->type);
|
||||
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
|
||||
|
||||
|
||||
#ifndef NO_EDATA
|
||||
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
|
||||
{
|
||||
ProcessEDMapthing(&mti[i], flags);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
flags &= ~MTF_SKILLMASK;
|
||||
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
|
||||
if (gameinfo.gametype == GAME_Strife)
|
||||
{
|
||||
mti[i].flags &= ~MTF_AMBUSH;
|
||||
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
|
||||
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
|
||||
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
|
||||
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
|
||||
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags & BTF_BADEDITORCHECK)
|
||||
{
|
||||
flags &= 0x1F;
|
||||
}
|
||||
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
|
||||
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
|
||||
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
|
||||
}
|
||||
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// [RH]
|
||||
// P_LoadThings2
|
||||
//
|
||||
// Same as P_LoadThings() except it assumes Things are
|
||||
// saved Hexen-style. Position also controls which single-
|
||||
// player start spots are spawned by filtering out those
|
||||
// whose first parameter don't match position.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapLoader::LoadThings2 (MapData * map)
|
||||
{
|
||||
int lumplen = map->Size(ML_THINGS);
|
||||
int numthings = lumplen / sizeof(mapthinghexen_t);
|
||||
|
||||
|
||||
MapThingsConverted.Resize(numthings);
|
||||
FMapThing *mti = &MapThingsConverted[0];
|
||||
|
||||
TArray<uint8_t> mtp;
|
||||
map->Read(ML_THINGS, mtp.Data());
|
||||
auto mth = (mapthinghexen_t*)mtp.Data();
|
||||
|
||||
for(int i = 0; i< numthings; i++)
|
||||
{
|
||||
memset (&mti[i], 0, sizeof(mti[i]));
|
||||
|
||||
mti[i].thingid = LittleShort(mth[i].thingid);
|
||||
mti[i].pos.X = LittleShort(mth[i].x);
|
||||
mti[i].pos.Y = LittleShort(mth[i].y);
|
||||
mti[i].pos.Z = LittleShort(mth[i].z);
|
||||
mti[i].angle = LittleShort(mth[i].angle);
|
||||
mti[i].EdNum = LittleShort(mth[i].type);
|
||||
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
|
||||
mti[i].flags = LittleShort(mth[i].flags);
|
||||
mti[i].special = mth[i].special;
|
||||
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
|
||||
mti[i].SkillFilter = MakeSkill(mti[i].flags);
|
||||
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
|
||||
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
|
||||
if (level->flags2 & LEVEL2_HEXENHACK)
|
||||
{
|
||||
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
|
||||
}
|
||||
|
||||
mti[i].Gravity = 1;
|
||||
mti[i].RenderStyle = STYLE_Count;
|
||||
mti[i].Alpha = -1;
|
||||
mti[i].Health = 1;
|
||||
mti[i].FloatbobPhase = -1;
|
||||
mti[i].friendlyseeblocks = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -55,6 +55,12 @@ public:
|
|||
TArray<node_t> &gamenodes;
|
||||
TArray<line_t*>linebuffer;
|
||||
FBlockmap &blockmap;
|
||||
TArray<uint8_t> &rejectmatrix;
|
||||
TArray<zone_t> &Zones;
|
||||
|
||||
TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
|
||||
TArray<FUDMFKey> MapThingsUserData;
|
||||
TArray<FMapThing> MapThingsConverted;
|
||||
|
||||
const int *oldvertextable = nullptr;
|
||||
int firstglvertex = -1;
|
||||
|
@ -119,8 +125,8 @@ public:
|
|||
void CopyPlane (int tag, const DVector2 &pos, bool copyCeil);
|
||||
void SetSlope (secplane_t *plane, bool setCeil, int xyangi, int zangi, const DVector3 &pos);
|
||||
void VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int which);
|
||||
void SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
|
||||
void SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
|
||||
void SetSlopesFromVertexHeights();
|
||||
void SpawnSlopeMakers ();
|
||||
void AlignPlane(sector_t *sec, line_t *line, int which);
|
||||
void SetSlopes ();
|
||||
void CopySlopes();
|
||||
|
@ -138,7 +144,9 @@ public:
|
|||
gamesubsectors(store.gamesubsectors),
|
||||
gamenodes(store.gamenodes),
|
||||
linebuffer(store.linebuffer),
|
||||
blockmap(store.blockmap)
|
||||
blockmap(store.blockmap),
|
||||
rejectmatrix(store.rejectmatrix),
|
||||
Zones(store.Zones)
|
||||
{
|
||||
tagManager = tm;
|
||||
}
|
||||
|
@ -156,8 +164,17 @@ public:
|
|||
void LoadSideDefs2 (MapData *map, FMissingTextureTracker &missingtex);
|
||||
void LoopSidedefs (bool firstloop);
|
||||
void GroupLines(bool buildmap);
|
||||
void LoadThings (MapData * map);
|
||||
void LoadThings2 (MapData * map);
|
||||
|
||||
bool LoadBsp(MapData *map);
|
||||
void LoadReject (MapData * map);
|
||||
void FloodZone (sector_t *sec, int zonenum);
|
||||
void FloodZones ();
|
||||
void SummarizeMissingTextures(const FMissingTextureTracker &missing);
|
||||
void SetRenderSector();
|
||||
void FixMinisegReferences();
|
||||
void FixHoles();
|
||||
|
||||
void ParseTextMap(MapData *map, FMissingTextureTracker &missingtex);
|
||||
|
||||
|
|
580
src/maploader/maploader_misc.cpp
Normal file
580
src/maploader/maploader_misc.cpp
Normal file
|
@ -0,0 +1,580 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright 1993-1996 id Software
|
||||
// Copyright 1994-1996 Raven Software
|
||||
// Copyright 1999-2016 Randy Heit
|
||||
// Copyright 2002-2018 Christoph Oelckers
|
||||
//
|
||||
// This program 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 3 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, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Map loader
|
||||
//
|
||||
|
||||
#include "p_setup.h"
|
||||
#include "maploader.h"
|
||||
#include "mapdata.h"
|
||||
#include "files.h"
|
||||
#include "i_system.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "r_sky.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "p_tags.h"
|
||||
#include "v_text.h"
|
||||
#include "doomstat.h"
|
||||
#include "edata.h"
|
||||
#include "w_wad.h"
|
||||
#include "actor.h"
|
||||
#include "gi.h"
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapLoader::LoadReject (MapData * map)
|
||||
{
|
||||
const int neededsize = (sectors.Size() * sectors.Size() + 7) >> 3;
|
||||
int rejectsize;
|
||||
|
||||
if (!map->CheckName(ML_REJECT, "REJECT"))
|
||||
{
|
||||
rejectsize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rejectsize = map->Size(ML_REJECT);
|
||||
}
|
||||
|
||||
if (rejectsize < neededsize)
|
||||
{
|
||||
if (rejectsize > 0)
|
||||
{
|
||||
Printf ("REJECT is %d byte%s too small.\n", neededsize - rejectsize,
|
||||
neededsize-rejectsize==1?"":"s");
|
||||
}
|
||||
rejectmatrix.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the reject has some actual content. If not, free it.
|
||||
rejectsize = MIN (rejectsize, neededsize);
|
||||
rejectmatrix.Alloc(rejectsize);
|
||||
|
||||
map->Read (ML_REJECT, &rejectmatrix[0], rejectsize);
|
||||
|
||||
int qwords = rejectsize / 8;
|
||||
int i;
|
||||
|
||||
if (qwords > 0)
|
||||
{
|
||||
const uint64_t *qreject = (const uint64_t *)&rejectmatrix[0];
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
if (qreject[i] != 0)
|
||||
return;
|
||||
} while (++i < qwords);
|
||||
}
|
||||
rejectsize &= 7;
|
||||
qwords *= 8;
|
||||
for (i = 0; i < rejectsize; ++i)
|
||||
{
|
||||
if (rejectmatrix[qwords + i] != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Reject has no data, so pretend it isn't there.
|
||||
rejectmatrix.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Sound enviroment handling
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapLoader::FloodZone (sector_t *sec, int zonenum)
|
||||
{
|
||||
if (sec->ZoneNumber == zonenum)
|
||||
return;
|
||||
|
||||
sec->ZoneNumber = zonenum;
|
||||
|
||||
for (auto check : sec->Lines)
|
||||
{
|
||||
sector_t *other;
|
||||
|
||||
if (check->sidedef[1] == nullptr || (check->flags & ML_ZONEBOUNDARY))
|
||||
continue;
|
||||
|
||||
if (check->frontsector == sec)
|
||||
{
|
||||
assert(check->backsector != nullptr);
|
||||
other = check->backsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(check->frontsector != nullptr);
|
||||
other = check->frontsector;
|
||||
}
|
||||
|
||||
if (other->ZoneNumber != zonenum)
|
||||
FloodZone (other, zonenum);
|
||||
}
|
||||
}
|
||||
|
||||
void MapLoader::FloodZones ()
|
||||
{
|
||||
int z = 0, i;
|
||||
ReverbContainer *reverb;
|
||||
|
||||
for (auto &sec : sectors)
|
||||
{
|
||||
if (sec.ZoneNumber == 0xFFFF)
|
||||
{
|
||||
FloodZone (&sec, z++);
|
||||
}
|
||||
}
|
||||
Zones.Resize(z);
|
||||
reverb = S_FindEnvironment(level->DefaultEnvironment);
|
||||
if (reverb == NULL)
|
||||
{
|
||||
Printf("Sound environment %d, %d not found\n", level->DefaultEnvironment >> 8, level->DefaultEnvironment & 255);
|
||||
reverb = DefaultEnvironments[0];
|
||||
}
|
||||
for (i = 0; i < z; ++i)
|
||||
{
|
||||
Zones[i].Environment = reverb;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Keep both the original nodes from the WAD and the GL nodes created here.
|
||||
// The original set is only being used to get the sector for in-game
|
||||
// positioning of actors but not for rendering.
|
||||
//
|
||||
// This is necessary because ZDBSP is much more sensitive
|
||||
// to sloppy mapping practices that produce overlapping sectors.
|
||||
// The crane in P:AR E1M3 is a good example that would be broken if
|
||||
// this wasn't done.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PointOnLine
|
||||
//
|
||||
// Same as the one im the node builder, but not part of a specific class
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool PointOnLine (int x, int y, int x1, int y1, int dx, int dy)
|
||||
{
|
||||
const double SIDE_EPSILON = 6.5536;
|
||||
|
||||
// For most cases, a simple dot product is enough.
|
||||
double d_dx = double(dx);
|
||||
double d_dy = double(dy);
|
||||
double d_x = double(x);
|
||||
double d_y = double(y);
|
||||
double d_x1 = double(x1);
|
||||
double d_y1 = double(y1);
|
||||
|
||||
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
|
||||
|
||||
if (fabs(s_num) < 17179869184.0) // 4<<32
|
||||
{
|
||||
// Either the point is very near the line, or the segment defining
|
||||
// the line is very short: Do a more expensive test to determine
|
||||
// just how far from the line the point is.
|
||||
double l = g_sqrt(d_dx*d_dx+d_dy*d_dy);
|
||||
double dist = fabs(s_num)/l;
|
||||
if (dist < SIDE_EPSILON)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SetRenderSector
|
||||
//
|
||||
// Sets the render sector for each GL subsector so that the proper flat
|
||||
// information can be retrieved
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void MapLoader::SetRenderSector()
|
||||
{
|
||||
int i;
|
||||
uint32_t j;
|
||||
TArray<subsector_t *> undetermined;
|
||||
subsector_t * ss;
|
||||
|
||||
#if 0 // doesn't work as expected :(
|
||||
|
||||
// hide all sectors on textured automap that only have hidden lines.
|
||||
bool *hidesec = new bool[numsectors];
|
||||
for(i = 0; i < numsectors; i++)
|
||||
{
|
||||
hidesec[i] = true;
|
||||
}
|
||||
for(i = 0; i < numlines; i++)
|
||||
{
|
||||
if (!(lines[i].flags & ML_DONTDRAW))
|
||||
{
|
||||
hidesec[lines[i].frontsector - sectors] = false;
|
||||
if (lines[i].backsector != NULL)
|
||||
{
|
||||
hidesec[lines[i].backsector - sectors] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i = 0; i < numsectors; i++)
|
||||
{
|
||||
if (hidesec[i]) sectors[i].MoreFlags |= SECMF_HIDDEN;
|
||||
}
|
||||
delete [] hidesec;
|
||||
#endif
|
||||
|
||||
// Check for incorrect partner seg info so that the following code does not crash.
|
||||
|
||||
for (auto &seg : segs)
|
||||
{
|
||||
auto p = seg.PartnerSeg;
|
||||
if (p != nullptr)
|
||||
{
|
||||
int partner = p->Index();
|
||||
|
||||
if (partner < 0 || partner >= (int)segs.Size() || &segs[partner] != p)
|
||||
{
|
||||
seg.PartnerSeg = nullptr;
|
||||
}
|
||||
|
||||
// glbsp creates such incorrect references for Strife.
|
||||
if (seg.linedef && seg.PartnerSeg != nullptr && !seg.PartnerSeg->linedef)
|
||||
{
|
||||
seg.PartnerSeg = seg.PartnerSeg->PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &seg : segs)
|
||||
{
|
||||
if (seg.PartnerSeg != nullptr && seg.PartnerSeg->PartnerSeg != &seg)
|
||||
{
|
||||
seg.PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// look up sector number for each subsector
|
||||
for (auto &ss : subsectors)
|
||||
{
|
||||
// For rendering pick the sector from the first seg that is a sector boundary
|
||||
// this takes care of self-referencing sectors
|
||||
seg_t *seg = ss.firstline;
|
||||
|
||||
// Check for one-dimensional subsectors. These should be ignored when
|
||||
// being processed for automap drawing etc.
|
||||
ss.flags |= SSECF_DEGENERATE;
|
||||
for(j=2; j<ss.numlines; j++)
|
||||
{
|
||||
if (!PointOnLine(seg[j].v1->fixX(), seg[j].v1->fixY(), seg->v1->fixX(), seg->v1->fixY(), seg->v2->fixX() -seg->v1->fixX(), seg->v2->fixY() -seg->v1->fixY()))
|
||||
{
|
||||
// Not on the same line
|
||||
ss.flags &= ~SSECF_DEGENERATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seg = ss.firstline;
|
||||
for(j=0; j<ss.numlines; j++)
|
||||
{
|
||||
if(seg->sidedef && (seg->PartnerSeg == nullptr || (seg->PartnerSeg->sidedef != nullptr && seg->sidedef->sector!=seg->PartnerSeg->sidedef->sector)))
|
||||
{
|
||||
ss.render_sector = seg->sidedef->sector;
|
||||
break;
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
if(ss.render_sector == NULL)
|
||||
{
|
||||
undetermined.Push(&ss);
|
||||
}
|
||||
}
|
||||
|
||||
// assign a vaild render sector to all subsectors which haven't been processed yet.
|
||||
while (undetermined.Size())
|
||||
{
|
||||
bool deleted=false;
|
||||
for(i=undetermined.Size()-1;i>=0;i--)
|
||||
{
|
||||
ss=undetermined[i];
|
||||
seg_t * seg = ss->firstline;
|
||||
|
||||
for(j=0; j<ss->numlines; j++)
|
||||
{
|
||||
if (seg->PartnerSeg != nullptr && seg->PartnerSeg->Subsector)
|
||||
{
|
||||
sector_t * backsec = seg->PartnerSeg->Subsector->render_sector;
|
||||
if (backsec)
|
||||
{
|
||||
ss->render_sector = backsec;
|
||||
undetermined.Delete(i);
|
||||
deleted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
}
|
||||
// We still got some left but the loop above was unable to assign them.
|
||||
// This only happens when a subsector is off the map.
|
||||
// Don't bother and just assign the real sector for rendering
|
||||
if (!deleted && undetermined.Size())
|
||||
{
|
||||
for(i=undetermined.Size()-1;i>=0;i--)
|
||||
{
|
||||
ss=undetermined[i];
|
||||
ss->render_sector=ss->sector;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FixMinisegReferences
|
||||
//
|
||||
// Sometimes it can happen that two matching minisegs do not have their partner set.
|
||||
// Fix that here.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void MapLoader::FixMinisegReferences()
|
||||
{
|
||||
TArray<seg_t *> bogussegs;
|
||||
|
||||
for (unsigned i = 0; i < segs.Size(); i++)
|
||||
{
|
||||
if (segs[i].sidedef == nullptr && segs[i].PartnerSeg == nullptr)
|
||||
{
|
||||
bogussegs.Push(&segs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < bogussegs.Size(); i++)
|
||||
{
|
||||
auto seg1 = bogussegs[i];
|
||||
seg_t *pick = nullptr;
|
||||
unsigned int picki = -1;
|
||||
|
||||
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
|
||||
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
|
||||
{
|
||||
auto seg2 = bogussegs[j];
|
||||
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
|
||||
{
|
||||
pick = seg2;
|
||||
picki = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pick)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
|
||||
pick->PartnerSeg = seg1;
|
||||
seg1->PartnerSeg = pick;
|
||||
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
|
||||
bogussegs.Delete(picki);
|
||||
bogussegs.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FixHoles
|
||||
//
|
||||
// ZDBSP can leave holes in the node tree on extremely detailed maps.
|
||||
// To help out the triangulator these are filled with dummy subsectors
|
||||
// so that it can process the area correctly.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void MapLoader::FixHoles()
|
||||
{
|
||||
TArray<seg_t *> bogussegs;
|
||||
TArray<TArray<seg_t *>> segloops;
|
||||
|
||||
for (unsigned i = 0; i < segs.Size(); i++)
|
||||
{
|
||||
if (segs[i].sidedef == nullptr && segs[i].PartnerSeg == nullptr)
|
||||
{
|
||||
bogussegs.Push(&segs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
while (bogussegs.Size() > 0)
|
||||
{
|
||||
segloops.Reserve(1);
|
||||
auto *segloop = &segloops.Last();
|
||||
|
||||
seg_t *startseg;
|
||||
seg_t *checkseg;
|
||||
while (bogussegs.Size() > 0)
|
||||
{
|
||||
bool foundsome = false;
|
||||
if (segloop->Size() == 0)
|
||||
{
|
||||
bogussegs.Pop(startseg);
|
||||
segloop->Push(startseg);
|
||||
checkseg = startseg;
|
||||
}
|
||||
for (unsigned i = 0; i < bogussegs.Size(); i++)
|
||||
{
|
||||
auto seg1 = bogussegs[i];
|
||||
|
||||
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
|
||||
{
|
||||
foundsome = true;
|
||||
segloop->Push(seg1);
|
||||
bogussegs.Delete(i);
|
||||
i--;
|
||||
checkseg = seg1;
|
||||
|
||||
if (seg1->v2 == startseg->v1)
|
||||
{
|
||||
// The loop is complete. Start a new one
|
||||
segloops.Reserve(1);
|
||||
segloop = &segloops.Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundsome)
|
||||
{
|
||||
if ((*segloop)[0]->v1 != segloop->Last()->v2)
|
||||
{
|
||||
// There was no connected seg, leaving an unclosed loop.
|
||||
// Clear this and continue looking.
|
||||
segloop->Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segloops.Size(); i++)
|
||||
{
|
||||
if (segloops[i].Size() == 0)
|
||||
{
|
||||
segloops.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// Add dummy entries to the level's seg and subsector arrays
|
||||
if (segloops.Size() > 0)
|
||||
{
|
||||
// cound the number of segs to add.
|
||||
unsigned segcount = 0;
|
||||
for (auto &segloop : segloops)
|
||||
segcount += segloop.Size();
|
||||
|
||||
seg_t *oldsegstartptr = &segs[0];
|
||||
subsector_t *oldssstartptr = &subsectors[0];
|
||||
|
||||
unsigned newsegstart = segs.Reserve(segcount);
|
||||
unsigned newssstart = subsectors.Reserve(segloops.Size());
|
||||
|
||||
seg_t *newsegstartptr = &segs[0];
|
||||
subsector_t *newssstartptr = &subsectors[0];
|
||||
|
||||
// Now fix all references to these in the level data.
|
||||
// Note that the Index() method does not work here due to the reallocation.
|
||||
for (auto &seg : segs)
|
||||
{
|
||||
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
|
||||
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
|
||||
}
|
||||
for (auto &sub : subsectors)
|
||||
{
|
||||
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
|
||||
}
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
// How hideous... :(
|
||||
for (auto & p : node.children)
|
||||
{
|
||||
auto intp = (intptr_t)p;
|
||||
if (intp & 1)
|
||||
{
|
||||
subsector_t *sp = (subsector_t*)(intp - 1);
|
||||
sp = newssstartptr + (sp - oldssstartptr);
|
||||
intp = intptr_t(sp) + 1;
|
||||
p = (void*)intp;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &segloop : segloops)
|
||||
{
|
||||
for (auto &seg : segloop)
|
||||
{
|
||||
seg = newsegstartptr + (seg - oldsegstartptr);
|
||||
}
|
||||
}
|
||||
|
||||
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
|
||||
|
||||
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
|
||||
for (auto &segloop : segloops)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
|
||||
|
||||
subsector_t &sub = subsectors[newssstart++];
|
||||
memset(&sub, 0, sizeof(sub));
|
||||
sub.sector = segloop[0]->frontsector;
|
||||
sub.render_sector = segloop[0]->Subsector->render_sector;
|
||||
sub.numlines = segloop.Size();
|
||||
sub.firstline = &segs[newsegstart];
|
||||
sub.flags = SSECF_HOLE;
|
||||
|
||||
for (auto otherseg : segloop)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
|
||||
seg_t &seg = segs[newsegstart++];
|
||||
memset(&seg, 0, sizeof(seg));
|
||||
seg.v1 = otherseg->v2;
|
||||
seg.v2 = otherseg->v1;
|
||||
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
|
||||
seg.PartnerSeg = otherseg;
|
||||
otherseg->PartnerSeg = &seg;
|
||||
seg.Subsector = ⊂
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -276,14 +276,15 @@ void MapLoader::VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int whi
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MapLoader::SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
|
||||
void MapLoader::SetSlopesFromVertexHeights()
|
||||
{
|
||||
TMap<int, double> vt_heights[2];
|
||||
FMapThing *mt;
|
||||
bool vt_found = false;
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
{
|
||||
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
|
||||
{
|
||||
auto mt = &MapThingsConverted[i];
|
||||
if (mt->info != nullptr && mt->info->Type == nullptr)
|
||||
{
|
||||
if (mt->info->Special == SMT_VertexFloorZ || mt->info->Special == SMT_VertexCeilingZ)
|
||||
|
@ -401,12 +402,11 @@ void MapLoader::SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
|
||||
void MapLoader::SpawnSlopeMakers ()
|
||||
{
|
||||
FMapThing *mt;
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
|
||||
{
|
||||
auto mt = &MapThingsConverted[i];
|
||||
if (mt->info != nullptr && mt->info->Type == nullptr &&
|
||||
(mt->info->Special >= SMT_SlopeFloorPointLine && mt->info->Special <= SMT_VavoomCeiling))
|
||||
{
|
||||
|
@ -444,8 +444,9 @@ void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const i
|
|||
}
|
||||
}
|
||||
|
||||
for (mt = firstmt; mt < lastmt; ++mt)
|
||||
{
|
||||
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
|
||||
{
|
||||
auto mt = &MapThingsConverted[i];
|
||||
if (mt->info != nullptr && mt->info->Type == nullptr &&
|
||||
(mt->info->Special == SMT_CopyFloorPlane || mt->info->Special == SMT_CopyCeilingPlane))
|
||||
{
|
||||
|
@ -454,7 +455,7 @@ void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const i
|
|||
}
|
||||
}
|
||||
|
||||
SetSlopesFromVertexHeights(firstmt, lastmt, oldvertextable);
|
||||
SetSlopesFromVertexHeights();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SpawnMapThing
|
||||
//
|
||||
//===========================================================================
|
||||
CVAR(Bool, dumpspawnedthings, false, 0)
|
||||
|
||||
AActor *SpawnMapThing(int index, FMapThing *mt, int position)
|
||||
{
|
||||
AActor *spawned = P_SpawnMapThing(mt, position);
|
||||
if (dumpspawnedthings)
|
||||
{
|
||||
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
|
||||
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
|
||||
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
|
||||
}
|
||||
T_AddSpawnedThing(spawned);
|
||||
return spawned;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SetMapThingUserData
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static void SetMapThingUserData(AActor *actor, unsigned udi)
|
||||
{
|
||||
if (actor == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (MapThingsUserData[udi].Key != NAME_None)
|
||||
{
|
||||
FName varname = MapThingsUserData[udi].Key;
|
||||
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
|
||||
|
||||
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
|
||||
{
|
||||
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
|
||||
actor->GetClass()->TypeName.GetChars());
|
||||
}
|
||||
else
|
||||
{ // Set the value of the specified user variable.
|
||||
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
|
||||
if (var->Type == TypeString)
|
||||
{
|
||||
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
|
||||
}
|
||||
else if (var->Type->isFloat())
|
||||
{
|
||||
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
|
||||
}
|
||||
else if (var->Type->isInt() || var->Type == TypeBool)
|
||||
{
|
||||
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
|
||||
}
|
||||
}
|
||||
|
||||
udi++;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// P_LoadThings
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
uint16_t MakeSkill(int flags)
|
||||
{
|
||||
uint16_t res = 0;
|
||||
if (flags & 1) res |= 1+2;
|
||||
if (flags & 2) res |= 4;
|
||||
if (flags & 4) res |= 8+16;
|
||||
return res;
|
||||
}
|
||||
|
||||
void P_LoadThings (MapData * map)
|
||||
{
|
||||
int lumplen = map->Size(ML_THINGS);
|
||||
int numthings = lumplen / sizeof(mapthing_t);
|
||||
|
||||
char *mtp;
|
||||
mapthing_t *mt;
|
||||
|
||||
mtp = new char[lumplen];
|
||||
map->Read(ML_THINGS, mtp);
|
||||
mt = (mapthing_t*)mtp;
|
||||
|
||||
MapThingsConverted.Resize(numthings);
|
||||
FMapThing *mti = &MapThingsConverted[0];
|
||||
|
||||
// [RH] ZDoom now uses Hexen-style maps as its native format.
|
||||
// Since this is the only place where Doom-style Things are ever
|
||||
// referenced, we translate them into a Hexen-style thing.
|
||||
for (int i=0 ; i < numthings; i++, mt++)
|
||||
{
|
||||
// [RH] At this point, monsters unique to Doom II were weeded out
|
||||
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
|
||||
// handle these and more cases better, so we just pass it
|
||||
// everything and let it decide what to do with them.
|
||||
|
||||
// [RH] Need to translate the spawn flags to Hexen format.
|
||||
short flags = LittleShort(mt->options);
|
||||
|
||||
memset (&mti[i], 0, sizeof(mti[i]));
|
||||
|
||||
mti[i].Gravity = 1;
|
||||
mti[i].Conversation = 0;
|
||||
mti[i].SkillFilter = MakeSkill(flags);
|
||||
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
|
||||
mti[i].RenderStyle = STYLE_Count;
|
||||
mti[i].Alpha = -1;
|
||||
mti[i].Health = 1;
|
||||
mti[i].FloatbobPhase = -1;
|
||||
|
||||
mti[i].pos.X = LittleShort(mt->x);
|
||||
mti[i].pos.Y = LittleShort(mt->y);
|
||||
mti[i].angle = LittleShort(mt->angle);
|
||||
mti[i].EdNum = LittleShort(mt->type);
|
||||
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
|
||||
|
||||
|
||||
#ifndef NO_EDATA
|
||||
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
|
||||
{
|
||||
ProcessEDMapthing(&mti[i], flags);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
flags &= ~MTF_SKILLMASK;
|
||||
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
|
||||
if (gameinfo.gametype == GAME_Strife)
|
||||
{
|
||||
mti[i].flags &= ~MTF_AMBUSH;
|
||||
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
|
||||
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
|
||||
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
|
||||
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
|
||||
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags & BTF_BADEDITORCHECK)
|
||||
{
|
||||
flags &= 0x1F;
|
||||
}
|
||||
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
|
||||
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
|
||||
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
|
||||
}
|
||||
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
|
||||
}
|
||||
}
|
||||
delete [] mtp;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// [RH]
|
||||
// P_LoadThings2
|
||||
//
|
||||
// Same as P_LoadThings() except it assumes Things are
|
||||
// saved Hexen-style. Position also controls which single-
|
||||
// player start spots are spawned by filtering out those
|
||||
// whose first parameter don't match position.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_LoadThings2 (MapData * map)
|
||||
{
|
||||
int lumplen = map->Size(ML_THINGS);
|
||||
int numthings = lumplen / sizeof(mapthinghexen_t);
|
||||
|
||||
char *mtp;
|
||||
|
||||
MapThingsConverted.Resize(numthings);
|
||||
FMapThing *mti = &MapThingsConverted[0];
|
||||
|
||||
mtp = new char[lumplen];
|
||||
map->Read(ML_THINGS, mtp);
|
||||
mapthinghexen_t *mth = (mapthinghexen_t*)mtp;
|
||||
|
||||
for(int i = 0; i< numthings; i++)
|
||||
{
|
||||
memset (&mti[i], 0, sizeof(mti[i]));
|
||||
|
||||
mti[i].thingid = LittleShort(mth[i].thingid);
|
||||
mti[i].pos.X = LittleShort(mth[i].x);
|
||||
mti[i].pos.Y = LittleShort(mth[i].y);
|
||||
mti[i].pos.Z = LittleShort(mth[i].z);
|
||||
mti[i].angle = LittleShort(mth[i].angle);
|
||||
mti[i].EdNum = LittleShort(mth[i].type);
|
||||
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
|
||||
mti[i].flags = LittleShort(mth[i].flags);
|
||||
mti[i].special = mth[i].special;
|
||||
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
|
||||
mti[i].SkillFilter = MakeSkill(mti[i].flags);
|
||||
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
|
||||
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
|
||||
if (level.flags2 & LEVEL2_HEXENHACK)
|
||||
{
|
||||
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
|
||||
}
|
||||
|
||||
mti[i].Gravity = 1;
|
||||
mti[i].RenderStyle = STYLE_Count;
|
||||
mti[i].Alpha = -1;
|
||||
mti[i].Health = 1;
|
||||
mti[i].FloatbobPhase = -1;
|
||||
mti[i].friendlyseeblocks = -1;
|
||||
}
|
||||
delete[] mtp;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_SpawnThings (int position)
|
||||
{
|
||||
int numthings = MapThingsConverted.Size();
|
||||
|
||||
for (int i=0; i < numthings; i++)
|
||||
{
|
||||
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
|
||||
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
|
||||
if (udi != NULL)
|
||||
{
|
||||
SetMapThingUserData(actor, *udi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright 1993-1996 id Software
|
||||
// Copyright 1994-1996 Raven Software
|
||||
|
@ -98,6 +98,8 @@
|
|||
#include "events.h"
|
||||
#include "actorinlines.h"
|
||||
#include "a_dynlight.h"
|
||||
#include "fragglescript/t_fs.h"
|
||||
#include "types.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
@ -5791,13 +5793,14 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
|
|||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// P_SpawnMapThing
|
||||
// The fields of the mapthing should
|
||||
// already be in host byte order.
|
||||
//
|
||||
// [RH] position is used to weed out unwanted start spots
|
||||
AActor *P_SpawnMapThing (FMapThing *mthing, int position)
|
||||
static AActor *P_SpawnMapThing (FMapThing *mthing, int position)
|
||||
{
|
||||
PClassActor *i;
|
||||
int mask;
|
||||
|
@ -6193,6 +6196,94 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
|
|||
return mobj;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SpawnMapThing
|
||||
//
|
||||
//===========================================================================
|
||||
CVAR(Bool, dumpspawnedthings, false, 0)
|
||||
|
||||
static AActor *SpawnMapThing(int index, FMapThing *mt, int position)
|
||||
{
|
||||
AActor *spawned = P_SpawnMapThing(mt, position);
|
||||
if (dumpspawnedthings)
|
||||
{
|
||||
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
|
||||
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
|
||||
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
|
||||
}
|
||||
T_AddSpawnedThing(spawned);
|
||||
return spawned;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SetMapThingUserData
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static void SetMapThingUserData(AActor *actor, unsigned udi, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex)
|
||||
{
|
||||
if (actor == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (MapThingsUserData[udi].Key != NAME_None)
|
||||
{
|
||||
FName varname = MapThingsUserData[udi].Key;
|
||||
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
|
||||
|
||||
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
|
||||
{
|
||||
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
|
||||
actor->GetClass()->TypeName.GetChars());
|
||||
}
|
||||
else
|
||||
{ // Set the value of the specified user variable.
|
||||
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
|
||||
if (var->Type == TypeString)
|
||||
{
|
||||
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
|
||||
}
|
||||
else if (var->Type->isFloat())
|
||||
{
|
||||
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
|
||||
}
|
||||
else if (var->Type->isInt() || var->Type == TypeBool)
|
||||
{
|
||||
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
|
||||
}
|
||||
}
|
||||
|
||||
udi++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_SpawnThings (TArray<FMapThing> &MapThingsConverted, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex, int position)
|
||||
{
|
||||
int numthings = MapThingsConverted.Size();
|
||||
|
||||
for (int i=0; i < numthings; i++)
|
||||
{
|
||||
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
|
||||
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
|
||||
if (udi != NULL)
|
||||
{
|
||||
SetMapThingUserData(actor, *udi, MapThingsUserData, MapThingsUserDataIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
|
226
src/p_setup.cpp
226
src/p_setup.cpp
|
@ -116,7 +116,6 @@ void BloodCrypt (void *data, int key, int len);
|
|||
void P_ClearUDMFKeys();
|
||||
void InitRenderInfo();
|
||||
|
||||
extern AActor *P_SpawnMapThing (FMapThing *mthing, int position);
|
||||
|
||||
extern void P_TranslateTeleportThings (void);
|
||||
|
||||
|
@ -140,6 +139,8 @@ inline bool P_LoadBuildMap(uint8_t *mapdata, size_t len, FMapThing **things, int
|
|||
return false;
|
||||
}
|
||||
|
||||
void P_SpawnThings (TArray<FMapThing> &MapThingsConverted, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex, int position);
|
||||
|
||||
|
||||
//
|
||||
// MAP related Lookup tables.
|
||||
|
@ -149,164 +150,8 @@ TArray<vertexdata_t> vertexdatas;
|
|||
|
||||
bool hasglnodes;
|
||||
|
||||
TArray<FMapThing> MapThingsConverted;
|
||||
TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
|
||||
TArray<FUDMFKey> MapThingsUserData;
|
||||
|
||||
static void P_AllocateSideDefs (MapData *map, int count);
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SummarizeMissingTextures
|
||||
//
|
||||
// Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
|
||||
// times.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static void SummarizeMissingTextures(const FMissingTextureTracker &missing)
|
||||
{
|
||||
FMissingTextureTracker::ConstIterator it(missing);
|
||||
FMissingTextureTracker::ConstPair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
if (pair->Value.Count > MISSING_TEXTURE_WARN_LIMIT)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Missing texture '"
|
||||
TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
|
||||
"' is used %d more times\n",
|
||||
pair->Key.GetChars(), pair->Value.Count - MISSING_TEXTURE_WARN_LIMIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Sound enviroment handling
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_FloodZone (sector_t *sec, int zonenum)
|
||||
{
|
||||
if (sec->ZoneNumber == zonenum)
|
||||
return;
|
||||
|
||||
sec->ZoneNumber = zonenum;
|
||||
|
||||
for (auto check : sec->Lines)
|
||||
{
|
||||
sector_t *other;
|
||||
|
||||
if (check->sidedef[1] == NULL || (check->flags & ML_ZONEBOUNDARY))
|
||||
continue;
|
||||
|
||||
if (check->frontsector == sec)
|
||||
{
|
||||
assert(check->backsector != NULL);
|
||||
other = check->backsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(check->frontsector != NULL);
|
||||
other = check->frontsector;
|
||||
}
|
||||
|
||||
if (other->ZoneNumber != zonenum)
|
||||
P_FloodZone (other, zonenum);
|
||||
}
|
||||
}
|
||||
|
||||
void P_FloodZones ()
|
||||
{
|
||||
int z = 0, i;
|
||||
ReverbContainer *reverb;
|
||||
|
||||
for (auto &sec : level.sectors)
|
||||
{
|
||||
if (sec.ZoneNumber == 0xFFFF)
|
||||
{
|
||||
P_FloodZone (&sec, z++);
|
||||
}
|
||||
}
|
||||
level.Zones.Resize(z);
|
||||
reverb = S_FindEnvironment(level.DefaultEnvironment);
|
||||
if (reverb == NULL)
|
||||
{
|
||||
Printf("Sound environment %d, %d not found\n", level.DefaultEnvironment >> 8, level.DefaultEnvironment & 255);
|
||||
reverb = DefaultEnvironments[0];
|
||||
}
|
||||
for (i = 0; i < z; ++i)
|
||||
{
|
||||
level.Zones[i].Environment = reverb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_LoadReject (MapData * map, bool junk)
|
||||
{
|
||||
const int neededsize = (level.sectors.Size() * level.sectors.Size() + 7) >> 3;
|
||||
int rejectsize;
|
||||
|
||||
if (!map->CheckName(ML_REJECT, "REJECT"))
|
||||
{
|
||||
rejectsize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rejectsize = junk ? 0 : map->Size(ML_REJECT);
|
||||
}
|
||||
|
||||
if (rejectsize < neededsize)
|
||||
{
|
||||
if (rejectsize > 0)
|
||||
{
|
||||
Printf ("REJECT is %d byte%s too small.\n", neededsize - rejectsize,
|
||||
neededsize-rejectsize==1?"":"s");
|
||||
}
|
||||
level.rejectmatrix.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the reject has some actual content. If not, free it.
|
||||
rejectsize = MIN (rejectsize, neededsize);
|
||||
level.rejectmatrix.Alloc(rejectsize);
|
||||
|
||||
map->Read (ML_REJECT, &level.rejectmatrix[0], rejectsize);
|
||||
|
||||
int qwords = rejectsize / 8;
|
||||
int i;
|
||||
|
||||
if (qwords > 0)
|
||||
{
|
||||
const uint64_t *qreject = (const uint64_t *)&level.rejectmatrix[0];
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
if (qreject[i] != 0)
|
||||
return;
|
||||
} while (++i < qwords);
|
||||
}
|
||||
rejectsize &= 7;
|
||||
qwords *= 8;
|
||||
for (i = 0; i < rejectsize; ++i)
|
||||
{
|
||||
if (level.rejectmatrix[qwords + i] != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Reject has no data, so pretend it isn't there.
|
||||
level.rejectmatrix.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
|
@ -439,9 +284,6 @@ void P_FreeLevelData ()
|
|||
|
||||
// [ZZ] delete per-map event handlers
|
||||
E_Shutdown(true);
|
||||
MapThingsConverted.Clear();
|
||||
MapThingsUserDataIndex.Clear();
|
||||
MapThingsUserData.Clear();
|
||||
FCanvasTextureInfo::EmptyList();
|
||||
R_FreePastViewers();
|
||||
P_ClearUDMFKeys();
|
||||
|
@ -569,12 +411,7 @@ void P_FreeExtraLevelData()
|
|||
void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
||||
{
|
||||
cycle_t times[20];
|
||||
#if 0
|
||||
FMapThing *buildthings;
|
||||
int numbuildthings;
|
||||
#endif
|
||||
int i;
|
||||
bool buildmap;
|
||||
|
||||
level.ShaderStartTime = I_msTimeFS(); // indicate to the shader system that the level just started
|
||||
|
||||
|
@ -672,10 +509,6 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
}
|
||||
|
||||
|
||||
// ----------------------------------------->
|
||||
MapLoader maploader(level, &tagManager);
|
||||
maploader.maptype = level.maptype;
|
||||
maploader.ForceNodeBuild = gennodes;
|
||||
|
||||
// note: most of this ordering is important
|
||||
|
||||
|
@ -713,10 +546,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
level.maptype = MAPTYPE_UDMF;
|
||||
}
|
||||
FName checksum = CheckCompatibility(map);
|
||||
if (ib_compatflags & BCOMPATF_REBUILDNODES)
|
||||
{
|
||||
maploader.ForceNodeBuild = true;
|
||||
}
|
||||
|
||||
T_LoadScripts(map);
|
||||
|
||||
|
||||
|
@ -727,6 +557,18 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
|
||||
|
||||
P_LoadStrifeConversations (map, lumpname);
|
||||
|
||||
// ----------------------------------------->
|
||||
|
||||
MapLoader maploader(level, &tagManager);
|
||||
maploader.maptype = level.maptype;
|
||||
maploader.ForceNodeBuild = gennodes;
|
||||
|
||||
if (ib_compatflags & BCOMPATF_REBUILDNODES)
|
||||
{
|
||||
maploader.ForceNodeBuild = true;
|
||||
}
|
||||
|
||||
|
||||
FMissingTextureTracker missingtex;
|
||||
|
||||
|
@ -745,9 +587,9 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
maploader.FinishLoadingLineDefs ();
|
||||
|
||||
if (!map->HasBehavior)
|
||||
P_LoadThings (map);
|
||||
maploader.LoadThings (map);
|
||||
else
|
||||
P_LoadThings2 (map); // [RH] Load Hexen-style things
|
||||
maploader.LoadThings2 (map); // [RH] Load Hexen-style things
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -759,34 +601,33 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
|
||||
maploader.LoopSidedefs (true);
|
||||
|
||||
SummarizeMissingTextures(missingtex);
|
||||
bool reloop = false;
|
||||
maploader.SummarizeMissingTextures(missingtex);
|
||||
|
||||
maploader.LoadBsp(map);
|
||||
|
||||
// set the head node for gameplay purposes. If the separate gamenodes array is not empty, use that, otherwise use the render nodes.
|
||||
level.headgamenode = level.gamenodes.Size() > 0 ? &level.gamenodes[level.gamenodes.Size() - 1] : level.nodes.Size()? &level.nodes[level.nodes.Size() - 1] : nullptr;
|
||||
bool reloop = maploader.LoadBsp(map);
|
||||
|
||||
maploader.LoadBlockMap (map);
|
||||
|
||||
P_LoadReject (map, buildmap);
|
||||
maploader.LoadReject (map);
|
||||
|
||||
maploader.GroupLines (buildmap);
|
||||
maploader.GroupLines (false);
|
||||
maploader.SetSlopes();
|
||||
|
||||
P_FloodZones ();
|
||||
maploader.FloodZones ();
|
||||
|
||||
P_SetRenderSector();
|
||||
FixMinisegReferences();
|
||||
FixHoles();
|
||||
maploader.SetRenderSector();
|
||||
maploader.FixMinisegReferences();
|
||||
maploader.FixHoles();
|
||||
|
||||
|
||||
// [RH] Spawn slope creating things first.
|
||||
maploader.SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], maploader.oldvertextable);
|
||||
maploader.SpawnSlopeMakers();
|
||||
maploader.CopySlopes();
|
||||
|
||||
//////////////////////// <-------------------------------------
|
||||
|
||||
// set the head node for gameplay purposes. If the separate gamenodes array is not empty, use that, otherwise use the render nodes.
|
||||
level.headgamenode = level.gamenodes.Size() > 0 ? &level.gamenodes[level.gamenodes.Size() - 1] : level.nodes.Size()? &level.nodes[level.nodes.Size() - 1] : nullptr;
|
||||
|
||||
// phares 8/10/98: Clear body queue so the corpses from previous games are
|
||||
// not assumed to be from this one.
|
||||
bodyqueslot = 0;
|
||||
|
@ -799,7 +640,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
P_Spawn3DFloors();
|
||||
|
||||
times[14].Clock();
|
||||
P_SpawnThings(position);
|
||||
P_SpawnThings(maploader.MapThingsConverted, maploader.MapThingsUserData, maploader.MapThingsUserDataIndex, position);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
|
@ -953,6 +794,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
P_ResetSightCounters (true);
|
||||
//Printf ("free memory: 0x%x\n", Z_FreeMemory());
|
||||
|
||||
/*
|
||||
if (showloadtimes)
|
||||
{
|
||||
Printf ("---Total load times---\n");
|
||||
|
@ -982,10 +824,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
Printf ("Time%3d:%9.4f ms (%s)\n", i, times[i].TimeMS(), timenames[i]);
|
||||
}
|
||||
}
|
||||
MapThingsConverted.Clear();
|
||||
MapThingsUserDataIndex.Clear();
|
||||
MapThingsUserData.Clear();
|
||||
|
||||
*/
|
||||
|
||||
// Create a backup of the map data so the savegame code can toss out all fields that haven't changed in order to reduce processing time and file size.
|
||||
// Note that we want binary identity here, so assignment is not sufficient because it won't initialize any padding bytes.
|
||||
// Note that none of these structures may contain non POD fields anyway.
|
||||
|
|
|
@ -56,16 +56,10 @@ int P_TranslateSectorSpecial (int);
|
|||
int GetUDMFInt(int type, int index, FName key);
|
||||
double GetUDMFFloat(int type, int index, FName key);
|
||||
|
||||
void P_SetRenderSector();
|
||||
void FixMinisegReferences();
|
||||
void FixHoles();
|
||||
void ReportUnpairedMinisegs();
|
||||
|
||||
|
||||
extern bool hasglnodes;
|
||||
|
||||
extern TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
|
||||
extern TArray<FUDMFKey> MapThingsUserData;
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -793,7 +793,7 @@ public:
|
|||
FUDMFKey ukey;
|
||||
ukey.Key = key;
|
||||
ReadUserKey(ukey);
|
||||
MapThingsUserData.Push(ukey);
|
||||
maploader->MapThingsUserData.Push(ukey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2132,17 +2132,17 @@ public:
|
|||
if (sc.Compare("thing"))
|
||||
{
|
||||
FMapThing th;
|
||||
unsigned userdatastart = MapThingsUserData.Size();
|
||||
unsigned userdatastart = maploader->MapThingsUserData.Size();
|
||||
ParseThing(&th);
|
||||
MapThingsConverted.Push(th);
|
||||
if (userdatastart < MapThingsUserData.Size())
|
||||
if (userdatastart < maploader->MapThingsUserData.Size())
|
||||
{ // User data added
|
||||
MapThingsUserDataIndex[MapThingsConverted.Size()-1] = userdatastart;
|
||||
maploader->MapThingsUserDataIndex[MapThingsConverted.Size()-1] = userdatastart;
|
||||
// Mark end of the user data for this map thing
|
||||
FUDMFKey ukey;
|
||||
ukey.Key = NAME_None;
|
||||
ukey = 0;
|
||||
MapThingsUserData.Push(ukey);
|
||||
maploader->MapThingsUserData.Push(ukey);
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("linedef"))
|
||||
|
|
|
@ -567,223 +567,9 @@ void InitRenderInfo()
|
|||
}
|
||||
delete[] checkmap;
|
||||
|
||||
#if 0
|
||||
gl_CreateSections();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FixMinisegReferences
|
||||
//
|
||||
// Sometimes it can happen that two matching minisegs do not have their partner set.
|
||||
// Fix that here.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FixMinisegReferences()
|
||||
{
|
||||
TArray<seg_t *> bogussegs;
|
||||
|
||||
for (unsigned i = 0; i < level.segs.Size(); i++)
|
||||
{
|
||||
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
|
||||
{
|
||||
bogussegs.Push(&level.segs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < bogussegs.Size(); i++)
|
||||
{
|
||||
auto seg1 = bogussegs[i];
|
||||
seg_t *pick = nullptr;
|
||||
unsigned int picki = -1;
|
||||
|
||||
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
|
||||
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
|
||||
{
|
||||
auto seg2 = bogussegs[j];
|
||||
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
|
||||
{
|
||||
pick = seg2;
|
||||
picki = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pick)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
|
||||
pick->PartnerSeg = seg1;
|
||||
seg1->PartnerSeg = pick;
|
||||
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
|
||||
bogussegs.Delete(picki);
|
||||
bogussegs.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FixHoles
|
||||
//
|
||||
// ZDBSP can leave holes in the node tree on extremely detailed maps.
|
||||
// To help out the triangulator these are filled with dummy subsectors
|
||||
// so that it can process the area correctly.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FixHoles()
|
||||
{
|
||||
TArray<seg_t *> bogussegs;
|
||||
TArray<TArray<seg_t *>> segloops;
|
||||
|
||||
for (unsigned i = 0; i < level.segs.Size(); i++)
|
||||
{
|
||||
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
|
||||
{
|
||||
bogussegs.Push(&level.segs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
while (bogussegs.Size() > 0)
|
||||
{
|
||||
segloops.Reserve(1);
|
||||
auto *segloop = &segloops.Last();
|
||||
|
||||
seg_t *startseg;
|
||||
seg_t *checkseg;
|
||||
while (bogussegs.Size() > 0)
|
||||
{
|
||||
bool foundsome = false;
|
||||
if (segloop->Size() == 0)
|
||||
{
|
||||
bogussegs.Pop(startseg);
|
||||
segloop->Push(startseg);
|
||||
checkseg = startseg;
|
||||
}
|
||||
for (unsigned i = 0; i < bogussegs.Size(); i++)
|
||||
{
|
||||
auto seg1 = bogussegs[i];
|
||||
|
||||
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
|
||||
{
|
||||
foundsome = true;
|
||||
segloop->Push(seg1);
|
||||
bogussegs.Delete(i);
|
||||
i--;
|
||||
checkseg = seg1;
|
||||
|
||||
if (seg1->v2 == startseg->v1)
|
||||
{
|
||||
// The loop is complete. Start a new one
|
||||
segloops.Reserve(1);
|
||||
segloop = &segloops.Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundsome)
|
||||
{
|
||||
if ((*segloop)[0]->v1 != segloop->Last()->v2)
|
||||
{
|
||||
// There was no connected seg, leaving an unclosed loop.
|
||||
// Clear this and continue looking.
|
||||
segloop->Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segloops.Size(); i++)
|
||||
{
|
||||
if (segloops[i].Size() == 0)
|
||||
{
|
||||
segloops.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// Add dummy entries to the level's seg and subsector arrays
|
||||
if (segloops.Size() > 0)
|
||||
{
|
||||
// cound the number of segs to add.
|
||||
unsigned segcount = 0;
|
||||
for (auto &segloop : segloops)
|
||||
segcount += segloop.Size();
|
||||
|
||||
seg_t *oldsegstartptr = &level.segs[0];
|
||||
subsector_t *oldssstartptr = &level.subsectors[0];
|
||||
|
||||
unsigned newsegstart = level.segs.Reserve(segcount);
|
||||
unsigned newssstart = level.subsectors.Reserve(segloops.Size());
|
||||
|
||||
seg_t *newsegstartptr = &level.segs[0];
|
||||
subsector_t *newssstartptr = &level.subsectors[0];
|
||||
|
||||
// Now fix all references to these in the level data.
|
||||
// Note that the Index() method does not work here due to the reallocation.
|
||||
for (auto &seg : level.segs)
|
||||
{
|
||||
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
|
||||
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
|
||||
}
|
||||
for (auto &sub : level.subsectors)
|
||||
{
|
||||
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
|
||||
}
|
||||
for (auto &node : level.nodes)
|
||||
{
|
||||
// How hideous... :(
|
||||
for (auto & p : node.children)
|
||||
{
|
||||
auto intp = (intptr_t)p;
|
||||
if (intp & 1)
|
||||
{
|
||||
subsector_t *sp = (subsector_t*)(intp - 1);
|
||||
sp = newssstartptr + (sp - oldssstartptr);
|
||||
intp = intptr_t(sp) + 1;
|
||||
p = (void*)intp;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &segloop : segloops)
|
||||
{
|
||||
for (auto &seg : segloop)
|
||||
{
|
||||
seg = newsegstartptr + (seg - oldsegstartptr);
|
||||
}
|
||||
}
|
||||
|
||||
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
|
||||
|
||||
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
|
||||
for (auto &segloop : segloops)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
|
||||
|
||||
subsector_t &sub = level.subsectors[newssstart++];
|
||||
memset(&sub, 0, sizeof(sub));
|
||||
sub.sector = segloop[0]->frontsector;
|
||||
sub.render_sector = segloop[0]->Subsector->render_sector;
|
||||
sub.numlines = segloop.Size();
|
||||
sub.firstline = &level.segs[newsegstart];
|
||||
sub.flags = SSECF_HOLE;
|
||||
|
||||
for (auto otherseg : segloop)
|
||||
{
|
||||
DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
|
||||
seg_t &seg = level.segs[newsegstart++];
|
||||
memset(&seg, 0, sizeof(seg));
|
||||
seg.v1 = otherseg->v2;
|
||||
seg.v2 = otherseg->v1;
|
||||
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
|
||||
seg.PartnerSeg = otherseg;
|
||||
otherseg->PartnerSeg = &seg;
|
||||
seg.Subsector = ⊂
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -837,202 +623,3 @@ CCMD(listbadminisegs)
|
|||
ReportUnpairedMinisegs();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Keep both the original nodes from the WAD and the GL nodes created here.
|
||||
// The original set is only being used to get the sector for in-game
|
||||
// positioning of actors but not for rendering.
|
||||
//
|
||||
// This is necessary because ZDBSP is much more sensitive
|
||||
// to sloppy mapping practices that produce overlapping sectors.
|
||||
// The crane in P:AR E1M3 is a good example that would be broken if
|
||||
// this wasn't done.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PointOnLine
|
||||
//
|
||||
// Same as the one im the node builder, but not part of a specific class
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool PointOnLine (int x, int y, int x1, int y1, int dx, int dy)
|
||||
{
|
||||
const double SIDE_EPSILON = 6.5536;
|
||||
|
||||
// For most cases, a simple dot product is enough.
|
||||
double d_dx = double(dx);
|
||||
double d_dy = double(dy);
|
||||
double d_x = double(x);
|
||||
double d_y = double(y);
|
||||
double d_x1 = double(x1);
|
||||
double d_y1 = double(y1);
|
||||
|
||||
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
|
||||
|
||||
if (fabs(s_num) < 17179869184.0) // 4<<32
|
||||
{
|
||||
// Either the point is very near the line, or the segment defining
|
||||
// the line is very short: Do a more expensive test to determine
|
||||
// just how far from the line the point is.
|
||||
double l = g_sqrt(d_dx*d_dx+d_dy*d_dy);
|
||||
double dist = fabs(s_num)/l;
|
||||
if (dist < SIDE_EPSILON)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SetRenderSector
|
||||
//
|
||||
// Sets the render sector for each GL subsector so that the proper flat
|
||||
// information can be retrieved
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void P_SetRenderSector()
|
||||
{
|
||||
int i;
|
||||
uint32_t j;
|
||||
TArray<subsector_t *> undetermined;
|
||||
subsector_t * ss;
|
||||
|
||||
#if 0 // doesn't work as expected :(
|
||||
|
||||
// hide all sectors on textured automap that only have hidden lines.
|
||||
bool *hidesec = new bool[numsectors];
|
||||
for(i = 0; i < numsectors; i++)
|
||||
{
|
||||
hidesec[i] = true;
|
||||
}
|
||||
for(i = 0; i < numlines; i++)
|
||||
{
|
||||
if (!(lines[i].flags & ML_DONTDRAW))
|
||||
{
|
||||
hidesec[lines[i].frontsector - sectors] = false;
|
||||
if (lines[i].backsector != NULL)
|
||||
{
|
||||
hidesec[lines[i].backsector - sectors] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i = 0; i < numsectors; i++)
|
||||
{
|
||||
if (hidesec[i]) sectors[i].MoreFlags |= SECMF_HIDDEN;
|
||||
}
|
||||
delete [] hidesec;
|
||||
#endif
|
||||
|
||||
// Check for incorrect partner seg info so that the following code does not crash.
|
||||
|
||||
for (auto &seg : level.segs)
|
||||
{
|
||||
auto p = seg.PartnerSeg;
|
||||
if (p != nullptr)
|
||||
{
|
||||
int partner = p->Index();
|
||||
|
||||
if (partner < 0 || partner >= (int)level.segs.Size() || &level.segs[partner] != p)
|
||||
{
|
||||
seg.PartnerSeg = nullptr;
|
||||
}
|
||||
|
||||
// glbsp creates such incorrect references for Strife.
|
||||
if (seg.linedef && seg.PartnerSeg != nullptr && !seg.PartnerSeg->linedef)
|
||||
{
|
||||
seg.PartnerSeg = seg.PartnerSeg->PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &seg : level.segs)
|
||||
{
|
||||
if (seg.PartnerSeg != nullptr && seg.PartnerSeg->PartnerSeg != &seg)
|
||||
{
|
||||
seg.PartnerSeg = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// look up sector number for each subsector
|
||||
for (auto &ss : level.subsectors)
|
||||
{
|
||||
// For rendering pick the sector from the first seg that is a sector boundary
|
||||
// this takes care of self-referencing sectors
|
||||
seg_t *seg = ss.firstline;
|
||||
|
||||
// Check for one-dimensional subsectors. These should be ignored when
|
||||
// being processed for automap drawing etc.
|
||||
ss.flags |= SSECF_DEGENERATE;
|
||||
for(j=2; j<ss.numlines; j++)
|
||||
{
|
||||
if (!PointOnLine(seg[j].v1->fixX(), seg[j].v1->fixY(), seg->v1->fixX(), seg->v1->fixY(), seg->v2->fixX() -seg->v1->fixX(), seg->v2->fixY() -seg->v1->fixY()))
|
||||
{
|
||||
// Not on the same line
|
||||
ss.flags &= ~SSECF_DEGENERATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seg = ss.firstline;
|
||||
for(j=0; j<ss.numlines; j++)
|
||||
{
|
||||
if(seg->sidedef && (seg->PartnerSeg == nullptr || (seg->PartnerSeg->sidedef != nullptr && seg->sidedef->sector!=seg->PartnerSeg->sidedef->sector)))
|
||||
{
|
||||
ss.render_sector = seg->sidedef->sector;
|
||||
break;
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
if(ss.render_sector == NULL)
|
||||
{
|
||||
undetermined.Push(&ss);
|
||||
}
|
||||
}
|
||||
|
||||
// assign a vaild render sector to all subsectors which haven't been processed yet.
|
||||
while (undetermined.Size())
|
||||
{
|
||||
bool deleted=false;
|
||||
for(i=undetermined.Size()-1;i>=0;i--)
|
||||
{
|
||||
ss=undetermined[i];
|
||||
seg_t * seg = ss->firstline;
|
||||
|
||||
for(j=0; j<ss->numlines; j++)
|
||||
{
|
||||
if (seg->PartnerSeg != nullptr && seg->PartnerSeg->Subsector)
|
||||
{
|
||||
sector_t * backsec = seg->PartnerSeg->Subsector->render_sector;
|
||||
if (backsec)
|
||||
{
|
||||
ss->render_sector = backsec;
|
||||
undetermined.Delete(i);
|
||||
deleted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
}
|
||||
// We still got some left but the loop above was unable to assign them.
|
||||
// This only happens when a subsector is off the map.
|
||||
// Don't bother and just assign the real sector for rendering
|
||||
if (!deleted && undetermined.Size())
|
||||
{
|
||||
for(i=undetermined.Size()-1;i>=0;i--)
|
||||
{
|
||||
ss=undetermined[i];
|
||||
ss->render_sector=ss->sector;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue