void P_SetupLevel()

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;
	}
}