diff --git a/src/dehacked.c b/src/dehacked.c
index 1ba9da30cfed330d4adcb1571b3b42053ada3b94..a1e336064909ac9f199c3a5043069f44a2c0d1b3 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1838,6 +1838,8 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
 				textprompts[num]->page[pagenum].nextprompt = usi;
 			else if (fastcmp(word, "NEXTPAGE"))
 				textprompts[num]->page[pagenum].nextpage = usi;
+			else if (fastcmp(word, "NEXTTAG"))
+				strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33);
 			else if (fastcmp(word, "TIMETONEXT"))
 				textprompts[num]->page[pagenum].timetonext = get_number(word2);
 			else
diff --git a/src/doomstat.h b/src/doomstat.h
index 5066e2c3b288ea6cec37a20a258022a57c0892f8..a0c1eda1c3ad9fe5efdc49d7e2432623f5fad05f 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -211,6 +211,7 @@ typedef struct
 	sfxenum_t textsfx; // sfx_ id for printing text
 	UINT8 nextprompt; // next prompt to jump to, one-based. 0 = current prompt
 	UINT8 nextpage; // next page to jump to, one-based. 0 = next page within prompt->numpages
+	char nexttag[33]; // next tag to jump to. If set, this overrides nextprompt and nextpage.
 	INT32 timetonext; // time in tics to jump to next page automatically. 0 = don't jump automatically
 	char *text;
 } textpage_t;
diff --git a/src/f_finale.c b/src/f_finale.c
index 25a1150b30502e5c4e0b35c82502e65a0579025f..e054ef264495bce8c5edaddd1ab3bae93e5bd15d 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2137,25 +2137,31 @@ static void F_PreparePageText(char *pagetext)
 
 static void F_AdvanceToNextPage(void)
 {
-	INT32 nextprompt = textprompts[cutnum]->page[scenenum].nextprompt,
-		nextpage = textprompts[cutnum]->page[scenenum].nextpage,
+	INT32 nextprompt = textprompts[cutnum]->page[scenenum].nextprompt ? textprompts[cutnum]->page[scenenum].nextprompt - 1 : INT32_MAX,
+		nextpage = textprompts[cutnum]->page[scenenum].nextpage ? textprompts[cutnum]->page[scenenum].nextpage - 1 : INT32_MAX,
 		oldcutnum = cutnum;
 
+	if (textprompts[cutnum]->page[scenenum].nexttag[0])
+		F_GetPromptPageByNamedTag(textprompts[cutnum]->page[scenenum].nexttag, &nextprompt, &nextpage);
+
 	// determine next prompt
-	if (nextprompt)
+	if (nextprompt != INT32_MAX)
 	{
-		if (nextprompt <= MAX_PROMPTS && textprompts[nextprompt-1])
-			cutnum = nextprompt-1;
+		if (nextprompt <= MAX_PROMPTS && textprompts[nextprompt])
+			cutnum = nextprompt;
 		else
 			cutnum = INT32_MAX;
 	}
 
 	// determine next page
-	if (nextpage)
+	if (nextpage != INT32_MAX)
 	{
-		scenenum = nextpage-1;
-		if (scenenum >= MAX_PAGES || scenenum > textprompts[cutnum]->numpages-1)
-			scenenum = INT32_MAX;
+		if (nextprompt != INT32_MAX)
+		{
+			scenenum = nextpage;
+			if (scenenum >= MAX_PAGES || scenenum > textprompts[cutnum]->numpages-1)
+				scenenum = INT32_MAX;
+		}
 	}
 	else
 	{
@@ -2345,41 +2351,43 @@ static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length)
 	return suffixed;
 }
 
-void F_StartTextPromptByNamedTag(char *tag, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime)
+void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum)
 {
-	INT32 promptnum, pagenum, nosuffixpromptnum = INT32_MAX, nosuffixpagenum = INT32_MAX;
+	INT32 nosuffixpromptnum = INT32_MAX, nosuffixpagenum = INT32_MAX;
 	INT32 tutorialpromptnum = (tutorialmode) ? TUTORIAL_PROMPT-1 : 0;
 	boolean suffixed = false, found = false;
 	char suffixedtag[33];
 
+	*promptnum = *pagenum = INT32_MAX;
+
 	if (!tag || !tag[0])
 		return;
 
 	strncpy(suffixedtag, tag, 33);
 	suffixedtag[32] = 0;
-
+	tutorialmode = true;
 	if (tutorialmode)
-		suffixed = F_GetTextPromptTutorialTag(suffixedtag, 33);
+		suffixed = F_GetTextPromptTutorialTag(suffixedtag, 33); tutorialmode = false;
 
-	for (promptnum = 0 + tutorialpromptnum; promptnum < MAX_PROMPTS; promptnum++)
+	for (*promptnum = 0 + tutorialpromptnum; *promptnum < MAX_PROMPTS; (*promptnum)++)
 	{
-		if (!textprompts[promptnum])
+		if (!textprompts[*promptnum])
 			continue;
 
-		for (pagenum = 0; pagenum < textprompts[promptnum]->numpages && pagenum < MAX_PAGES; pagenum++)
+		for (*pagenum = 0; *pagenum < textprompts[*promptnum]->numpages && *pagenum < MAX_PAGES; (*pagenum)++)
 		{
-			if (suffixed && fastcmp(suffixedtag, textprompts[promptnum]->page[pagenum].tag))
+			if (suffixed && fastcmp(suffixedtag, textprompts[*promptnum]->page[*pagenum].tag))
 			{
 				// this goes first because fastcmp ends early if first string is shorter
 				found = true;
 				break;
 			}
-			else if (nosuffixpromptnum == INT32_MAX && nosuffixpagenum == INT32_MAX && fastcmp(tag, textprompts[promptnum]->page[pagenum].tag))
+			else if (nosuffixpromptnum == INT32_MAX && nosuffixpagenum == INT32_MAX && fastcmp(tag, textprompts[*promptnum]->page[*pagenum].tag))
 			{
 				if (suffixed)
 				{
-					nosuffixpromptnum = promptnum;
-					nosuffixpagenum = pagenum;
+					nosuffixpromptnum = *promptnum;
+					nosuffixpagenum = *pagenum;
 					// continue searching for the suffixed tag
 				}
 				else
@@ -2397,13 +2405,11 @@ void F_StartTextPromptByNamedTag(char *tag, mobj_t *mo, UINT16 postexectag, bool
 	if (suffixed && !found && nosuffixpromptnum != INT32_MAX && nosuffixpagenum != INT32_MAX)
 	{
 		found = true;
-		promptnum = nosuffixpromptnum;
-		pagenum = nosuffixpagenum;
+		*promptnum = nosuffixpromptnum;
+		*pagenum = nosuffixpagenum;
 	}
 
-	if (found)
-		F_StartTextPrompt(promptnum, pagenum, mo, postexectag, blockcontrols, freezerealtime);
-	else
+	if (!found)
 		CONS_Debug(DBG_GAMELOGIC, "Text prompt: Can't find a page with named tag %s or suffixed tag %s\n", tag, suffixedtag);
 }
 
diff --git a/src/f_finale.h b/src/f_finale.h
index 093389c1f7c997207d6efc4e6d0e941a83dc7f8f..ca43e17e0aac68010a1c262cc30a3fd4ef76217c 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -53,7 +53,7 @@ void F_CutsceneDrawer(void);
 void F_EndCutScene(void);
 
 void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime);
-void F_StartTextPromptByNamedTag(char *tag, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime);
+void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum);
 void F_TextPromptDrawer(void);
 void F_EndTextPrompt(boolean forceexec, boolean noexec);
 boolean F_GetPromptHideHudAll(void);
diff --git a/src/p_spec.c b/src/p_spec.c
index 24cad33d56461d2dd09be1ce6be88d64a4cbbefe..cd9c7cffd48d52bda7ab0bcae920022360216b59 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3776,10 +3776,12 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 				if (closetextprompt)
 					F_EndTextPrompt(false, false);
-				else if (callbynamedtag && sides[line->sidenum[0]].text && sides[line->sidenum[0]].text[0])
-					F_StartTextPromptByNamedTag(sides[line->sidenum[0]].text, mo, runpostexec ? postexectag : 0, blockcontrols, freezerealtime);
 				else
+				{
+					if (callbynamedtag && sides[line->sidenum[0]].text && sides[line->sidenum[0]].text[0])
+						F_GetPromptPageByNamedTag(sides[line->sidenum[0]].text, &promptnum, &pagenum);
 					F_StartTextPrompt(promptnum, pagenum, mo, runpostexec ? postexectag : 0, blockcontrols, freezerealtime);
+				}
 			}
 			break;