diff --git a/src/dehacked.c b/src/dehacked.c
index 5ba5d75d48fa1d55530a4a37d741e636ad634583..cc3f196a655a3dddcdc1d9d683d9b455bcc1cc59 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -2277,6 +2277,8 @@ static void reademblemdata(MYFILE *f, INT32 num)
 					emblemlocations[num-1].type = ET_NGRADE;
 				else if (fastcmp(word2, "NTIME"))
 					emblemlocations[num-1].type = ET_NTIME;
+				else if (fastcmp(word2, "MAP"))
+					emblemlocations[num-1].type = ET_MAP;
 				else
 					emblemlocations[num-1].type = (UINT8)value;
 			}
@@ -7424,7 +7426,12 @@ struct {
 	{"SF_X8AWAYSOUND",SF_X8AWAYSOUND},
 	{"SF_NOINTERRUPT",SF_NOINTERRUPT},
 	{"SF_X2AWAYSOUND",SF_X2AWAYSOUND},
-
+	
+	// Map emblem var flags
+	{"ME_ALLEMERALDS",ME_ALLEMERALDS},
+	{"ME_ULTIMATE",ME_ULTIMATE},
+	{"ME_PERFECT",ME_PERFECT},
+	
 #ifdef HAVE_BLUA
 	// p_local.h constants
 	{"FLOATSPEED",FLOATSPEED},
diff --git a/src/m_cond.c b/src/m_cond.c
index 5e23d40802bfe9ef81e5560b86d8403a43a41340..7f977c15d815b73b972094b805f4a28a6f0128d7 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -929,7 +929,7 @@ UINT8 M_CheckLevelEmblems(void)
 	// Update Score, Time, Rings emblems
 	for (i = 0; i < numemblems; ++i)
 	{
-		if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].collected)
+		if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected)
 			continue;
 
 		levelnum = emblemlocations[i].level;
@@ -963,6 +963,42 @@ UINT8 M_CheckLevelEmblems(void)
 	return somethingUnlocked;
 }
 
+UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
+{
+	INT32 i;
+	INT32 embtype;
+	INT16 levelnum;
+	UINT8 res;
+	UINT8 somethingUnlocked = 0;
+	UINT8 flags;
+
+	for (i = 0; i < numemblems; ++i)
+	{
+		if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected)
+			continue;
+
+		levelnum = emblemlocations[i].level;
+		embtype = emblemlocations[i].var;
+		flags = MV_BEATEN;
+		
+		if (embtype & ME_ALLEMERALDS)
+			flags |= MV_ALLEMERALDS;
+		
+		if (embtype & ME_ULTIMATE)
+			flags |= MV_ULTIMATE;
+		
+		if (embtype & ME_PERFECT)
+			flags |= MV_PERFECT;
+		
+		res = ((mapvisited[levelnum - 1] & flags) == flags);
+		
+		emblemlocations[i].collected = res;
+		if (res)
+			++somethingUnlocked;
+	}
+	return somethingUnlocked;
+}
+
 // -------------------
 // Quick unlock checks
 // -------------------
diff --git a/src/m_cond.h b/src/m_cond.h
index e61ff1f795819c03a4ff59803e9fa836afb25088..94802f66594bfd3f2f3e7162a87b4387d66c0ace 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -73,6 +73,12 @@ typedef struct
 #define ET_RINGS  4
 #define ET_NGRADE 5
 #define ET_NTIME  6
+#define ET_MAP    7
+
+// Map emblem flags
+#define ME_ALLEMERALDS 1
+#define ME_ULTIMATE    2
+#define ME_PERFECT     4
 
 typedef struct
 {
@@ -153,6 +159,7 @@ void M_CheckUnlockConditions(void);
 UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
 void M_SilentUpdateUnlockablesAndEmblems(void);
 UINT8 M_CheckLevelEmblems(void);
+UINT8 M_CompletionEmblems(void);
 
 // Checking unlockable status
 UINT8 M_AnySecretUnlocked(void);
diff --git a/src/m_menu.c b/src/m_menu.c
index f682cd1b596b9ef3fc62be550698d1cf5d9bd9d4..a269768cb2a53680fc437868455afcb3c61427b4 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2940,6 +2940,8 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y)
 				curtype = 1; break;
 			case ET_NGRADE: case ET_NTIME:
 				curtype = 2; break;
+			case ET_MAP:
+				curtype = 3; break;
 			default:
 				curtype = 0; break;
 		}
diff --git a/src/y_inter.c b/src/y_inter.c
index 3b14f28375ca3f3530a8a5d99c785d2c2a7616d2..5548fe346c88a59c9d016601483e7430519a200e 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -912,7 +912,8 @@ static void Y_UpdateRecordReplays(void)
 void Y_StartIntermission(void)
 {
 	INT32 i;
-
+	UINT8 completionEmblems = M_CompletionEmblems();
+	
 	intertic = -1;
 
 #ifdef PARANOIA
@@ -1007,6 +1008,9 @@ void Y_StartIntermission(void)
 
 				if (modeattacking == ATTACKING_RECORD)
 					Y_UpdateRecordReplays();
+				
+				if (completionEmblems)
+					CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)completionEmblems, completionEmblems > 1 ? "s" : "");
 			}
 
 			for (i = 0; i < 4; ++i)
@@ -1106,6 +1110,10 @@ void Y_StartIntermission(void)
 			{
 				if (!stagefailed)
 					mapvisited[gamemap-1] |= MV_BEATEN;
+				
+				// all emeralds/ultimate/perfect emblems won't be possible in ss, oh well?
+				if (completionEmblems)
+					CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)completionEmblems, completionEmblems > 1 ? "s" : "");
 			}
 
 			// give out ring bonuses