Maelstrom: Improved control selection in the multiplayer panel

From aaff12cad140e576ef48d94a13fb66726da4436a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 28 Nov 2025 14:52:45 -0800
Subject: [PATCH] Improved control selection in the multiplayer panel

---
 Data/Images/control16.png | Bin 721 -> 0 bytes
 Data/Images/control3.png  | Bin 2177 -> 0 bytes
 Data/Images/control31.png | Bin 0 -> 212 bytes
 Data/UI/lobby.xml         |  16 ++++++---
 game/MaelstromUI.cpp      |   7 ++--
 game/game.cpp             |  45 ++++++++++++++++--------
 game/gameinfo.cpp         |  72 ++++++++++++++++++++++++++++----------
 game/gameinfo.h           |   2 ++
 game/gameover.cpp         |   2 +-
 game/lobby.cpp            |  24 ++++---------
 10 files changed, 108 insertions(+), 60 deletions(-)
 delete mode 100644 Data/Images/control16.png
 delete mode 100644 Data/Images/control3.png
 create mode 100755 Data/Images/control31.png

diff --git a/Data/Images/control16.png b/Data/Images/control16.png
deleted file mode 100644
index 0fbe6cf6b69c50d95ab87c499262d74820b958e6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 721
zcmV;?0xtcDP)<h;3K|Lk000e1NJLTq001Na001Ni00000#B0Yy0000WV@Og>004R>
z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN
zS#tmYJ!b#_J!b(hFJ^ZD000McNliru=?fPGClem?<+=a>0yarRK~y-)b(FDdR6!8N
zf3tU7asd-Vuu5Sqf>F^wz!WMr1}p?i>l9W&L>m!pY^+iUv9J`;PEZuVG64k}tppJT
zW5is{<&JxAK8xLb@15dn=6$oXv)_EPvnFJK3}Fy|Beo1oaZzA=f`bMLQ~6CXS|*E5
zS;mz=q)o`4pM3e^)#5`-IQxNi%nHWV#v)w&R$(PuaZ-*=EMk`IZ^spr2^QMLdZ&T2
z4RN=SeP8cRc3*$Cgc-B$a(CA%l32;uvy}p1m|;vn0h1Y9WeKUuM765Ac_JZKWmQbZ
zwZ9fKMFcSYUrYd6?;i~ojug;th#9MsyK4=NGy`0d6bn{eU4sGJE4^Nr=ycj<R{7ic
zV5O+;J{Vms?T*hZqngvd-)%5j%NaJrY<?v??b{Q>#>D(fEZFA8ySt__G1=S2#r`gI
zX93i?xp9450%_71jfx7|eQD`{8V$xamhN4~OE26^7_6R346Mqe$9!3%zc(0;3Rh0o
z%oHi_TvA!ON_QV?W-X?H5B4me;0g+Lnd4%*%TSQD+_2CPBi#Z6yi`z72&DyuNVsua
zY+Gb#D3}5YvBM~iR2o@#Z@Gd3L|c5EI~G^lsf?k`O!cs!fZ~CzuScJqjpLWZ<owp@
zD^sOP_tKLP#t>7p!4tEGJ8vlErMr76=fm4Wa|o$c++7dY+T8r{^Vjyy@BUz@Ge|C%
zOYy-FV7=b)dWvlK)`j`}l+|Ktdiq@BJ`GOH=Nru*3j&L!Dx1rX00000NkvXXu0mjf
DmsB-K

diff --git a/Data/Images/control3.png b/Data/Images/control3.png
deleted file mode 100644
index 9cf639f88d3f9c7d596184925faa5529e8e304ba..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2177
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
DQ;Z_x

diff --git a/Data/Images/control31.png b/Data/Images/control31.png
new file mode 100755
index 0000000000000000000000000000000000000000..1563004cda7e8976c2ee5cfd0efaeab28440ce13
GIT binary patch
literal 212
zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1SIp4_|F3=#^NA%C&rs6b?Si}mUKs7M+SzC
z{oH>NS%G|oWRD<U28Jp%28M<f28Lfip@tU>45bDP46hOx7_4S6Fo+k-*%fF5l;AAz
zh%9Dc;5!V$jK}j=qyPokOFVsD*<W(Yh%w7Wa(3(k3Tb(|IEHu}e|y%E5ojTc!I$?n
uK_6~$t(kmf`bu}BnLcWhPo{vWyHSk)m|fS~#9jpIV(@hJb6Mw<&;$S|<T<|p

literal 0
HcmV?d00001

diff --git a/Data/UI/lobby.xml b/Data/UI/lobby.xml
index 79dcf932..5514b7d2 100644
--- a/Data/UI/lobby.xml
+++ b/Data/UI/lobby.xml
@@ -167,10 +167,13 @@
 						<Button name="control">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="16"/>
 						</Button>
-						<DialogLabel name="name" text="This is a name">
+						<DialogLabel name="desc">
+							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80"/>
+						</DialogLabel>
+						<DialogLabel name="name">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80" y="-10"/>
 						</DialogLabel>
-						<DialogLabel name="host" text="bwg101.corp.pacbell.net">
+						<DialogLabel name="host">
 							<Anchor anchorFrom="TOPLEFT" anchorTo="BOTTOMLEFT" anchor="name" y="2"/>
 						</DialogLabel>
 						<Image name="ping1" image="ping1">
@@ -195,6 +198,9 @@
 						<Button name="control">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="16"/>
 						</Button>
+						<DialogLabel name="desc">
+							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80"/>
+						</DialogLabel>
 						<DialogLabel name="name" text="This is a name">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80" y="-10"/>
 						</DialogLabel>
@@ -223,6 +229,9 @@
 						<Button name="control">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="16"/>
 						</Button>
+						<DialogLabel name="desc">
+							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80"/>
+						</DialogLabel>
 						<DialogLabel name="name" text="This is a name">
 							<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="label" x="80" y="-10"/>
 						</DialogLabel>
@@ -254,12 +263,11 @@
 		<DialogContainer name="control_dropdown" border="true" show="false">
 			<Elements>
 				<Button name="control0" image="control0"/>
-				<Button name="control3" image="control3"/>
+				<Button name="control31" image="control31"/>
 				<Button name="control1" image="control1"/>
 				<Button name="control2" image="control2"/>
 				<Button name="control4" image="control4"/>
 				<Button name="control8" image="control8"/>
-				<Button name="control16" image="control16"/>
 				<Button name="control32" image="control32"/>
 			</Elements>
 		</DialogContainer>
diff --git a/game/MaelstromUI.cpp b/game/MaelstromUI.cpp
index ddeb9203..5a8d9c65 100644
--- a/game/MaelstromUI.cpp
+++ b/game/MaelstromUI.cpp
@@ -203,13 +203,10 @@ MaelstromUI::MaelstromUI(FrameBuf *screen, Prefs *prefs) : UIManager(screen, pre
 	m_strings = hash_create(screen, hash_hash_string, hash_keymatch_string, hash_nuke_string_text);
 
 	/* Set up some conditions useful for UI loading */
-#ifdef USE_TOUCHCONTROL
-	SetCondition("TOUCH");
-#endif
-#if __IPHONEOS__ || __ANDROID__
+#if defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_ANDROID)
 	SetCondition("MOBILE");
 #endif
-#if __IPHONEOS__
+#if defined(SDL_PLATFORM_IOS)
 	SetCondition("IOS");
 #endif
 
diff --git a/game/game.cpp b/game/game.cpp
index 11b509df..a14db4ce 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -78,7 +78,24 @@ static bool SetupPlayers(void)
 					gDisplayed = i;
 				}
 			}
-			gPlayers[i]->SetControlType(gGameInfo.GetPlayer(i)->controlMask);
+
+			Uint8 controlMask = gGameInfo.GetPlayer(i)->controlMask;
+			if (controlMask == CONTROL_LOCAL) {
+				// Remove the controls that are used by other players
+				for (int j = 0; j < MAX_PLAYERS; ++j) {
+					if (!gGameInfo.IsValidPlayer(j)) {
+						continue;
+					}
+					if (i == j) {
+						continue;
+					}
+					Uint8 otherMask = gGameInfo.GetPlayer(j)->controlMask;
+					if (otherMask != CONTROL_LOCAL) {
+						controlMask &= ~otherMask;
+					}
+				}
+			}
+			gPlayers[i]->SetControlType(controlMask);
 		} else {
 			gPlayers[i]->SetControlType(CONTROL_NONE);
 		}
@@ -511,8 +528,7 @@ GamePanelDelegate::DrawStatus(Bool first)
 	}
 
 	if ( gGameInfo.IsMultiplayer() ) {
-#ifndef USE_TOUCHCONTROL
-		if (gReplay.IsPlaying()) {
+		if (gReplay.IsPlaying() && SDL_HasKeyboard()) {
 			char caption[BUFSIZ];
 
 			SDL_snprintf(caption, sizeof(caption), "Displaying player %d - press F1 to change", gDisplayed+1);
@@ -520,7 +536,6 @@ GamePanelDelegate::DrawStatus(Bool first)
 				m_multiplayerCaption->SetText(caption);
 			}
 		}
-#endif // USE_TOUCHCONTROL
 
 		/* Fill in the color by the frag count */
 		if (m_multiplayerColor) {
@@ -537,7 +552,7 @@ GamePanelDelegate::DrawStatus(Bool first)
 	if (m_shield) {
 		m_shield->SetWidth(fact);
 	}
-	
+
 	MultFactor = TheShip->GetBonusMult();
 	for (i = 0; (unsigned)i < SDL_arraysize(m_multiplier); ++i) {
 		if (!m_multiplier[i]) {
@@ -672,11 +687,11 @@ GamePanelDelegate::DoHousekeeping()
 	/* -- Maybe throw a multiplier up on the screen */
 	if (gMultiplierShown && (--gMultiplierShown == 0) )
 		MakeMultiplier();
-	
+
 	/* -- Maybe throw a prize(!) up on the screen */
 	if (gPrizeShown && (--gPrizeShown == 0) )
 		MakePrize();
-	
+
 	/* -- Maybe throw a bonus up on the screen */
 	if (gBonusShown && (--gBonusShown == 0) )
 		MakeBonus();
@@ -688,15 +703,15 @@ GamePanelDelegate::DoHousekeeping()
 	/* -- Maybe create a transcenfugal vortex */
 	if (gWhenGrav && (--gWhenGrav == 0) )
 		MakeGravity();
-	
+
 	/* -- Maybe create a recified space vehicle */
 	if (gWhenDamaged && (--gWhenDamaged == 0) )
 		MakeDamagedShip();
-	
+
 	/* -- Maybe create a autonominous tracking device */
 	if (gWhenHoming && (--gWhenHoming == 0) )
 		MakeHoming();
-	
+
 	/* -- Maybe make a supercranial destruction thang */
 	if (gWhenNova && (--gWhenNova == 0) )
 		MakeNova();
@@ -706,7 +721,7 @@ GamePanelDelegate::DoHousekeeping()
 		gLastStar = STAR_DELAY;
 		SetStar(FastRandom(MAX_STARS));
 	}
-	
+
 	/* -- Time for the next wave? */
 	if (gNumRocks == 0) {
 		if ( gWhenDone == 0 )
@@ -781,7 +796,7 @@ GamePanelDelegate::DoBonus()
 	if (label) {
 		label->Show();
 	}
-		
+
 	gGameInfo.SetLocalState(STATE_BONUS, true);
 
 	/* Fade out */
@@ -883,7 +898,7 @@ GamePanelDelegate::DoBonus()
 				TheShip->IncrScore(TheShip->GetBonus());
 				TheShip->IncrBonus(-TheShip->GetBonus());
 			}
-	
+
 			if (bonus) {
 				SDL_snprintf(numbuf, sizeof(numbuf), "%-5.1d", TheShip->GetBonus());
 				bonus->SetText(numbuf);
@@ -1019,10 +1034,10 @@ GamePanelDelegate::NextWave()
 	/* -- Create some asteroids */
 	for (i = 0; i < NewRoids; i++) {
 		int	randval;
-	
+
 		x = FastRandom(GAME_WIDTH) * SCALE_FACTOR;
 		y = 0;
-	
+
 		randval = FastRandom(10);
 
 		/* -- See what kind of asteroid to make */
diff --git a/game/gameinfo.cpp b/game/gameinfo.cpp
index f3421947..2e3bc94d 100644
--- a/game/gameinfo.cpp
+++ b/game/gameinfo.cpp
@@ -408,6 +408,23 @@ GameInfo::IsNetworkPlayer(int index) const
 	return (players[index].nodeID != localID);
 }
 
+bool
+GameInfo::OtherPlayerHasControl(int index, Uint8 controlMask)
+{
+	for (int i = 0; i < MAX_PLAYERS; ++i) {
+		if (!IsValidPlayer(i)) {
+			continue;
+		}
+		if (i == index) {
+			continue;
+		}
+		if (players[i].controlMask == controlMask) {
+			return true;
+		}
+	}
+	return false;
+}
+
 int
 GameInfo::GetNumPlayers() const
 {
@@ -475,6 +492,7 @@ GameInfo::BindPlayerToUI(int index, UIElement *element)
 	}
 
 	player->UI.element = element;
+	player->UI.desc = element->GetElement<UIElement>("desc");
 	player->UI.name = element->GetElement<UIElement>("name");
 	player->UI.host = element->GetElement<UIElement>("host");
 	player->UI.control = element->GetElement<UIElement>("control");
@@ -495,6 +513,20 @@ GameInfo::UpdateUI()
 	}
 }
 
+static const char *GetGamepadName(int index)
+{
+	const char *name = nullptr;
+	int count = 0;
+	SDL_JoystickID *gamepads = SDL_GetGamepads(&count);
+	if (gamepads) {
+		if (index < count) {
+			name = SDL_GetGamepadNameForID(gamepads[index]);
+		}
+		SDL_free(gamepads);
+	}
+	return name;
+}
+
 void
 GameInfo::UpdateUI(GameInfoPlayer *player)
 {
@@ -502,32 +534,36 @@ GameInfo::UpdateUI(GameInfoPlayer *player)
 		return;
 	}
 
-	if (player->UI.name) {
-		if (player->name[0]) {
-			player->UI.name->Show();
-			player->UI.name->SetText(player->name);
-		} else {
-			player->UI.name->Hide();
-		}
-	}
-	if (player->UI.host) {
+	if (player->UI.name && player->UI.host) {
 		const GameInfoNode *node = GetNodeByID(player->nodeID);
-		if (!node) {
-			player->UI.host->Hide();
-		} else if (node->nodeID == localID) {
-			//player->UI.host->Show();
-			//player->UI.host->SetText("localhost");
+		if (!node || node->nodeID == localID) {
+			player->UI.name->Hide();
 			player->UI.host->Hide();
 		} else {
+			player->UI.name->Show();
+			player->UI.name->SetText(player->name);
 			player->UI.host->Show();
 			player->UI.host->SetText(SDLNet_ResolveIP(&node->address));
 		}
 	}
-	if (player->UI.control) {
-		char name[128];
-		SDL_snprintf(name, sizeof(name), "Images/control%d.png", player->controlMask);
-		player->UI.control->SetImage(name);
+
+	char name[128];
+	SDL_snprintf(name, sizeof(name), "control%d", player->controlMask);
+	player->UI.control->SetImage(name);
+
+	if (player->UI.desc) {
+		const char *desc = NULL;
+
+		if (player->controlMask == CONTROL_JOYSTICK1) {
+			desc = GetGamepadName(0);
+		} else if (player->controlMask == CONTROL_JOYSTICK2) {
+			desc = GetGamepadName(1);
+		} else if (player->controlMask == CONTROL_JOYSTICK3) {
+			desc = GetGamepadName(2);
+		}
+		player->UI.desc->SetText(desc);
 	}
+
 	for (int i = 0; i < NUM_PING_STATES; ++i) {
 		UIElement *element = player->UI.ping_states[i];
 		if (element) {
diff --git a/game/gameinfo.h b/game/gameinfo.h
index ae782000..07e4d57f 100644
--- a/game/gameinfo.h
+++ b/game/gameinfo.h
@@ -96,6 +96,7 @@ struct GameInfoPlayer
 
 	struct {
 		UIElement *element;
+		UIElement *desc;
 		UIElement *name;
 		UIElement *host;
 		UIElement *control;
@@ -183,6 +184,7 @@ class GameInfo
 	bool IsValidPlayer(int index) const;
 	bool IsLocalPlayer(int index) const;
 	bool IsNetworkPlayer(int index) const;
+	bool OtherPlayerHasControl(int index, Uint8 controlMask);
 	int GetNumPlayers() const;
 
 	bool IsFull() const;
diff --git a/game/gameover.cpp b/game/gameover.cpp
index 9010e3c2..60b713a9 100644
--- a/game/gameover.cpp
+++ b/game/gameover.cpp
@@ -95,7 +95,7 @@ void GameOverPanelDelegate::OnShow()
 			SDL_snprintf(name, sizeof(name), "rank%d", nextLabel);
 			image = m_panel->GetElement<UIElement>(name);
 			if (image) {
-				SDL_snprintf(name, sizeof(name), "Images/player%d.png", final[i].Player);
+				SDL_snprintf(name, sizeof(name), "player%d", final[i].Player);
 				image->SetImage(name);
 				image->Show();
 			}
diff --git a/game/lobby.cpp b/game/lobby.cpp
index c8c3b07f..f6e7c23c 100644
--- a/game/lobby.cpp
+++ b/game/lobby.cpp
@@ -75,24 +75,14 @@ class ControlClickCallback : public UIClickCallback
 		}
 
 		// Show the control dialog
+		int num_gamepads = 0;
+		SDL_free(SDL_GetGamepads(&num_gamepads));
 		SetControl(CONTROL_NONE, (m_index > 0) && m_game.IsHosting());
-#ifdef USE_TOUCHCONTROL
-		SetControl(CONTROL_TOUCH, true);
-		SetControl(CONTROL_KEYBOARD, false);
-		SetControl(CONTROL_KEYBOARD|CONTROL_JOYSTICK1, false);
-		SetControl(CONTROL_JOYSTICK1, false);
-		SetControl(CONTROL_JOYSTICK2, false);
-		SetControl(CONTROL_JOYSTICK3, false);
-#else
-		int num_joysticks = 0;
-		SDL_free(SDL_GetJoysticks(&num_joysticks));
-		SetControl(CONTROL_TOUCH, false);
-		SetControl(CONTROL_KEYBOARD, true);
-		SetControl(CONTROL_KEYBOARD|CONTROL_JOYSTICK1, true);
-		SetControl(CONTROL_JOYSTICK1, num_joysticks > 0);
-		SetControl(CONTROL_JOYSTICK2, num_joysticks > 1);
-		SetControl(CONTROL_JOYSTICK3, num_joysticks > 2);
-#endif
+		SetControl(CONTROL_LOCAL, !m_game.OtherPlayerHasControl(m_index, CONTROL_LOCAL));
+		SetControl(CONTROL_KEYBOARD, SDL_HasKeyboard() && !m_game.OtherPlayerHasControl(m_index, CONTROL_KEYBOARD));
+		SetControl(CONTROL_JOYSTICK1, num_gamepads > 0 && !m_game.OtherPlayerHasControl(m_index, CONTROL_JOYSTICK1));
+		SetControl(CONTROL_JOYSTICK2, num_gamepads > 1 && !m_game.OtherPlayerHasControl(m_index, CONTROL_JOYSTICK2));
+		SetControl(CONTROL_JOYSTICK3, num_gamepads > 2 && !m_game.OtherPlayerHasControl(m_index, CONTROL_JOYSTICK3));
 		SetControl(CONTROL_NETWORK, (m_index > 0) && m_game.IsHosting());
 
 		m_dialog->SetAnchor(LEFT, RIGHT, m_button, -4, 0);