mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-04-25 08:04:33 -05:00
Add likely upcoming cards for Chill Season 2023 and option to disallow
This commit is contained in:
parent
750a430fa8
commit
dd7366c7d4
|
|
@ -75,11 +75,15 @@
|
|||
<p>Other players can join using a link to this page.<br/>
|
||||
<button type="button" id="shareLinkButton">Share link</button><button type="button" id="showQrCodeButton">Show QR code</button></p>
|
||||
<ul id="playerList"></ul>
|
||||
<h3>Game rules</h3>
|
||||
<label for="lobbyTimeLimitBox">
|
||||
Turn time limit:
|
||||
<input type="number" id="lobbyTimeLimitBox" min="10" max="120" step="10" placeholder="None"/>
|
||||
<span id="lobbyTimeLimitUnit">seconds</span>
|
||||
</label>
|
||||
<label for="lobbyAllowUpcomingCardsBox">
|
||||
<input type="checkbox" id="lobbyAllowUpcomingCardsBox"/> Allow upcoming cards
|
||||
</label>
|
||||
</section>
|
||||
<section id="lobbySelectedStageSection">
|
||||
<h3>Stage</h3>
|
||||
|
|
@ -619,6 +623,9 @@
|
|||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label for="gameSetupAllowUpcomingCardsBox"><input type="checkbox" id="gameSetupAllowUpcomingCardsBox" checked/> Allow upcoming cards</label>
|
||||
</p>
|
||||
<p>Stage selection:</p>
|
||||
<table>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ interface CustomRoomConfig {
|
|||
maxPlayers: number;
|
||||
turnTimeLimit: number | null;
|
||||
goalWinCount: number | null;
|
||||
allowUpcomingCards: boolean;
|
||||
stageSelectionMethodFirst: StageSelectionMethod;
|
||||
stageSelectionMethodAfterWin: StageSelectionMethod | null;
|
||||
stageSelectionMethodAfterDraw: StageSelectionMethod | null;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ interface Game {
|
|||
turnTimeLeft: number | null,
|
||||
/** The number of game wins needed to win the set, or null if no goal win count is set. */
|
||||
goalWinCount: number | null,
|
||||
/** Whether upcoming cards may be used. */
|
||||
allowUpcomingCards: boolean
|
||||
}
|
||||
|
||||
/** A UUID used to identify the client. */
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ function initTest(stage: Stage) {
|
|||
clear();
|
||||
testMode = true;
|
||||
gamePage.classList.add('deckTest');
|
||||
currentGame = { id: 'test', game: { state: GameState.Ongoing, maxPlayers: 2, players: [ ], turnNumber: 1, turnTimeLimit: null, turnTimeLeft: null, goalWinCount: null }, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ], stageSelectionPrompt: null }, webSocket: null };
|
||||
currentGame = { id: 'test', game: { state: GameState.Ongoing, maxPlayers: 2, players: [ ], turnNumber: 1, turnTimeLimit: null, turnTimeLeft: null, goalWinCount: null, allowUpcomingCards: true }, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ], stageSelectionPrompt: null }, webSocket: null };
|
||||
board.resize(stage.copyGrid());
|
||||
const startSpaces = stage.getStartSpaces(2);
|
||||
board.startSpaces = startSpaces;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const lobbyDeckButtons = new CheckButtonGroup<SavedDeck>(lobbyDeckList);
|
|||
const lobbyDeckSubmitButton = document.getElementById('submitDeckButton') as HTMLButtonElement;
|
||||
|
||||
const lobbyTimeLimitBox = document.getElementById('lobbyTimeLimitBox') as HTMLInputElement;
|
||||
const lobbyAllowUpcomingCardsBox = document.getElementById('lobbyAllowUpcomingCardsBox') as HTMLInputElement;
|
||||
const lobbyTimeLimitUnit = document.getElementById('lobbyTimeLimitUnit')!;
|
||||
|
||||
const qrCodeDialog = document.getElementById('qrCodeDialog') as HTMLDialogElement;
|
||||
|
|
@ -57,6 +58,7 @@ function initLobbyPage(url: string) {
|
|||
lobbyShareData = null;
|
||||
shareLinkButton.innerText = 'Copy link';
|
||||
}
|
||||
lobbyDeckSection.hidden = true;
|
||||
}
|
||||
|
||||
function showStageSelectionForm(prompt: StageSelectionPrompt | null, isReady: boolean) {
|
||||
|
|
@ -179,6 +181,7 @@ function lobbyResetSlots() {
|
|||
|
||||
function lobbyLockSettings(lock: boolean) {
|
||||
lobbyTimeLimitBox.readOnly = lock;
|
||||
lobbyAllowUpcomingCardsBox.disabled = lock;
|
||||
}
|
||||
|
||||
function clearReady() {
|
||||
|
|
@ -289,11 +292,13 @@ function initDeckSelection() {
|
|||
lobbyDeckButtons.add(button, deck);
|
||||
|
||||
buttonElement.addEventListener('click', () => {
|
||||
selectedDeck = deck;
|
||||
lobbyDeckSubmitButton.disabled = false;
|
||||
if (button.enabled) {
|
||||
selectedDeck = deck;
|
||||
lobbyDeckSubmitButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!deck.isValid) {
|
||||
if (!deck.isValid || (!currentGame.game.allowUpcomingCards && deck.cards.find(n => cardDatabase.get(n).number < 0))) {
|
||||
button.enabled = false;
|
||||
} else if (deck.name == lastDeckName) {
|
||||
selectedDeck = deck;
|
||||
|
|
@ -311,13 +316,22 @@ function initDeckSelection() {
|
|||
|
||||
lobbyTimeLimitBox.addEventListener('change', () => {
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/setTurnTimeLimit`);
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/setGameSettings`);
|
||||
let data = new URLSearchParams();
|
||||
data.append('clientToken', clientToken);
|
||||
data.append('turnTimeLimit', lobbyTimeLimitBox.value || '');
|
||||
req.send(data.toString());
|
||||
});
|
||||
|
||||
lobbyAllowUpcomingCardsBox.addEventListener('change', () => {
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/setGameSettings`);
|
||||
let data = new URLSearchParams();
|
||||
data.append('clientToken', clientToken);
|
||||
data.append('allowUpcomingCards', lobbyAllowUpcomingCardsBox.checked.toString());
|
||||
req.send(data.toString());
|
||||
});
|
||||
|
||||
deckSelectionForm.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
if (selectedDeck == null) return;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ const gameSetupForm = document.getElementById('gameSetupForm') as HTMLFormElemen
|
|||
const maxPlayersBox = document.getElementById('maxPlayersBox') as HTMLSelectElement;
|
||||
const turnTimeLimitBox = document.getElementById('turnTimeLimitBox') as HTMLInputElement;
|
||||
const goalWinCountBox = document.getElementById('goalWinCountBox') as HTMLSelectElement;
|
||||
const gameSetupAllowUpcomingCardsBox = document.getElementById('gameSetupAllowUpcomingCardsBox') as HTMLInputElement;
|
||||
const stageSelectionRuleFirstBox = document.getElementById('stageSelectionRuleFirstBox') as HTMLSelectElement;
|
||||
const stageSelectionRuleAfterWinBox = document.getElementById('stageSelectionRuleAfterWinBox') as HTMLSelectElement;
|
||||
const stageSelectionRuleAfterDrawBox = document.getElementById('stageSelectionRuleAfterDrawBox') as HTMLSelectElement;
|
||||
|
|
@ -153,10 +154,11 @@ function createRoom(useOptionsForm: boolean) {
|
|||
data.append('name', name);
|
||||
data.append('clientToken', clientToken);
|
||||
if (useOptionsForm) {
|
||||
const settings = {
|
||||
const settings = <CustomRoomConfig> {
|
||||
maxPlayers: parseInt(maxPlayersBox.value),
|
||||
turnTimeLimit: turnTimeLimitBox.value ? turnTimeLimitBox.valueAsNumber : null,
|
||||
goalWinCount: goalWinCountBox.value ? parseInt(goalWinCountBox.value) : null,
|
||||
allowUpcomingCards: gameSetupAllowUpcomingCardsBox.checked,
|
||||
stageSelectionMethodFirst: StageSelectionMethod[stageSelectionRuleFirstBox.value as keyof typeof StageSelectionMethod],
|
||||
stageSelectionMethodAfterWin: stageSelectionRuleAfterWinBox.value == 'Inherit' ? null : StageSelectionMethod[stageSelectionRuleAfterWinBox.value as keyof typeof StageSelectionMethod],
|
||||
stageSelectionMethodAfterDraw: stageSelectionRuleAfterDrawBox.value == 'Inherit' ? null : StageSelectionMethod[stageSelectionRuleAfterDrawBox.value as keyof typeof StageSelectionMethod],
|
||||
|
|
@ -172,6 +174,7 @@ function createRoom(useOptionsForm: boolean) {
|
|||
data.append('turnTimeLimit', turnTimeLimitBox.value);
|
||||
if (goalWinCountBox.value)
|
||||
data.append('goalWinCount', goalWinCountBox.value);
|
||||
data.append('allowUpcomingCards', settings.allowUpcomingCards.toString());
|
||||
|
||||
const stageSelectionRuleFirst = {
|
||||
method: settings.stageSelectionMethodFirst,
|
||||
|
|
@ -363,6 +366,7 @@ window.addEventListener('popstate', () => {
|
|||
maxPlayersBox.value = userConfig.lastCustomRoomConfig.maxPlayers.toString();
|
||||
turnTimeLimitBox.value = userConfig.lastCustomRoomConfig.turnTimeLimit?.toString() ?? '';
|
||||
goalWinCountBox.value = userConfig.lastCustomRoomConfig.goalWinCount?.toString() ?? '';
|
||||
gameSetupAllowUpcomingCardsBox.checked = userConfig.lastCustomRoomConfig.allowUpcomingCards ?? true;
|
||||
stageSelectionRuleFirstBox.value = StageSelectionMethod[userConfig.lastCustomRoomConfig.stageSelectionMethodFirst]
|
||||
stageSelectionRuleAfterWinBox.value = userConfig.lastCustomRoomConfig.stageSelectionMethodAfterWin != null ? StageSelectionMethod[userConfig.lastCustomRoomConfig.stageSelectionMethodAfterWin] : 'Inherit';
|
||||
stageSelectionRuleAfterDrawBox.value = userConfig.lastCustomRoomConfig.stageSelectionMethodAfterDraw != null ? StageSelectionMethod[userConfig.lastCustomRoomConfig.stageSelectionMethodAfterDraw] : 'Inherit';
|
||||
|
|
|
|||
|
|
@ -183,7 +183,8 @@ class ReplayLoader {
|
|||
turnNumber: 0,
|
||||
turnTimeLimit: null,
|
||||
turnTimeLeft: null,
|
||||
goalWinCount: goalWinCount
|
||||
goalWinCount: goalWinCount,
|
||||
allowUpcomingCards: true
|
||||
},
|
||||
me: null,
|
||||
webSocket: null
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ function onInitialise(callback: () => void) {
|
|||
|
||||
function initCardDatabase(cards: Card[]) {
|
||||
deckEditInitCardDatabase(cards);
|
||||
if (!cards.find(c => c.number < 0))
|
||||
gameSetupAllowUpcomingCardsBox.parentElement!.hidden = true;
|
||||
}
|
||||
function initStageDatabase(stages: Stage[]) {
|
||||
preGameInitStageDatabase(stages);
|
||||
|
|
@ -108,6 +110,7 @@ function onGameSettingsChange() {
|
|||
if (currentGame == null) return;
|
||||
if (lobbyTimeLimitBox.value != currentGame.game.turnTimeLimit?.toString() ?? '')
|
||||
lobbyTimeLimitBox.value = currentGame.game.turnTimeLimit?.toString() ?? '';
|
||||
lobbyAllowUpcomingCardsBox.checked = currentGame.game.allowUpcomingCards;
|
||||
}
|
||||
|
||||
function onGameStateChange(game: any, playerData: PlayerData | null) {
|
||||
|
|
@ -271,6 +274,7 @@ function setupWebSocket(gameID: string) {
|
|||
turnTimeLimit: payload.data.turnTimeLimit,
|
||||
turnTimeLeft: payload.data.turnTimeLeft,
|
||||
goalWinCount: payload.data.goalWinCount,
|
||||
allowUpcomingCards: payload.data.allowUpcomingCards
|
||||
},
|
||||
me: payload.playerData,
|
||||
webSocket: webSocket,
|
||||
|
|
@ -330,6 +334,7 @@ function setupWebSocket(gameID: string) {
|
|||
switch (payload.event) {
|
||||
case 'settingsChange':
|
||||
currentGame.game.turnTimeLimit = payload.data.turnTimeLimit;
|
||||
currentGame.game.allowUpcomingCards = payload.data.allowUpcomingCards;
|
||||
onGameSettingsChange();
|
||||
break;
|
||||
case 'join':
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ dialog::backdrop {
|
|||
flex-grow: 1;
|
||||
}
|
||||
.cardButton:is([data-card-number="163"], [data-card-number="166"], [data-card-number="196"], [data-card-number="197"], [data-card-number="199"],
|
||||
[data-card-number="202"], [data-card-number="216"]) .cardName {
|
||||
[data-card-number="202"], [data-card-number="216"], [data-card-number="-20"]) .cardName {
|
||||
position: absolute;
|
||||
left: -1em;
|
||||
right: -1em;
|
||||
|
|
|
|||
|
|
@ -2216,14 +2216,94 @@ public static class CardDatabase {
|
|||
{ 0, 0, 0, 0, 0, I, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}) { InkColour1 = new(84, 142, 122), InkColour2 = new(193, 111, 98) },
|
||||
new(-13, "Foil Squeezer", Rarity.Common, 0.95f, "ShooterFlash01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, S, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, I, I, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-14, "Snipewriter 5B", Rarity.Common, 0.86f, "ChargerPencil01", new Space[,] {
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, S, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, I, 0, 0, 0 },
|
||||
{ 0, 0, I, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-15, "Enperry\nSplat Dualies", Rarity.Common, 0.97f, "ManeuverNormal01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, S, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, I, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-16, "Undercover\nSorella Brella", Rarity.Common, 0.96f, "ShelterCompact01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, S, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, 0, I, 0, 0, 0 },
|
||||
{ 0, I, 0, I, 0, I, 0, 0 },
|
||||
{ 0, 0, 0, I, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-17, "Custom Blaster", Rarity.Common, 0.8f, "BlasterMiddle01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, S, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, I, 0, 0, 0 },
|
||||
{ 0, 0, I, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-18, "New S-BLAST", Rarity.Common, 0.97f, "BlasterPrecision01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, S, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, I, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-19, "Painbrush\nNouveau", Rarity.Common, 1f, "BrushHeavy01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, S, I, 0, 0, 0, 0 },
|
||||
{ 0, I, I, I, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, I, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, I, I, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, I, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
new(-20, "Splatana Stamper\nNouveau", Rarity.Common, 0.69f, "SaberNormal01", new Space[,] {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, I, S, I, I, I, I, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
}),
|
||||
];
|
||||
|
||||
private static readonly Dictionary<int, Card> byAltNumber;
|
||||
|
||||
public static int LastOfficialCardNumber { get; }
|
||||
|
||||
public static Version Version { get; } = new(5, 0, 2, 0);
|
||||
public static DateTime LastModified { get; } = new(2023, 10, 9, 22, 30, 0, DateTimeKind.Utc);
|
||||
public static Version Version { get; } = new(5, 1, 0, 0);
|
||||
public static DateTime LastModified { get; } = new(2023, 11, 24, 0, 0, 0, DateTimeKind.Utc);
|
||||
public static string JSON { get; }
|
||||
public static ReadOnlyCollection<Card> Cards { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ public class Game(int maxPlayers) {
|
|||
[JsonIgnore]
|
||||
internal DateTime abandonedSince = DateTime.UtcNow;
|
||||
|
||||
public bool AllowUpcomingCards { get; set; } = true;
|
||||
|
||||
public required StageSelectionRules StageSelectionRuleFirst { get; set; }
|
||||
public required StageSelectionRules StageSelectionRuleAfterWin { get; set; }
|
||||
public required StageSelectionRules StageSelectionRuleAfterDraw { get; set; }
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ using Timer = System.Timers.Timer;
|
|||
|
||||
namespace TableturfBattleServer;
|
||||
|
||||
internal class Program {
|
||||
internal partial class Program {
|
||||
internal static HttpServer? httpServer;
|
||||
|
||||
internal static Dictionary<Guid, Game> games = [];
|
||||
|
|
@ -25,6 +25,7 @@ internal class Program {
|
|||
|
||||
private const int InactiveGameLimit = 1000;
|
||||
private static readonly TimeSpan InactiveGameTimeout = TimeSpan.FromMinutes(5);
|
||||
internal static readonly char[] DELIMITERS = new[] { ',', ' ' };
|
||||
|
||||
private static string? GetClientRootPath() {
|
||||
var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
|
@ -160,6 +161,13 @@ internal class Program {
|
|||
} else
|
||||
clientToken = Guid.NewGuid();
|
||||
|
||||
var allowUpcomingCards = false;
|
||||
if (d.TryGetValue("allowUpcomingCards", out var allowUpcomingCardsString)) {
|
||||
if (!bool.TryParse(allowUpcomingCardsString, out allowUpcomingCards))
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "allowUpcomingCards was invalid."));
|
||||
} else
|
||||
allowUpcomingCards = true;
|
||||
|
||||
StageSelectionRules? stageSelectionRuleFirst = null, stageSelectionRuleAfterWin = null, stageSelectionRuleAfterDraw = null;
|
||||
if (d.TryGetValue("stageSelectionRuleFirst", out var json1)) {
|
||||
if (!TryParseStageSelectionRule(json1, out stageSelectionRuleFirst) || stageSelectionRuleFirst.Method is StageSelectionMethod.Same or StageSelectionMethod.Counterpick) {
|
||||
|
|
@ -197,7 +205,7 @@ internal class Program {
|
|||
} else
|
||||
spectate = false;
|
||||
|
||||
var game = new Game(maxPlayers) { GoalWinCount = goalWinCount, TurnTimeLimit = turnTimeLimit, StageSelectionRuleFirst = stageSelectionRuleFirst, StageSelectionRuleAfterWin = stageSelectionRuleAfterWin, StageSelectionRuleAfterDraw = stageSelectionRuleAfterDraw, ForceSameDeckAfterDraw = forceSameDeckAfterDraw };
|
||||
var game = new Game(maxPlayers) { GoalWinCount = goalWinCount, TurnTimeLimit = turnTimeLimit, AllowUpcomingCards = allowUpcomingCards, StageSelectionRuleFirst = stageSelectionRuleFirst, StageSelectionRuleAfterWin = stageSelectionRuleAfterWin, StageSelectionRuleAfterDraw = stageSelectionRuleAfterDraw, ForceSameDeckAfterDraw = forceSameDeckAfterDraw };
|
||||
if (!spectate)
|
||||
game.TryAddPlayer(new(game, name, clientToken), out _, out _);
|
||||
games.Add(game.ID, game);
|
||||
|
|
@ -214,7 +222,7 @@ internal class Program {
|
|||
} else if (e.Request.RawUrl == "/api/stages") {
|
||||
SetStaticResponse(e.Request, e.Response, StageDatabase.JSON, StageDatabase.Version.ToString(), StageDatabase.LastModified);
|
||||
} else {
|
||||
var m = Regex.Match(e.Request.RawUrl, @"^/api/games/([\w-]+)(?:/(\w+)(?:\?clientToken=([\w-]+))?)?$", RegexOptions.Compiled);
|
||||
var m = GamePathRegex().Match(e.Request.RawUrl);
|
||||
if (m.Success) {
|
||||
if (!Guid.TryParse(m.Groups[1].Value, out var gameID)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.BadRequest, "InvalidGameID", "Invalid game ID."));
|
||||
|
|
@ -304,7 +312,7 @@ internal class Program {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "setTurnTimeLimit": {
|
||||
case "setGameSettings": {
|
||||
if (e.Request.HttpMethod != "POST") {
|
||||
e.Response.AddHeader("Allow", "POST");
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.MethodNotAllowed, "MethodNotAllowed", "Invalid request method for this endpoint."));
|
||||
|
|
@ -324,17 +332,23 @@ internal class Program {
|
|||
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."));
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "Invalid turn time limit."));
|
||||
return;
|
||||
} else
|
||||
game.TurnTimeLimit = turnTimeLimit2;
|
||||
} else {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidTurnTimeLimit", "Invalid turn time limit."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (d.TryGetValue("allowUpcomingCards", out var allowUpcomingCardsString)) {
|
||||
if (!bool.TryParse(allowUpcomingCardsString, out var allowUpcomingCards)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "Invalid allowUpcomingCards."));
|
||||
return;
|
||||
} else
|
||||
game.AllowUpcomingCards = allowUpcomingCards;
|
||||
}
|
||||
|
||||
game.SendEvent("settingsChange", game, false);
|
||||
|
|
@ -363,7 +377,7 @@ internal class Program {
|
|||
}
|
||||
|
||||
var stages = new HashSet<int>();
|
||||
foreach (var field in stagesString.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)) {
|
||||
foreach (var field in stagesString.Split(DELIMITERS, StringSplitOptions.RemoveEmptyEntries)) {
|
||||
if (!int.TryParse(field, out var i)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.BadRequest, "InvalidStage", "Invalid stages."));
|
||||
return;
|
||||
|
|
@ -445,6 +459,10 @@ internal class Program {
|
|||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidDeckCards", "Deck cannot have duplicates."));
|
||||
return;
|
||||
}
|
||||
if (!game.AllowUpcomingCards && cardNumber < 0 && CardDatabase.GetCard(cardNumber).Number < 0) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "ForbiddenDeck", "Upcoming cards cannot be used in this game."));
|
||||
return;
|
||||
}
|
||||
cards[i] = cardNumber;
|
||||
}
|
||||
|
||||
|
|
@ -697,4 +715,7 @@ internal class Program {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^/api/games/([\w-]+)(?:/(\w+)(?:\?clientToken=([\w-]+))?)?$", RegexOptions.Compiled)]
|
||||
private static partial Regex GamePathRegex();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user