Maelstrom: Added a "Click to begin" step for Emscripten

From b951516ca6df0cd6406e98958898cb4d24681a02 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 16 Mar 2026 23:19:14 -0700
Subject: [PATCH] Added a "Click to begin" step for Emscripten

Web content will not play any sounds until you interact with the page.
---
 Data/UI/loading.xml  |  7 +++--
 game/MaelstromUI.cpp |  5 +++-
 game/init.cpp        | 71 ++++++++++++++++++++++++++++++++++++++++++--
 game/init.h          | 15 ++++++++++
 4 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/Data/UI/loading.xml b/Data/UI/loading.xml
index 80c01180..26eb75ad 100644
--- a/Data/UI/loading.xml
+++ b/Data/UI/loading.xml
@@ -1,8 +1,11 @@
-<Panel template="FramedPanel" enterSound="111" leaveSound="123">
+<Panel template="FramedPanel" delegate="LoadingPanel" leaveSound="123">
 	<Elements>
 		<Title name="image" id="130"/>
 
-		<Label template="SmallYellow" text="Loading...">
+		<Label template="SmallYellow" name="waiting" text="Click to begin" show="false">
+			<Anchor anchorFrom="CENTER" anchorTo="BOTTOM" anchor="image" y="20"/>
+		</Label>
+		<Label template="SmallYellow" name="loading" text="Loading...">
 			<Anchor anchorFrom="CENTER" anchorTo="BOTTOM" anchor="image" y="20"/>
 		</Label>
 
diff --git a/game/MaelstromUI.cpp b/game/MaelstromUI.cpp
index becb2871..ec06476b 100644
--- a/game/MaelstromUI.cpp
+++ b/game/MaelstromUI.cpp
@@ -24,6 +24,7 @@
 #include "main.h"
 #include "load.h"
 #include "controls.h"
+#include "init.h"
 #include "about.h"
 #include "game.h"
 #include "continue.h"
@@ -377,7 +378,9 @@ MaelstromUI::CreatePanel(const char *type, const char *name)
 UIPanelDelegate *
 MaelstromUI::CreatePanelDelegate(UIPanel *panel, const char *delegate)
 {
-	if (SDL_strcasecmp(delegate, "MainPanel") == 0) {
+	if (SDL_strcasecmp(delegate, "LoadingPanel") == 0) {
+		return new LoadingPanelDelegate(panel);
+	} else if (SDL_strcasecmp(delegate, "MainPanel") == 0) {
 		return new MainPanelDelegate(panel);
 	} else if (SDL_strcasecmp(delegate, "AboutPanel") == 0) {
 		return new AboutPanelDelegate(panel);
diff --git a/game/init.cpp b/game/init.cpp
index 375f175a..54cd2577 100644
--- a/game/init.cpp
+++ b/game/init.cpp
@@ -78,6 +78,7 @@ UITexture *gTripleFireIcon, *gShieldIcon;
 
 enum LoadingStage
 {
+	LOAD_STAGE_WAITING,
 	LOAD_STAGE_STARTING,
 	LOAD_STAGE_BLITS1,
 	LOAD_STAGE_BLITS2,
@@ -108,7 +109,7 @@ enum LoadingStage
 	LOAD_STAGE_SPRITES,
 	LOAD_STAGE_COMPLETE
 };
-static int gLoadingStage = LOAD_STAGE_STARTING;
+static int gLoadingStage = LOAD_STAGE_WAITING;
 
 // Local functions used in this file.
 static void DrawLoadBar();
@@ -820,8 +821,7 @@ bool StartInitialization(int window_width, int window_height, Uint32 window_flag
 	if (!InitResolutions(w, h)) {
 		return false;
 	}
-	window_flags |= SDL_WINDOW_HIDDEN;
-	if (screen->Init(w, h, window_flags, "Maelstrom", icon) < 0){
+	if (screen->Init(w, h, window_flags | SDL_WINDOW_HIDDEN, "Maelstrom", icon) < 0){
 		error("Fatal: %s\n", screen->Error());
 		return false;
 	}
@@ -958,6 +958,9 @@ bool ContinueInitialization()
 
 		gRunning = true;
 		break;
+
+	default:
+		break;
 	}
 
 	return true;
@@ -1351,3 +1354,65 @@ static int LoadSmallSprite(BlitPtr *theBlit, int baseID, int numFrames)
 {
 	return LoadSprite(false, theBlit, baseID, numFrames);
 }
+
+
+/* ----------------------------------------------------------------- */
+/* -- Delegate to handle the loading panel */
+
+void LoadingPanelDelegate::OnShow()
+{
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+	StartWaiting();
+#else
+	StartLoading();
+#endif
+}
+
+bool LoadingPanelDelegate::HandleEvent(const SDL_Event &event)
+{
+	if (gLoadingStage == LOAD_STAGE_WAITING) {
+		switch (event.type) {
+		case SDL_EVENT_KEY_DOWN:
+		case SDL_EVENT_MOUSE_BUTTON_DOWN:
+			StartLoading();
+			return true;
+		default:
+			break;
+		}
+	}
+	return false;
+}
+
+void LoadingPanelDelegate::StartWaiting()
+{
+	UIPanel *panel = ui->GetPanel(PANEL_LOADING);
+	if (panel) {
+		UIElement *waiting_label = panel->GetElement<UIElement>("waiting");
+		UIElement *loading_label = panel->GetElement<UIElement>("loading");
+		if (waiting_label) {
+			waiting_label->Show();
+		}
+		if (loading_label) {
+			loading_label->Hide();
+		}
+	}
+	gLoadingStage = LOAD_STAGE_WAITING;
+}
+
+void LoadingPanelDelegate::StartLoading()
+{
+	UIPanel *panel = ui->GetPanel(PANEL_LOADING);
+	if (panel) {
+		UIElement *waiting_label = panel->GetElement<UIElement>("waiting");
+		UIElement *loading_label = panel->GetElement<UIElement>("loading");
+		if (waiting_label) {
+			waiting_label->Hide();
+		}
+		if (loading_label) {
+			loading_label->Show();
+		}
+	}
+	sound->PlaySound(gPrizeAppears);
+	gLoadingStage = LOAD_STAGE_STARTING;
+}
+
diff --git a/game/init.h b/game/init.h
index bc41c172..e6a887e0 100644
--- a/game/init.h
+++ b/game/init.h
@@ -22,6 +22,21 @@
 #ifndef _init_h
 #define _init_h
 
+#include "../screenlib/UIPanel.h"
+
+class LoadingPanelDelegate : public UIPanelDelegate
+{
+public:
+	LoadingPanelDelegate(UIPanel *panel) : UIPanelDelegate(panel) { }
+
+	virtual void OnShow();
+	virtual bool HandleEvent(const SDL_Event &event);
+
+private:
+	void StartWaiting();
+	void StartLoading();
+};
+
 extern bool StartInitialization(int window_width, int window_height, Uint32 window_flags);
 extern bool ContinueInitialization();
 extern void CleanUp(void);