diff --git a/TableturfBattleClient/index.html b/TableturfBattleClient/index.html
index 3ef2097..03c3c1b 100644
--- a/TableturfBattleClient/index.html
+++ b/TableturfBattleClient/index.html
@@ -73,7 +73,11 @@
Other players can join using a link to this page.
-
+
Stage
diff --git a/TableturfBattleClient/src/Pages/LobbyPage.ts b/TableturfBattleClient/src/Pages/LobbyPage.ts
index 6f235f8..7105c6c 100644
--- a/TableturfBattleClient/src/Pages/LobbyPage.ts
+++ b/TableturfBattleClient/src/Pages/LobbyPage.ts
@@ -101,6 +101,12 @@ function lobbyResetSlots() {
playerListItems.push(el);
playerList.appendChild(el);
}
+
+ lobbyLockSettings(currentGame.me?.playerIndex != 0);
+}
+
+function lobbyLockSettings(lock: boolean) {
+ lobbyTimeLimitBox.readOnly = lock;
}
function clearReady() {
@@ -169,6 +175,15 @@ function initDeckSelection() {
}
}
+lobbyTimeLimitBox.addEventListener('change', () => {
+ let req = new XMLHttpRequest();
+ req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/setTurnTimeLimit`);
+ let data = new URLSearchParams();
+ data.append('clientToken', clientToken);
+ data.append('turnTimeLimit', lobbyTimeLimitBox.value || '');
+ req.send(data.toString());
+});
+
deckSelectionForm.addEventListener('submit', e => {
e.preventDefault();
if (selectedDeck == null) return;
diff --git a/TableturfBattleClient/src/app.ts b/TableturfBattleClient/src/app.ts
index 7ce42b3..daaa4e8 100644
--- a/TableturfBattleClient/src/app.ts
+++ b/TableturfBattleClient/src/app.ts
@@ -95,6 +95,12 @@ function clearUrlFromGame() {
location.hash = '';
}
+function onGameSettingsChange() {
+ if (currentGame == null) return;
+ if (lobbyTimeLimitBox.value != currentGame.turnTimeLimit?.toString() ?? '')
+ lobbyTimeLimitBox.value = currentGame.turnTimeLimit?.toString() ?? '';
+}
+
function onGameStateChange(game: any, playerData: PlayerData | null) {
if (currentGame == null)
throw new Error('currentGame is null');
@@ -113,6 +119,9 @@ function onGameStateChange(game: any, playerData: PlayerData | null) {
gamePage.dataset.myPlayerIndex = playerData ? playerData.playerIndex.toString() : '';
gamePage.dataset.uiBaseColourIsSpecialColour = playerData && game.players[playerData.playerIndex].uiBaseColourIsSpecialColour ? 'true' : 'false';
+ if (game.stage != GameState.WaitingForPlayers)
+ lobbyLockSettings(true);
+
redrawModal.hidden = true;
gamePage.classList.remove('gameEnded');
switch (game.state) {
@@ -252,8 +261,7 @@ function setupWebSocket(gameID: string) {
lobbyResetSlots();
for (let i = 0; i < currentGame.players.length; i++)
lobbyAddPlayer(i);
- lobbyTimeLimitBox.value = currentGame.turnTimeLimit?.toString() ?? '';
- lobbyTimeLimitUnit.hidden = currentGame.turnTimeLimit == null;
+ onGameSettingsChange();
for (let i = 0; i < playerBars.length; i++) {
playerBars[i].visible = i < currentGame.maxPlayers;
@@ -298,6 +306,10 @@ function setupWebSocket(gameID: string) {
return;
}
switch (payload.event) {
+ case 'settingsChange':
+ currentGame.turnTimeLimit = payload.data.turnTimeLimit;
+ onGameSettingsChange();
+ break;
case 'join':
if (payload.data.playerIndex == currentGame.players.length) {
currentGame.players.push(payload.data.player);
diff --git a/TableturfBattleServer/Program.cs b/TableturfBattleServer/Program.cs
index 8bddffd..d0774ae 100644
--- a/TableturfBattleServer/Program.cs
+++ b/TableturfBattleServer/Program.cs
@@ -264,6 +264,43 @@ internal class Program {
}
break;
}
+ case "setTurnTimeLimit": {
+ if (e.Request.HttpMethod != "POST") {
+ e.Response.AddHeader("Allow", "POST");
+ SetErrorResponse(e.Response, new(HttpStatusCode.MethodNotAllowed, "MethodNotAllowed", "Invalid request method for this endpoint."));
+ } else if (e.Request.ContentLength64 >= 65536) {
+ e.Response.StatusCode = (int) HttpStatusCode.RequestEntityTooLarge;
+ } else {
+ var d = DecodeFormData(e.Request.InputStream);
+ if (!d.TryGetValue("clientToken", out var tokenString) || !Guid.TryParse(tokenString, out var clientToken)) {
+ SetErrorResponse(e.Response, new(HttpStatusCode.BadRequest, "InvalidClientToken", "Invalid client token."));
+ return;
+ }
+ if (game.State != GameState.WaitingForPlayers) {
+ SetErrorResponse(e.Response, new(HttpStatusCode.Gone, "GameAlreadyStarted", "The game has already started."));
+ return;
+ }
+ if (!game.GetPlayer(clientToken, out var playerIndex, out var player) || playerIndex != 0) {
+ SetErrorResponse(e.Response, new(HttpStatusCode.Forbidden, "AccessDenied", "Only the host can do that."));
+ return;
+ }
+ if (d.TryGetValue("turnTimeLimit", out var turnTimeLimitString)) {
+ if (turnTimeLimitString == "")
+ game.TurnTimeLimit = null;
+ else if (!int.TryParse(turnTimeLimitString, out var turnTimeLimit2) || turnTimeLimit2 < 10) {
+ SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidTurnTimeLimit", "Invalid turn time limit."));
+ return;
+ } else
+ game.TurnTimeLimit = turnTimeLimit2;
+ } else {
+ SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidTurnTimeLimit", "Invalid turn time limit."));
+ return;
+ }
+
+ game.SendEvent("settingsChange", game, false);
+ }
+ break;
+ }
case "chooseStage": {
if (e.Request.HttpMethod != "POST") {
e.Response.AddHeader("Allow", "POST");