mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-08-20 01:51:51 -05:00
Prevent choosing stages that don't support the number of players in the game
This commit is contained in:
@@ -75,7 +75,7 @@ function showStageSelectionForm(prompt: StageSelectionPrompt | null, isReady: bo
|
||||
let i = -1;
|
||||
for (const button of stageButtons.buttons) {
|
||||
const originalClass = i < 0 ? 'stageRandom' : 'stage';
|
||||
if (prompt.bannedStages?.includes(i)) {
|
||||
if (i >= 0 && (currentGame!.game.maxPlayers > stageDatabase.stages![i].maxPlayers || prompt.bannedStages?.includes(i))) {
|
||||
button.buttonElement.className = `${originalClass} banned`;
|
||||
button.enabled = false;
|
||||
} else if (prompt.struckStages?.includes(i)) {
|
||||
|
||||
@@ -93,7 +93,7 @@ function stageSwitchButton_click(e: Event) {
|
||||
let status = button.dataset.status == '0' ? 1 : button.dataset.status == '1' ? 2 : 0;
|
||||
button.dataset.status = status.toString();
|
||||
(<HTMLElement>button.getElementsByClassName('stageStatus')[0]).innerText = [ 'Allowed', 'Counterpick only', 'Banned' ][status];
|
||||
gameSetupSubmitButton.disabled = stageSwitchButtons.every(b => b.dataset.status != '0');
|
||||
updateCreateRoomButton();
|
||||
}
|
||||
|
||||
maxPlayersBox.addEventListener('change', () => {
|
||||
@@ -103,8 +103,18 @@ maxPlayersBox.addEventListener('change', () => {
|
||||
else
|
||||
maxPlayersBox.value = '2';
|
||||
}
|
||||
const maxPlayers = parseInt(maxPlayersBox.value);
|
||||
for (let i = 0; i < stageDatabase.stages!.length; i++) {
|
||||
stageSwitchButtons[i].disabled = maxPlayers > stageDatabase.stages![i].maxPlayers;
|
||||
}
|
||||
updateCreateRoomButton();
|
||||
});
|
||||
|
||||
function updateCreateRoomButton() {
|
||||
const maxPlayers = parseInt(maxPlayersBox.value);
|
||||
gameSetupSubmitButton.disabled = stageSwitchButtons.every((b, i) => b.dataset.status != '0' || maxPlayers > stageDatabase.stages![i].maxPlayers);
|
||||
}
|
||||
|
||||
newGameSetupButton.addEventListener('click', _ => {
|
||||
gameSetupDialog.showModal();
|
||||
});
|
||||
@@ -172,8 +182,9 @@ function createRoom(useOptionsForm: boolean) {
|
||||
data.append('name', name);
|
||||
data.append('clientToken', clientToken);
|
||||
if (useOptionsForm) {
|
||||
const maxPlayers = parseInt(maxPlayersBox.value);
|
||||
const settings = <CustomRoomConfig> {
|
||||
maxPlayers: parseInt(maxPlayersBox.value),
|
||||
maxPlayers,
|
||||
turnTimeLimit: turnTimeLimitBox.value ? turnTimeLimitBox.valueAsNumber : null,
|
||||
goalWinCount: goalWinCountBox.value ? parseInt(goalWinCountBox.value) : null,
|
||||
allowUpcomingCards: gameSetupAllowUpcomingCardsBox.checked,
|
||||
|
||||
@@ -13,6 +13,8 @@ class Stage {
|
||||
return new Stage(obj.name, obj.grid, obj.startSpaces);
|
||||
}
|
||||
|
||||
get maxPlayers() { return Math.max(...this.startSpaces.map(a => a.length)); }
|
||||
|
||||
getStartSpaces(numPlayers: number) {
|
||||
let list = null as Point[] | null;
|
||||
for (const list2 of this.startSpaces) {
|
||||
|
||||
@@ -75,6 +75,7 @@ class StageButton extends CheckButton {
|
||||
this.startCells.splice(0);
|
||||
|
||||
const startSpaces = this.stage.getStartSpaces(numPlayers);
|
||||
if (startSpaces == null) return;
|
||||
for (let i = 0; i < numPlayers; i++) {
|
||||
const space = startSpaces[i];
|
||||
const cell = this.cells[space.x][space.y]!;
|
||||
|
||||
@@ -115,6 +115,10 @@ public class Game(int maxPlayers) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (stages.Any(i => i >= StageDatabase.Stages.Count)) {
|
||||
error = new(HttpStatusCode.UnprocessableEntity, "InvalidStage", "Invalid stage selection.");
|
||||
return false;
|
||||
}
|
||||
var rule = this.GetCurrentStageSelectionRule();
|
||||
if (stages.Intersect(rule.BannedStages).Any()) {
|
||||
error = new(HttpStatusCode.UnprocessableEntity, "IllegalStage", "A selected stage is banned.");
|
||||
@@ -125,10 +129,6 @@ public class Game(int maxPlayers) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (stages.Any(i => i >= StageDatabase.Stages.Count)) {
|
||||
error = new(HttpStatusCode.UnprocessableEntity, "StageNotFound", "No such stage is known.");
|
||||
return false;
|
||||
}
|
||||
player.selectedStages = stages;
|
||||
error = default;
|
||||
return true;
|
||||
@@ -495,10 +495,10 @@ public class Game(int maxPlayers) {
|
||||
var stage = StageDatabase.Stages[stageIndex];
|
||||
this.StageIndex = stageIndex;
|
||||
this.setStages.Add(stageIndex);
|
||||
this.Board = (Space[,]) stage.grid.Clone();
|
||||
this.Board = (Space[,]) stage.Grid.Clone();
|
||||
|
||||
// Place starting positions.
|
||||
var list = stage.startSpaces.Where(s => s.Length >= this.Players.Count).MinBy(s => s.Length) ?? throw new InvalidOperationException("Couldn't find start spaces");
|
||||
var list = stage.StartSpaces.Where(s => s.Length >= this.Players.Count).MinBy(s => s.Length) ?? throw new InvalidOperationException("Couldn't find start spaces");
|
||||
this.StartSpaces = list;
|
||||
for (int i = 0; i < this.Players.Count; i++)
|
||||
this.Board[list[i].X, list[i].Y] = Space.SpecialInactive1 | (Space) i;
|
||||
|
||||
@@ -179,21 +179,21 @@ internal partial class Program {
|
||||
|
||||
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) {
|
||||
if (!TryParseStageSelectionRule(json1, maxPlayers, out stageSelectionRuleFirst) || stageSelectionRuleFirst.Method is StageSelectionMethod.Same or StageSelectionMethod.Counterpick) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "stageSelectionRuleFirst was invalid."));
|
||||
return;
|
||||
}
|
||||
} else
|
||||
stageSelectionRuleFirst = StageSelectionRules.Default;
|
||||
if (d.TryGetValue("stageSelectionRuleAfterWin", out var json2)) {
|
||||
if (!TryParseStageSelectionRule(json2, out stageSelectionRuleAfterWin)) {
|
||||
if (!TryParseStageSelectionRule(json2, maxPlayers, out stageSelectionRuleAfterWin)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "stageSelectionRuleAfterWin was invalid."));
|
||||
return;
|
||||
}
|
||||
} else
|
||||
stageSelectionRuleAfterWin = stageSelectionRuleFirst;
|
||||
if (d.TryGetValue("stageSelectionRuleAfterDraw", out var json3)) {
|
||||
if (!TryParseStageSelectionRule(json3, out stageSelectionRuleAfterDraw) || stageSelectionRuleAfterDraw.Method == StageSelectionMethod.Counterpick) {
|
||||
if (!TryParseStageSelectionRule(json3, maxPlayers, out stageSelectionRuleAfterDraw) || stageSelectionRuleAfterDraw.Method == StageSelectionMethod.Counterpick) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "stageSelectionRuleAfterDraw was invalid."));
|
||||
return;
|
||||
}
|
||||
@@ -766,10 +766,16 @@ internal partial class Program {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryParseStageSelectionRule(string json, [MaybeNullWhen(false)] out StageSelectionRules stageSelectionRule) {
|
||||
private static bool TryParseStageSelectionRule(string json, int maxPlayers, [MaybeNullWhen(false)] out StageSelectionRules stageSelectionRule) {
|
||||
try {
|
||||
stageSelectionRule = JsonUtils.Deserialise<StageSelectionRules>(json);
|
||||
return stageSelectionRule != null;
|
||||
if (stageSelectionRule == null) return false;
|
||||
stageSelectionRule.AddUnavailableStages(maxPlayers);
|
||||
// Check that at least one stage is allowed.
|
||||
for (var i = 0; i < StageDatabase.Stages.Count; i++) {
|
||||
if (!stageSelectionRule.BannedStages.Contains(i)) return true;
|
||||
}
|
||||
return false;
|
||||
} catch (JsonSerializationException) {
|
||||
stageSelectionRule = null;
|
||||
return false;
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace TableturfBattleServer;
|
||||
public class Stage(string name, Space[,] grid, Point[][] startSpaces) {
|
||||
public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
|
||||
[JsonProperty]
|
||||
internal readonly Space[,] grid = grid ?? throw new ArgumentNullException(nameof(grid));
|
||||
internal Space[,] Grid = grid ?? throw new ArgumentNullException(nameof(grid));
|
||||
/// <summary>
|
||||
/// The lists of starting spaces on this stage.
|
||||
/// </summary>
|
||||
@@ -13,5 +13,8 @@ public class Stage(string name, Space[,] grid, Point[][] startSpaces) {
|
||||
/// For example, if there is a list of 3 and a list of 4, the list of 3 will be used for 2 or 3 players, and the list of 4 will be used for 4 players.
|
||||
/// </remarks>
|
||||
[JsonProperty]
|
||||
internal readonly Point[][] startSpaces = startSpaces ?? throw new ArgumentNullException(nameof(startSpaces));
|
||||
internal Point[][] StartSpaces = startSpaces ?? throw new ArgumentNullException(nameof(startSpaces));
|
||||
|
||||
[JsonIgnore]
|
||||
public int MaxPlayers => this.StartSpaces.Max(a => a.Length);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,17 @@ public class StageSelectionRules(StageSelectionMethod method, int[]? bannedStage
|
||||
public int[] BannedStages { get; set; } = bannedStages ?? Array.Empty<int>();
|
||||
|
||||
public static StageSelectionRules Default { get; } = new(StageSelectionMethod.Vote, Array.Empty<int>());
|
||||
|
||||
public void AddUnavailableStages(int maxPlayers) {
|
||||
if (maxPlayers == 2) return;
|
||||
|
||||
var list = new List<int>(this.BannedStages);
|
||||
for (var i = 0; i < StageDatabase.Stages.Count; i++) {
|
||||
if (maxPlayers > StageDatabase.Stages[i].MaxPlayers)
|
||||
list.Add(i);
|
||||
}
|
||||
this.BannedStages = list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public enum StageSelectionMethod {
|
||||
|
||||
Reference in New Issue
Block a user