in doom_py/src/vizdoom/src/p_setup.cpp [3522:4125]
void P_SetupLevel (const char *lumpname, int position)
{
cycle_t times[20];
FMapThing *buildthings;
int numbuildthings;
int i;
bool buildmap;
const int *oldvertextable = NULL;
// This is motivated as follows:
bool RequireGLNodes = Renderer->RequireGLNodes() || am_textured;
for (i = 0; i < (int)countof(times); ++i)
{
times[i].Reset();
}
level.maptype = MAPTYPE_UNKNOWN;
wminfo.partime = 180;
MapThingsConverted.Clear();
MapThingsUserDataIndex.Clear();
MapThingsUserData.Clear();
linemap.Clear();
FCanvasTextureInfo::EmptyList ();
R_FreePastViewers ();
P_ClearUDMFKeys();
if (!savegamerestore)
{
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].killcount = players[i].secretcount
= players[i].itemcount = 0;
}
}
for (i = 0; i < MAXPLAYERS; ++i)
{
players[i].mo = NULL;
}
// [RH] Clear any scripted translation colors the previous level may have set.
for (i = 0; i < int(translationtables[TRANSLATION_LevelScripted].Size()); ++i)
{
FRemapTable *table = translationtables[TRANSLATION_LevelScripted][i];
if (table != NULL)
{
delete table;
translationtables[TRANSLATION_LevelScripted][i] = NULL;
}
}
translationtables[TRANSLATION_LevelScripted].Clear();
// Initial height of PointOfView will be set by player think.
players[consoleplayer].viewz = 1;
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ();
// [RH] Clear all ThingID hash chains.
AActor::ClearTIDHashes ();
// [RH] clear out the mid-screen message
C_MidPrint (NULL, NULL);
// Free all level data from the previous map
P_FreeLevelData ();
MapData *map = P_OpenMapData(lumpname, true);
if (map == NULL)
{
I_Error("Unable to open map '%s'\n", lumpname);
}
// find map num
level.lumpnum = map->lumpnum;
hasglnodes = false;
// [RH] Support loading Build maps (because I felt like it. :-)
buildmap = false;
if (map->Size(0) > 0)
{
BYTE *mapdata = new BYTE[map->Size(0)];
map->Seek(0);
map->file->Read(mapdata, map->Size(0));
times[0].Clock();
buildmap = P_LoadBuildMap (mapdata, map->Size(0), &buildthings, &numbuildthings);
times[0].Unclock();
delete[] mapdata;
}
if (!buildmap)
{
// note: most of this ordering is important
ForceNodeBuild = gennodes;
// [RH] Load in the BEHAVIOR lump
FBehavior::StaticUnloadModules ();
if (map->HasBehavior)
{
P_LoadBehavior (map);
level.maptype = MAPTYPE_HEXEN;
}
else
{
// We need translators only for Doom format maps.
const char *translator;
if (!level.info->Translator.IsEmpty())
{
// The map defines its own translator.
translator = level.info->Translator.GetChars();
}
else
{
// Has the user overridden the game's default translator with a commandline parameter?
translator = Args->CheckValue("-xlat");
if (translator == NULL)
{
// Use the game's default.
translator = gameinfo.translator.GetChars();
}
}
P_LoadTranslator(translator);
level.maptype = MAPTYPE_DOOM;
}
if (map->isText)
{
level.maptype = MAPTYPE_UDMF;
}
CheckCompatibility(map);
if (ib_compatflags & BCOMPATF_REBUILDNODES)
{
ForceNodeBuild = true;
}
T_LoadScripts(map);
if (!map->HasBehavior || map->isText)
{
// Doom format and UDMF text maps get strict monster activation unless the mapinfo
// specifies differently.
if (!(level.flags2 & LEVEL2_LAXACTIVATIONMAPINFO))
{
level.flags2 &= ~LEVEL2_LAXMONSTERACTIVATION;
}
}
if (!map->HasBehavior && !map->isText)
{
// set compatibility flags
if (gameinfo.gametype == GAME_Strife)
{
level.flags2 |= LEVEL2_RAILINGHACK;
}
level.flags2 |= LEVEL2_DUMMYSWITCHES;
}
FBehavior::StaticLoadDefaultModules ();
P_LoadStrifeConversations (map, lumpname);
FMissingTextureTracker missingtex;
if (!map->isText)
{
times[0].Clock();
P_LoadVertexes (map);
times[0].Unclock();
// Check for maps without any BSP data at all (e.g. SLIGE)
times[1].Clock();
P_LoadSectors (map, missingtex);
times[1].Unclock();
times[2].Clock();
P_LoadSideDefs (map);
times[2].Unclock();
times[3].Clock();
if (!map->HasBehavior)
P_LoadLineDefs (map);
else
P_LoadLineDefs2 (map); // [RH] Load Hexen-style linedefs
times[3].Unclock();
times[4].Clock();
P_LoadSideDefs2 (map, missingtex);
times[4].Unclock();
times[5].Clock();
P_FinishLoadingLineDefs ();
times[5].Unclock();
if (!map->HasBehavior)
P_LoadThings (map);
else
P_LoadThings2 (map); // [RH] Load Hexen-style things
SetCompatibilityParams();
}
else
{
times[0].Clock();
P_ParseTextMap(map, missingtex);
times[0].Unclock();
}
times[6].Clock();
P_LoopSidedefs (true);
times[6].Unclock();
linemap.Clear();
linemap.ShrinkToFit();
SummarizeMissingTextures(missingtex);
}
else
{
ForceNodeBuild = true;
level.maptype = MAPTYPE_BUILD;
}
bool reloop = false;
if (!ForceNodeBuild)
{
// Check for compressed nodes first, then uncompressed nodes
FWadLump test;
DWORD id = MAKE_ID('X','x','X','x'), idcheck = 0, idcheck2 = 0, idcheck3 = 0, idcheck4 = 0, idcheck5 = 0, idcheck6 = 0;
if (map->Size(ML_ZNODES) != 0)
{
// Test normal nodes first
map->Seek(ML_ZNODES);
idcheck = MAKE_ID('Z','N','O','D');
idcheck2 = MAKE_ID('X','N','O','D');
}
else if (map->Size(ML_GLZNODES) != 0)
{
map->Seek(ML_GLZNODES);
idcheck = MAKE_ID('Z','G','L','N');
idcheck2 = MAKE_ID('Z','G','L','2');
idcheck3 = MAKE_ID('Z','G','L','3');
idcheck4 = MAKE_ID('X','G','L','N');
idcheck5 = MAKE_ID('X','G','L','2');
idcheck6 = MAKE_ID('X','G','L','3');
}
map->file->Read (&id, 4);
if (id != 0 && (id == idcheck || id == idcheck2 || id == idcheck3 || id == idcheck4 || id == idcheck5 || id == idcheck6))
{
try
{
P_LoadZNodes (*map->file, id);
}
catch (CRecoverableError &error)
{
Printf ("Error loading nodes: %s\n", error.GetMessage());
ForceNodeBuild = true;
if (subsectors != NULL)
{
delete[] subsectors;
subsectors = NULL;
}
if (segs != NULL)
{
delete[] segs;
segs = NULL;
}
if (nodes != NULL)
{
delete[] nodes;
nodes = NULL;
}
}
}
else if (!map->isText) // regular nodes are not supported for text maps
{
// If all 3 node related lumps are empty there's no need to output a message.
// This just means that the map has no nodes and the engine is supposed to build them.
if (map->Size(ML_SEGS) != 0 || map->Size(ML_SSECTORS) != 0 || map->Size(ML_NODES) != 0)
{
if (!P_CheckV4Nodes(map))
{
times[7].Clock();
P_LoadSubsectors<mapsubsector_t, mapseg_t> (map);
times[7].Unclock();
times[8].Clock();
if (!ForceNodeBuild) P_LoadNodes<mapnode_t, mapsubsector_t> (map);
times[8].Unclock();
times[9].Clock();
if (!ForceNodeBuild) P_LoadSegs<mapseg_t> (map);
times[9].Unclock();
}
else
{
times[7].Clock();
P_LoadSubsectors<mapsubsector4_t, mapseg4_t> (map);
times[7].Unclock();
times[8].Clock();
if (!ForceNodeBuild) P_LoadNodes<mapnode4_t, mapsubsector4_t> (map);
times[8].Unclock();
times[9].Clock();
if (!ForceNodeBuild) P_LoadSegs<mapseg4_t> (map);
times[9].Unclock();
}
}
else ForceNodeBuild = true;
}
else ForceNodeBuild = true;
// If loading the regular nodes failed try GL nodes before considering a rebuild
if (ForceNodeBuild)
{
if (P_LoadGLNodes(map))
{
ForceNodeBuild = false;
reloop = true;
}
}
}
else reloop = true;
unsigned int startTime=0, endTime=0;
bool BuildGLNodes;
if (ForceNodeBuild)
{
BuildGLNodes = RequireGLNodes || multiplayer || demoplayback || demorecording || genglnodes;
startTime = I_FPSTime ();
TArray<FNodeBuilder::FPolyStart> polyspots, anchors;
P_GetPolySpots (map, polyspots, anchors);
FNodeBuilder::FLevel leveldata =
{
vertexes, numvertexes,
sides, numsides,
lines, numlines,
0, 0, 0, 0
};
leveldata.FindMapBounds ();
// We need GL nodes if am_textured is on.
// In case a sync critical game mode is started, also build GL nodes to avoid problems
// if the different machines' am_textured setting differs.
FNodeBuilder builder (leveldata, polyspots, anchors, BuildGLNodes);
delete[] vertexes;
builder.Extract (nodes, numnodes,
segs, glsegextras, numsegs,
subsectors, numsubsectors,
vertexes, numvertexes);
endTime = I_FPSTime ();
DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs);
oldvertextable = builder.GetOldVertexTable();
reloop = true;
}
else
{
BuildGLNodes = false;
// Older ZDBSPs had problems with compressed sidedefs and assigned wrong sides to the segs if both sides were the same sidedef.
for(i=0;i<numsegs;i++)
{
seg_t * seg=&segs[i];
if (seg->backsector == seg->frontsector && seg->linedef)
{
fixed_t d1=P_AproxDistance(seg->v1->x-seg->linedef->v1->x,seg->v1->y-seg->linedef->v1->y);
fixed_t d2=P_AproxDistance(seg->v2->x-seg->linedef->v1->x,seg->v2->y-seg->linedef->v1->y);
if (d2<d1) // backside
{
seg->sidedef = seg->linedef->sidedef[1];
}
else // front side
{
seg->sidedef = seg->linedef->sidedef[0];
}
}
}
}
// Copy pointers to the old nodes so that R_PointInSubsector can use them
if (nodes && subsectors)
{
gamenodes = nodes;
numgamenodes = numnodes;
gamesubsectors = subsectors;
numgamesubsectors = numsubsectors;
}
else
{
gamenodes=NULL;
}
if (RequireGLNodes)
{
// Build GL nodes if we want a textured automap or GL nodes are forced to be built.
// If the original nodes being loaded are not GL nodes they will be kept around for
// use in P_PointInSubsector to avoid problems with maps that depend on the specific
// nodes they were built with (P:AR E1M3 is a good example for a map where this is the case.)
reloop |= P_CheckNodes(map, BuildGLNodes, endTime - startTime);
hasglnodes = true;
}
else
{
hasglnodes = P_CheckForGLNodes();
}
times[10].Clock();
P_LoadBlockMap (map);
times[10].Unclock();
times[11].Clock();
P_LoadReject (map, buildmap);
times[11].Unclock();
times[12].Clock();
P_GroupLines (buildmap);
times[12].Unclock();
times[13].Clock();
P_FloodZones ();
times[13].Unclock();
if (hasglnodes)
{
P_SetRenderSector();
}
bodyqueslot = 0;
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one.
for (i = 0; i < BODYQUESIZE; i++)
bodyque[i] = NULL;
deathmatchstarts.Clear();
AllPlayerStarts.Clear();
memset(playerstarts, 0, sizeof(playerstarts));
if (!buildmap)
{
// [RH] Spawn slope creating things first.
P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], oldvertextable);
P_CopySlopes();
// Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials
P_Spawn3DFloors();
times[14].Clock();
P_SpawnThings(position);
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].mo != NULL)
players[i].health = players[i].mo->health;
}
times[14].Unclock();
times[15].Clock();
if (!map->HasBehavior && !map->isText)
P_TranslateTeleportThings (); // [RH] Assign teleport destination TIDs
times[15].Unclock();
}
else
{
for (i = 0; i < numbuildthings; ++i)
{
SpawnMapThing (i, &buildthings[i], 0);
}
delete[] buildthings;
}
delete map;
if (oldvertextable != NULL)
{
delete[] oldvertextable;
}
// set up world state
P_SpawnSpecials ();
// This must be done BEFORE the PolyObj Spawn!!!
Renderer->PreprocessLevel();
times[16].Clock();
if (reloop) P_LoopSidedefs (false);
PO_Init (); // Initialize the polyobjs
times[16].Unclock();
assert(sidetemp != NULL);
delete[] sidetemp;
sidetemp = NULL;
// if deathmatch, randomly spawn the active players
if (deathmatch)
{
for (i=0 ; i<MAXPLAYERS ; i++)
{
if (playeringame[i])
{
players[i].mo = NULL;
G_DeathMatchSpawnPlayer (i);
}
}
}
// the same, but for random single/coop player starts
else if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)
{
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i])
{
players[i].mo = NULL;
FPlayerStart *mthing = G_PickPlayerStart(i);
P_SpawnPlayer(mthing, i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
}
}
}
// Don't count monsters in end-of-level sectors if option is on
if (dmflags2 & DF2_NOCOUNTENDMONST)
{
TThinkerIterator<AActor> it;
AActor * mo;
while ((mo=it.Next()))
{
if (mo->flags & MF_COUNTKILL)
{
if (mo->Sector->damageamount > 0 && (mo->Sector->Flags & (SECF_ENDGODMODE|SECF_ENDLEVEL)) == (SECF_ENDGODMODE|SECF_ENDLEVEL))
{
mo->ClearCounters();
}
}
}
}
T_PreprocessScripts(); // preprocess FraggleScript scripts
// build subsector connect matrix
// UNUSED P_ConnectSubsectors ();
R_OldBlend = 0xffffffff;
// [RH] Remove all particles
P_ClearParticles ();
times[17].Clock();
// preload graphics and sounds
if (precache)
{
TexMan.PrecacheLevel ();
S_PrecacheLevel ();
}
times[17].Unclock();
if (deathmatch)
{
AnnounceGameStart ();
}
P_ResetSightCounters (true);
//Printf ("free memory: 0x%x\n", Z_FreeMemory());
if (showloadtimes)
{
Printf ("---Total load times---\n");
for (i = 0; i < 18; ++i)
{
static const char *timenames[] =
{
"load vertexes",
"load sectors",
"load sides",
"load lines",
"load sides 2",
"load lines 2",
"loop sides",
"load subsectors",
"load nodes",
"load segs",
"load blockmap",
"load reject",
"group lines",
"flood zones",
"load things",
"translate teleports",
"init polys",
"precache"
};
Printf ("Time%3d:%9.4f ms (%s)\n", i, times[i].TimeMS(), timenames[i]);
}
}
MapThingsConverted.Clear();
MapThingsUserDataIndex.Clear();
MapThingsUserData.Clear();
if (glsegextras != NULL)
{
delete[] glsegextras;
glsegextras = NULL;
}
}