Beispiel #1
0
// [RH] Check dmflags for noexit and respond accordingly
bool CheckIfExitIsGood (AActor *self, level_info_t *info)
{
	cluster_info_t *cluster;

	// The world can always exit itself.
	if (self == NULL)
		return true;

	// We must kill all monsters to exit the level.
	if ((dmflags2 & DF2_KILL_MONSTERS) && level.killed_monsters != level.total_monsters)
		return false;

	// Is this a deathmatch game and we're not allowed to exit?
	if ((deathmatch || alwaysapplydmflags) && (dmflags & DF_NO_EXIT))
	{
		P_DamageMobj (self, self, self, TELEFRAG_DAMAGE, NAME_Exit);
		return false;
	}
	// Is this a singleplayer game and the next map is part of the same hub and we're dead?
	if (self->health <= 0 &&
		!multiplayer &&
		info != NULL &&
		info->cluster == level.cluster &&
		(cluster = FindClusterInfo(level.cluster)) != NULL &&
		cluster->flags & CLUSTER_HUB)
	{
		return false;
	}
	if (deathmatch && gameaction != ga_completed)
	{
		Printf ("%s exited the level.\n", self->player->userinfo.GetName());
	}
	return true;
}
Beispiel #2
0
/*
 * Note also that the CountofClusters value is exactly that—the count of data
 * clusters starting at cluster
 * 2. The maximum valid cluster number for the volume is CountofClusters + 1,
 * and the “count of clusters including the two reserved clusters” is CountofClusters + 2.
*/
int SetRootDir(FILE * img) {

	struct cluster temp;

	fatcat.rootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytesPerSec - 1)) / BPB_BytesPerSec;

	fatcat.firstDataSector = BPB_RsvdSecCnt + (BPB_NumFATs * BPB_FATSz32) + fatcat.rootDirSectors;

	fatcat.dataSectors = BPB_TotSec32 -(BPB_RsvdSecCnt + (BPB_NumFATs * BPB_FATSz32) + fatcat.rootDirSectors);

	fatcat.dataClusters = fatcat.dataSectors / BPB_SecPerClus;

	if(fatcat.dataClusters < 65526) return -1;

	temp = FindClusterInfo(BPB_RootClus);

	memcpy(fatcat.curDir->cluster, &temp, sizeof(struct cluster));

	fatcat.dirName = "/";

#ifdef _DEBUGGING_BOOT_SECT
	printf("\n\nRoot Directory Address: %d", fatcat.rootDirSectors);
	printf("\nFirst Data Sector: 0x%x", fatcat.firstDataSector);
	printf("\nTotal Data Sectors: %d", fatcat.dataSectors);
	printf("\nTotal Data Clusters: %d\n\n", fatcat.dataClusters);
#endif

	return 0;
}
Beispiel #3
0
struct cluster getfreeclus(FILE * img){
	struct cluster free;

	fseek(img, BPB_FSInfo * BPB_BytesPerSec, SEEK_SET);
	fread(&fatcat.fsinfo.FSI_LeadSig, 4, 1, img);
	fseek(img, 480, SEEK_CUR);
	fread(&fatcat.fsinfo.FSI_StrcSig, 4, 1, img);

	if(fatcat.fsinfo.FSI_LeadSig == LEAD_SIG_VAL &&
			fatcat.fsinfo.FSI_StrcSig == STRUC_SIG_VAL){
		fread(&fatcat.fsinfo.FSI_Free_Count, 4, 1, img);
		fread(&fatcat.fsinfo.FSI_Nxt_Free, 4, 1, img);
	}

	if(fatcat.fsinfo.FSI_Free_Count < 1){
		free = FindClusterInfo(fatcat.fsinfo.FSI_Nxt_Free + 1);
		if(free.firstSectors[0] != 0){

		}
	} else {
		// No clusters are free
		free.firstSectors[0] = 0x00;
	}

	return free;
}
Beispiel #4
0
unsigned int setdirclus(struct directory * dir){
		struct cluster temp;
		unsigned int clusval = 0;

		dir->cluster = malloc(sizeof(struct cluster));

		clusval = dir->FstClusHi;
		clusval = clusval << 1;
		clusval = clusval | dir->FstClusLO;
		clusval = clusval == 0x00 ? BPB_RootClus : clusval;

		temp = FindClusterInfo(clusval);
#ifdef  _DEBUGGING
		printf("\nSetting Cluster info");
		printf("\nCLUSTER VALUE: %d\n", clusval);
#endif
		memcpy(dir->cluster, &temp, sizeof(struct cluster));
		return 0;
}
Beispiel #5
0
static void ParseMapInfoLower (MapInfoHandler *handlers,
							   const char *strings[],
							   level_pwad_info_t *levelinfo,
							   cluster_info_t *clusterinfo,
							   DWORD flags)
{
	int entry;
	MapInfoHandler *handler;
	byte *info;

	info = levelinfo ? (byte *)levelinfo : (byte *)clusterinfo;

	while (SC_GetString ())
	{
		if (SC_MatchString (MapInfoTopLevel) != -1)
		{
			SC_UnGet ();
			break;
		}
		entry = SC_MustMatchString (strings);
		handler = handlers + entry;
		switch (handler->type)
		{
		case MITYPE_IGNORE:
			break;

		case MITYPE_EATNEXT:
			SC_MustGetString ();
			break;

		case MITYPE_INT:
			SC_MustGetNumber ();
			*((int *)(info + handler->data1)) = sc_Number;
			break;

		case MITYPE_FLOAT:
			SC_MustGetFloat ();
			*((float *)(info + handler->data1)) = sc_Float;
			break;

		case MITYPE_COLOR:
			{
				SC_MustGetString ();
				std::string string = V_GetColorStringByName (sc_String);
				if (string.length())
				{
					*((DWORD *)(info + handler->data1)) =
						V_GetColorFromString (NULL, string.c_str());
				}
				else
				{
					*((DWORD *)(info + handler->data1)) =
										V_GetColorFromString (NULL, sc_String);
				}
			}
			break;

		case MITYPE_MAPNAME:
			SC_MustGetString ();
			if (IsNum (sc_String))
			{
				int map = atoi (sc_String);
				sprintf (sc_String, "MAP%02d", map);
			}
			strncpy ((char *)(info + handler->data1), sc_String, 8);
			break;

		case MITYPE_LUMPNAME:
			SC_MustGetString ();
			uppercopy ((char *)(info + handler->data1), sc_String);
			break;

		case MITYPE_SKY:
			SC_MustGetString ();	// get texture name;
			uppercopy ((char *)(info + handler->data1), sc_String);
			SC_MustGetFloat ();		// get scroll speed
			//if (HexenHack)
			//{
			//	*((fixed_t *)(info + handler->data2)) = sc_Number << 8;
			//}
			//else
			//{
			//	*((fixed_t *)(info + handler->data2)) = (fixed_t)(sc_Float * 65536.0f);
			//}
			break;

		case MITYPE_SETFLAG:
			flags |= handler->data1;
			break;

		case MITYPE_SCFLAGS:
			flags = (flags & handler->data2) | handler->data1;
			break;

		case MITYPE_CLUSTER:
			SC_MustGetNumber ();
			*((int *)(info + handler->data1)) = sc_Number;
			if (HexenHack)
			{
				cluster_info_t *clusterH = FindClusterInfo (sc_Number);
				if (clusterH)
					clusterH->flags |= CLUSTER_HUB;
			}
			break;

		case MITYPE_STRING:
			SC_MustGetString ();
			ReplaceString ((const char **)(info + handler->data1), sc_String);
			break;

		case MITYPE_CSTRING:
			SC_MustGetString ();
			strncpy ((char *)(info + handler->data1), sc_String, handler->data2);
			*((char *)(info + handler->data1 + handler->data2)) = '\0';
			break;
		}
	}
	if (levelinfo)
		levelinfo->flags = flags;
	else
		clusterinfo->flags = flags;
}
Beispiel #6
0
void G_WorldDone (void) 
{ 
	cluster_info_t *nextcluster;
	cluster_info_t *thiscluster;

	gameaction = ga_worlddone; 

	if (level.flags & LEVEL_CHANGEMAPCHEAT)
		return;

	thiscluster = FindClusterInfo (level.cluster);

	if (strncmp (nextlevel, "enDSeQ", 6) == 0)
	{
		FName endsequence = ENamedName(strtol(nextlevel.GetChars()+6, NULL, 16));
		// Strife needs a special case here to choose between good and sad ending. Bad is handled elsewherw.
		if (endsequence == NAME_Inter_Strife)
		{
			if (players[0].mo->FindInventory (QuestItemClasses[24]) ||
				players[0].mo->FindInventory (QuestItemClasses[27]))
			{
				endsequence = NAME_Inter_Strife_Good;
			}
			else
			{
				endsequence = NAME_Inter_Strife_Sad;
			}
		}

		F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder,
			thiscluster->cdtrack, thiscluster->cdid,
			thiscluster->FinaleFlat, thiscluster->ExitText,
			thiscluster->flags & CLUSTER_EXITTEXTINLUMP,
			thiscluster->flags & CLUSTER_FINALEPIC,
			thiscluster->flags & CLUSTER_LOOKUPEXITTEXT,
			true, endsequence);
	}
	else
	{
		nextcluster = FindClusterInfo (FindLevelInfo (nextlevel)->cluster);

		if (nextcluster->cluster != level.cluster && !deathmatch)
		{
			// Only start the finale if the next level's cluster is different
			// than the current one and we're not in deathmatch.
			if (nextcluster->EnterText.IsNotEmpty())
			{
				F_StartFinale (nextcluster->MessageMusic, nextcluster->musicorder,
					nextcluster->cdtrack, nextcluster->cdid,
					nextcluster->FinaleFlat, nextcluster->EnterText,
					nextcluster->flags & CLUSTER_ENTERTEXTINLUMP,
					nextcluster->flags & CLUSTER_FINALEPIC,
					nextcluster->flags & CLUSTER_LOOKUPENTERTEXT,
					false);
			}
			else if (thiscluster->ExitText.IsNotEmpty())
			{
				F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder,
					thiscluster->cdtrack, nextcluster->cdid,
					thiscluster->FinaleFlat, thiscluster->ExitText,
					thiscluster->flags & CLUSTER_EXITTEXTINLUMP,
					thiscluster->flags & CLUSTER_FINALEPIC,
					thiscluster->flags & CLUSTER_LOOKUPEXITTEXT,
					false);
			}
		}
	}
} 
Beispiel #7
0
void G_DoCompleted (void)
{
	int i; 

	gameaction = ga_nothing;

	if (gamestate == GS_TITLELEVEL)
	{
		level.MapName = nextlevel;
		G_DoLoadLevel (startpos, false);
		startpos = 0;
		viewactive = true;
		return;
	}

	// [RH] Mark this level as having been visited
	if (!(level.flags & LEVEL_CHANGEMAPCHEAT))
		FindLevelInfo (level.MapName)->flags |= LEVEL_VISITED;

	if (automapactive)
		AM_Stop ();

	wminfo.finished_ep = level.cluster - 1;
	wminfo.LName0 = TexMan[TexMan.CheckForTexture(level.info->PName, FTexture::TEX_MiscPatch)];
	wminfo.current = level.MapName;

	if (deathmatch &&
		(dmflags & DF_SAME_LEVEL) &&
		!(level.flags & LEVEL_CHANGEMAPCHEAT))
	{
		wminfo.next = level.MapName;
		wminfo.LName1 = wminfo.LName0;
	}
	else
	{
		level_info_t *nextinfo = FindLevelInfo (nextlevel, false);
		if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0)
		{
			wminfo.next = nextlevel;
			wminfo.LName1 = NULL;
		}
		else
		{
			wminfo.next = nextinfo->MapName;
			wminfo.LName1 = TexMan[TexMan.CheckForTexture(nextinfo->PName, FTexture::TEX_MiscPatch)];
		}
	}

	CheckWarpTransMap (wminfo.next, true);
	nextlevel = wminfo.next;

	wminfo.next_ep = FindLevelInfo (wminfo.next)->cluster - 1;
	wminfo.maxkills = level.total_monsters;
	wminfo.maxitems = level.total_items;
	wminfo.maxsecret = level.total_secrets;
	wminfo.maxfrags = 0;
	wminfo.partime = TICRATE * level.partime;
	wminfo.sucktime = level.sucktime;
	wminfo.pnum = consoleplayer;
	wminfo.totaltime = level.totaltime;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
		wminfo.plyr[i].in = playeringame[i];
		wminfo.plyr[i].skills = players[i].killcount;
		wminfo.plyr[i].sitems = players[i].itemcount;
		wminfo.plyr[i].ssecret = players[i].secretcount;
		wminfo.plyr[i].stime = level.time;
		memcpy (wminfo.plyr[i].frags, players[i].frags
				, sizeof(wminfo.plyr[i].frags));
		wminfo.plyr[i].fragcount = players[i].fragcount;
	}

	// [RH] If we're in a hub and staying within that hub, take a snapshot
	//		of the level. If we're traveling to a new hub, take stuff from
	//		the player and clear the world vars. If this is just an
	//		ordinary cluster (not a hub), take stuff from the player, but
	//		leave the world vars alone.
	cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
	cluster_info_t *nextcluster = FindClusterInfo (wminfo.next_ep+1);	// next_ep is cluster-1
	EFinishLevelType mode;

	if (thiscluster != nextcluster || deathmatch ||
		!(thiscluster->flags & CLUSTER_HUB))
	{
		if (nextcluster->flags & CLUSTER_HUB)
		{
			mode = FINISH_NextHub;
		}
		else
		{
			mode = FINISH_NoHub;
		}
	}
	else
	{
		mode = FINISH_SameHub;
	}

	// Intermission stats for entire hubs
	G_LeavingHub(mode, thiscluster, &wminfo);

	for (i = 0; i < MAXPLAYERS; i++)
	{
		if (playeringame[i])
		{ // take away appropriate inventory
			G_PlayerFinishLevel (i, mode, changeflags);
		}
	}

	if (mode == FINISH_SameHub)
	{ // Remember the level's state for re-entry.
		if (!(level.flags2 & LEVEL2_FORGETSTATE))
		{
			G_SnapshotLevel ();
			// Do not free any global strings this level might reference
			// while it's not loaded.
			FBehavior::StaticLockLevelVarStrings();
		}
		else
		{ // Make sure we don't have a snapshot lying around from before.
			level.info->ClearSnapshot();
		}
	}
	else
	{ // Forget the states of all existing levels.
		G_ClearSnapshots ();

		if (mode == FINISH_NextHub)
		{ // Reset world variables for the new hub.
			P_ClearACSVars(false);
		}
		level.time = 0;
		level.maptime = 0;
	}

	if (!deathmatch &&
		((level.flags & LEVEL_NOINTERMISSION) ||
		((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB))))
	{
		G_WorldDone ();
		return;
	}

	gamestate = GS_INTERMISSION;
	viewactive = false;
	automapactive = false;

// [RH] If you ever get a statistics driver operational, adapt this.
//	if (statcopy)
//		memcpy (statcopy, &wminfo, sizeof(wminfo));

	WI_Start (&wminfo);
}
Beispiel #8
0
void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill)
{
	level_info_t *nextinfo = NULL;

	if (unloading)
	{
		Printf (TEXTCOLOR_RED "Unloading scripts cannot exit the level again.\n");
		return;
	}

	if (levelname == NULL || *levelname == 0)
	{
		// end the game
		levelname = NULL;
		if (!level.NextMap.Compare("enDSeQ",6))
		{
			nextlevel = level.NextMap;	// If there is already an end sequence please leave it alone!
		}
		else 
		{
			nextlevel.Format("enDSeQ%04x", int(gameinfo.DefaultEndSequence));
		}
	}
	else if (strncmp(levelname, "enDSeQ", 6) != 0)
	{
		nextinfo = FindLevelInfo (levelname, false);
		if (nextinfo != NULL)
		{
			level_info_t *nextredir = nextinfo->CheckLevelRedirect();
			if (nextredir != NULL)
			{
				nextinfo = nextredir;
			}
		}
		nextlevel = nextinfo->MapName;
	}
	else
	{
		nextlevel = levelname;
	}

	if (nextSkill != -1)
		NextSkill = nextSkill;

	if (flags & CHANGELEVEL_NOINTERMISSION)
	{
		level.flags |= LEVEL_NOINTERMISSION;
	}

	cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
	cluster_info_t *nextcluster = nextinfo? FindClusterInfo (nextinfo->cluster) : NULL;

	startpos = position;
	gameaction = ga_completed;
		
	if (nextinfo != NULL) 
	{
		if (thiscluster != nextcluster || (thiscluster && !(thiscluster->flags & CLUSTER_HUB)))
		{
			if (nextinfo->flags2 & LEVEL2_RESETINVENTORY)
			{
				flags |= CHANGELEVEL_RESETINVENTORY;
			}
			if (nextinfo->flags2 & LEVEL2_RESETHEALTH)
			{
				flags |= CHANGELEVEL_RESETHEALTH;
			}
		}
	}
	changeflags = flags;

	bglobal.End();	//Added by MC:

	// [RH] Give scripts a chance to do something
	unloading = true;
	FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true);
	unloading = false;

	STAT_ChangeLevel(nextlevel);

	if (thiscluster && (thiscluster->flags & CLUSTER_HUB))
	{
		if ((level.flags & LEVEL_NOINTERMISSION) || (nextcluster == thiscluster))
			NoWipe = 35;
		D_DrawIcon = "TELEICON";
	}

	for(int i = 0; i < MAXPLAYERS; i++)
	{
		if (playeringame[i])
		{
			player_t *player = &players[i];

			// Un-crouch all players here.
			player->Uncrouch();

			// If this is co-op, respawn any dead players now so they can
			// keep their inventory on the next map.
			if ((multiplayer || level.flags2 & LEVEL2_ALLOWRESPAWN) && !deathmatch && player->playerstate == PST_DEAD)
			{
				// Copied from the end of P_DeathThink [[
				player->cls = NULL;		// Force a new class if the player is using a random class
				player->playerstate = PST_REBORN;
				if (player->mo->special1 > 2)
				{
					player->mo->special1 = 0;
				}
				// ]]
				G_DoReborn(i, false);
			}
		}
	}
}
Beispiel #9
0
void G_InitLevelLocals ()
{
	level_info_t *info;

	BaseBlendA = 0.0f;		// Remove underwater blend effect, if any
	NormalLight.Maps = realcolormaps;

	// [BB] Instead of just setting the color, we also have to reset Desaturate and build the lights.
	NormalLight.ChangeColor (PalEntry (255, 255, 255), 0);

	level.gravity = sv_gravity * 35/TICRATE;
	level.aircontrol = (fixed_t)(sv_aircontrol * 65536.f);
	level.teamdamage = teamdamage;
	level.flags = 0;
	level.flags2 = 0;

	info = FindLevelInfo (level.MapName);

	level.info = info;
	level.skyspeed1 = info->skyspeed1;
	level.skyspeed2 = info->skyspeed2;
	level.skytexture1 = TexMan.GetTexture(info->SkyPic1, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
	level.skytexture2 = TexMan.GetTexture(info->SkyPic2, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
	level.fadeto = info->fadeto;
	level.cdtrack = info->cdtrack;
	level.cdid = info->cdid;
	level.FromSnapshot = false;
	if (level.fadeto == 0)
	{
		R_SetDefaultColormap (info->FadeTable);
		if (strnicmp (info->FadeTable, "COLORMAP", 8) != 0)
		{
			level.flags |= LEVEL_HASFADETABLE;
		}
		/*
	}
	else
	{
		NormalLight.ChangeFade (level.fadeto);
		*/
	}
	level.airsupply = info->airsupply*TICRATE;
	level.outsidefog = info->outsidefog;
	level.WallVertLight = info->WallVertLight*2;
	level.WallHorizLight = info->WallHorizLight*2;
	if (info->gravity != 0.f)
	{
		level.gravity = info->gravity * 35/TICRATE;
	}
	if (info->aircontrol != 0.f)
	{
		level.aircontrol = (fixed_t)(info->aircontrol * 65536.f);
	}
	if (info->teamdamage != 0.f)
	{
		level.teamdamage = info->teamdamage;
	}

	G_AirControlChanged ();

	cluster_info_t *clus = FindClusterInfo (info->cluster);

	level.partime = info->partime;
	level.sucktime = info->sucktime;
	level.cluster = info->cluster;
	level.clusterflags = clus ? clus->flags : 0;
	level.flags |= info->flags;
	level.flags2 |= info->flags2;
	level.levelnum = info->levelnum;
	level.Music = info->Music;
	level.musicorder = info->musicorder;

	level.LevelName = level.info->LookupLevelName();
	level.NextMap = info->NextMap;
	level.NextSecretMap = info->NextSecretMap;

	compatflags.Callback();
	compatflags2.Callback();

	NormalLight.ChangeFade (level.fadeto);

	level.DefaultEnvironment = info->DefaultEnvironment;
	level.DefaultSkybox = NULL;
}
Beispiel #10
0
void DrawHUD()
{
	player_t * CPlayer = StatusBar->CPlayer;

	// [BB] The player may not have a body while connecting.
	if ( CPlayer->mo == NULL )
		return;

	if (HudFont==NULL) HUD_InitHud();

	players[consoleplayer].inventorytics = 0;
	if (hud_althudscale && SCREENWIDTH>640) 
	{
		hudwidth=SCREENWIDTH/2;
		if (hud_althudscale == 2) 
		{
			// Optionally just double the pixels to reduce scaling artifacts.
			hudheight=SCREENHEIGHT/2;
		}
		else 
		{
			if (WidescreenRatio == 4)
			{
				hudheight = hudwidth * 30 / BaseRatioSizes[WidescreenRatio][3];	// BaseRatioSizes is inverted for this mode
			}
			else
			{
				hudheight = hudwidth * 30 / (48*48/BaseRatioSizes[WidescreenRatio][3]);
			}
		}
	}
	else
	{
		hudwidth=SCREENWIDTH;
		hudheight=SCREENHEIGHT;
	}

	if (!automapactive)
	{
		int i;

		// No HUD in the title level!
		if (gamestate == GS_TITLELEVEL || !CPlayer) return;

		if (!deathmatch) DrawStatus(CPlayer, 5, hudheight-50);
		else
		{
			DrawStatus(CPlayer, 5, hudheight-75);

			// [TP] Only draw frags if not playing in teams
			if (( GAMEMODE_GetCurrentFlags() & GMF_PLAYERSONTEAMS ) == 0 )
				DrawFrags(CPlayer, 5, hudheight-70);
		}

		// [TP] Only draw health and armor if we are allowed to know those stats. Also don't
		// draw spectator's health/armor stats.
		if ( ShouldDrawHealth( CPlayer ))
		{
			DrawHealth(CPlayer->health, 5, hudheight-45);
			// Yes, that doesn't work properly for Hexen but frankly, I have no
			// idea how to make a meaningful value out of Hexen's armor system!
			DrawArmor(CPlayer->mo->FindInventory(RUNTIME_CLASS(ABasicArmor)), 5, hudheight-20);
		}

		// [TP] Draw team stuff.
		DrawTeamScores( 10, hudheight - 90 );

		i=DrawKeys(CPlayer, hudwidth-4, hudheight-10);
		i=DrawAmmo(CPlayer, hudwidth-5, i);
		DrawWeapons(CPlayer, hudwidth-5, i);
		DrawInventory(CPlayer, 144, hudheight-28);
		if (CPlayer->camera && CPlayer->camera->player)
		{
			StatusBar->DrawCrosshair();
		}
		if (idmypos) DrawCoordinates(CPlayer);
	}
	else
	{
		char printstr[256];
		int seconds;
		cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
		bool hub = !!(thiscluster->flags&CLUSTER_HUB);
		int length=8*SmallFont->GetCharWidth('0');
		int fonth=SmallFont->GetHeight()+1;
		int bottom=hudheight-1;

		if (am_showtotaltime)
		{
			seconds = level.totaltime / TICRATE;
			mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
			DrawHudText(SmallFont, hudcolor_ttim, printstr, hudwidth-length, bottom, FRACUNIT);
			bottom -= fonth;
		}

		if (am_showtime)
		{
			seconds = level.time /TICRATE;
			mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
			DrawHudText(SmallFont, hudcolor_time, printstr, hudwidth-length, bottom, FRACUNIT);
			bottom -= fonth;

			// Single level time for hubs
			if (level.clusterflags&CLUSTER_HUB)
			{
				seconds= level.maptime /TICRATE;
				mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
				DrawHudText(SmallFont, hudcolor_ltim, printstr, hudwidth-length, bottom, FRACUNIT);
			}
		}

		mysnprintf(printstr, countof(printstr), "%s: %s", level.mapname, level.LevelName.GetChars());
		screen->DrawText(SmallFont, hudcolor_titl, 1, hudheight-fonth-1, printstr,
			DTA_KeepRatio, true,
			DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, TAG_DONE);

		DrawCoordinates(CPlayer);
	}
}