diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 28b20f61e287d6826ea2680d135cbcef154f726c..484daaf49e68f04c1ba9a5223dd53916caf054cd 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -69,6 +69,7 @@ patch_t *nightsnum[10]; // 0-9
 // Level title and credits fonts
 patch_t *lt_font[LT_FONTSIZE];
 patch_t *cred_font[CRED_FONTSIZE];
+patch_t *ttlnum[20]; // act numbers (0-19)
 
 static player_t *plr;
 boolean chat_on; // entering a chat message?
@@ -238,6 +239,13 @@ void HU_LoadGraphics(void)
 	tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX);
 	tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX);
 
+	// cache act numbers for level titles
+	for (i = 0; i < 20; i++)
+	{
+		sprintf(buffer, "TTL%.2d", i);
+		ttlnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
+	}
+
 	// cache the crosshairs, don't bother to know which one is being used,
 	// just cache all 3, they're so small anyway.
 	for (i = 0; i < HU_CROSSHAIRS; i++)
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 2dbeb556d7056e65b62f38b02785e3ef38dee942..b7fa0abb0cae2ca09d6c2e5779428cab1f6f47c5 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -63,6 +63,7 @@ extern patch_t *tallnum[10];
 extern patch_t *nightsnum[10];
 extern patch_t *lt_font[LT_FONTSIZE];
 extern patch_t *cred_font[CRED_FONTSIZE];
+extern patch_t *ttlnum[20];
 extern patch_t *emeraldpics[7];
 extern patch_t *tinyemeraldpics[7];
 extern patch_t *rflagico;
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 437b6758a386381dc06276c1efb43357e3a83a4f..ddb3e2738464c951ffbd29c9101fd51fb6c07792 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -77,7 +77,6 @@ static patch_t *race1;
 static patch_t *race2;
 static patch_t *race3;
 static patch_t *racego;
-static patch_t *ttlnum;
 static patch_t *nightslink;
 static patch_t *curweapon;
 static patch_t *normring;
@@ -992,13 +991,10 @@ static void ST_drawLevelTitle(void)
 	if (!(timeinmap > 2 && timeinmap-3 < 110))
 		return;
 
+	lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
+
 	if (actnum > 0)
-	{
-		ttlnum = W_CachePatchName(va("TTL%.2d", actnum), PU_CACHE);
-		lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)) - SHORT(ttlnum->width);
-	}
-	else
-		lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
+		lvlttlxpos -= V_LevelActNumWidth(actnum);
 
 	ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl);
 	zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("ZONE"));
@@ -1026,7 +1022,7 @@ static void ST_drawLevelTitle(void)
 	}
 
 	if (actnum)
-		V_DrawScaledPatch(ttlnumxpos, zoney, 0, ttlnum);
+		V_DrawLevelActNum(ttlnumxpos, zoney, 0, actnum);
 
 	V_DrawLevelTitle(lvlttlxpos, lvlttly, 0, lvlttl);
 
diff --git a/src/v_video.c b/src/v_video.c
index 20fe9229e8171910d741a1ac493eca764ace095f..5d9b9e841aa909055a4a3c0fe68f0e64766229c3 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -1819,6 +1819,16 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
 	} while (--digits);
 }
 
+// Draw an act number for a level title
+// Todo: actually draw two-digit numbers as two act num patches
+void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num)
+{
+	if (num < 0 || num > 19)
+		return; // not supported
+
+	V_DrawScaledPatch(x, y, flags, ttlnum[num]);
+}
+
 // Write a string using the credit font
 // NOTE: the text is centered for screens larger than the base width
 //
@@ -1983,6 +1993,16 @@ INT32 V_LevelNameHeight(const char *string)
 	return w;
 }
 
+// For ST_drawLevelTitle
+// Returns the width of the act num patch
+INT32 V_LevelActNumWidth(INT32 num)
+{
+	if (num < 0 || num > 19)
+		return 0; // not a valid number
+
+	return SHORT(ttlnum[num]->width);
+}
+
 //
 // Find string width from hu_font chars
 //
diff --git a/src/v_video.h b/src/v_video.h
index f9fd475f463014caad938c45a9d64d4d36233f61..50c927aefe47cdadfd8aefd441dabac638c6d3d9 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -177,10 +177,12 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 // Draw tall nums, used for menu, HUD, intermission
 void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num);
 void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits);
+void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num);
 
 // Find string width from lt_font chars
 INT32 V_LevelNameWidth(const char *string);
 INT32 V_LevelNameHeight(const char *string);
+INT32 V_LevelActNumWidth(INT32 num); // act number width
 
 void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string);
 INT32 V_CreditStringWidth(const char *string);
diff --git a/src/y_inter.c b/src/y_inter.c
index 8a93b3eabcecd93b31d39fa967e1236a5c6f7bb1..10cf27369a1c12641a481d9c9064c071715f416a 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -70,7 +70,7 @@ typedef union
 		UINT32 score, total; // fake score, total
 		UINT32 tics; // time
 
-		patch_t *ttlnum; // act number being displayed
+		INT32 actnum; // act number being displayed
 		patch_t *ptotal; // TOTAL
 		UINT8 gotlife; // Number of extra lives obtained
 	} coop;
@@ -287,8 +287,8 @@ void Y_IntermissionDrawer(void)
 		V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1);
 		V_DrawLevelTitle(data.coop.passedx2, 49+V_LevelNameHeight(data.coop.passed2)+2, 0, data.coop.passed2);
 
-		if (mapheaderinfo[gamemap-1]->actnum)
-			V_DrawScaledPatch(244, 57, 0, data.coop.ttlnum);
+		if (data.coop.actnum)
+			V_DrawLevelActNum(244, 57, 0, data.coop.actnum);
 
 		bonusy = 150;
 		// Total
@@ -1081,11 +1081,7 @@ void Y_StartIntermission(void)
 			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
 
 			// get act number
-			if (mapheaderinfo[prevmap]->actnum)
-				data.coop.ttlnum = W_CachePatchName(va("TTL%.2d", mapheaderinfo[prevmap]->actnum),
-					PU_STATIC);
-			else
-				data.coop.ttlnum = W_CachePatchName("TTL01", PU_STATIC);
+			data.coop.actnum = mapheaderinfo[gamemap-1]->actnum;
 
 			// get background patches
 			widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
@@ -1888,7 +1884,6 @@ static void Y_UnloadData(void)
 	{
 		case int_coop:
 			// unload the coop and single player patches
-			UNLOAD(data.coop.ttlnum);
 			UNLOAD(data.coop.bonuspatches[3]);
 			UNLOAD(data.coop.bonuspatches[2]);
 			UNLOAD(data.coop.bonuspatches[1]);