mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-07-18 16:21:24 -05:00
Implement custom cards in battles
This commit is contained in:
parent
3d742ac443
commit
ba96b6b6f6
|
|
@ -85,6 +85,9 @@
|
|||
<label for="lobbyAllowUpcomingCardsBox">
|
||||
<input type="checkbox" id="lobbyAllowUpcomingCardsBox"/> Allow upcoming cards
|
||||
</label>
|
||||
<label for="lobbyAllowCustomCardsBox">
|
||||
<input type="checkbox" id="lobbyAllowCustomCardsBox"/> Allow custom cards
|
||||
</label>
|
||||
</section>
|
||||
<section id="lobbySelectedStageSection">
|
||||
<h3>Stage</h3>
|
||||
|
|
@ -663,6 +666,7 @@
|
|||
</p>
|
||||
<p>
|
||||
<label for="gameSetupAllowUpcomingCardsBox"><input type="checkbox" id="gameSetupAllowUpcomingCardsBox" checked/> Allow upcoming cards</label>
|
||||
<label for="gameSetupAllowCustomCardsBox"><input type="checkbox" id="gameSetupAllowCustomCardsBox"/> Allow custom cards</label>
|
||||
</p>
|
||||
<p>Stage selection:</p>
|
||||
<table>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ interface CustomRoomConfig {
|
|||
turnTimeLimit: number | null;
|
||||
goalWinCount: number | null;
|
||||
allowUpcomingCards: boolean;
|
||||
allowCustomCards: boolean;
|
||||
stageSelectionMethodFirst: StageSelectionMethod;
|
||||
stageSelectionMethodAfterWin: StageSelectionMethod | null;
|
||||
stageSelectionMethodAfterDraw: StageSelectionMethod | null;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ interface Game {
|
|||
/** 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
|
||||
allowUpcomingCards: boolean,
|
||||
/** Whether custom cards may be used. */
|
||||
allowCustomCards: boolean
|
||||
}
|
||||
|
||||
/** A UUID used to identify the client. */
|
||||
|
|
|
|||
|
|
@ -158,7 +158,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, allowUpcomingCards: true }, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ], stageSelectionPrompt: null }, isHost: false, webSocket: null };
|
||||
currentGame = { id: 'test', game: { state: GameState.Ongoing, maxPlayers: 2, players: [ ], turnNumber: 1, turnTimeLimit: null, turnTimeLeft: null, goalWinCount: null, allowUpcomingCards: true, allowCustomCards: true }, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ], stageSelectionPrompt: null }, isHost: false, webSocket: null };
|
||||
board.flip = false;
|
||||
board.resize(stage.copyGrid());
|
||||
const startSpaces = stage.getStartSpaces(2);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const lobbyDeckSubmitButton = document.getElementById('submitDeckButton') as HTM
|
|||
|
||||
const lobbyTimeLimitBox = document.getElementById('lobbyTimeLimitBox') as HTMLInputElement;
|
||||
const lobbyAllowUpcomingCardsBox = document.getElementById('lobbyAllowUpcomingCardsBox') as HTMLInputElement;
|
||||
const lobbyAllowCustomCardsBox = document.getElementById('lobbyAllowCustomCardsBox') as HTMLInputElement;
|
||||
const lobbyTimeLimitUnit = document.getElementById('lobbyTimeLimitUnit')!;
|
||||
|
||||
const qrCodeDialog = document.getElementById('qrCodeDialog') as HTMLDialogElement;
|
||||
|
|
@ -182,6 +183,7 @@ function lobbyResetSlots() {
|
|||
function lobbyLockSettings(lock: boolean) {
|
||||
lobbyTimeLimitBox.readOnly = lock;
|
||||
lobbyAllowUpcomingCardsBox.disabled = lock;
|
||||
lobbyAllowCustomCardsBox.disabled = lock;
|
||||
}
|
||||
|
||||
function clearReady() {
|
||||
|
|
@ -298,7 +300,9 @@ function initDeckSelection() {
|
|||
}
|
||||
});
|
||||
|
||||
if (!deck.isValid || (!currentGame.game.allowUpcomingCards && deck.cards.find(n => cardDatabase.get(n).number < 0))) {
|
||||
if (!deck.isValid
|
||||
|| (!currentGame.game.allowUpcomingCards && deck.cards.find(n => cardDatabase.get(n).isUpcoming))
|
||||
|| (!currentGame.game.allowCustomCards && deck.cards.find(n => n <= CUSTOM_CARD_START))) {
|
||||
button.enabled = false;
|
||||
} else if (deck.name == lastDeckName) {
|
||||
selectedDeck = deck;
|
||||
|
|
@ -332,10 +336,28 @@ lobbyAllowUpcomingCardsBox.addEventListener('change', () => {
|
|||
req.send(data.toString());
|
||||
});
|
||||
|
||||
lobbyAllowCustomCardsBox.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('allowCustomCards', lobbyAllowCustomCardsBox.checked.toString());
|
||||
req.send(data.toString());
|
||||
});
|
||||
|
||||
deckSelectionForm.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
if (selectedDeck == null) return;
|
||||
|
||||
const customCards: {[key: number]: Card} = { };
|
||||
let anyCustomCards = false;
|
||||
for (const number of selectedDeck.cards) {
|
||||
if (number <= CUSTOM_CARD_START) {
|
||||
customCards[number] = cardDatabase.get(number);
|
||||
anyCustomCards = true;
|
||||
}
|
||||
}
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/chooseDeck`);
|
||||
req.addEventListener('load', () => {
|
||||
|
|
@ -351,6 +373,8 @@ deckSelectionForm.addEventListener('submit', e => {
|
|||
data.append('deckName', selectedDeck.name);
|
||||
data.append('deckCards', selectedDeck.cards.join('+'));
|
||||
data.append('deckSleeves', selectedDeck.sleeves.toString());
|
||||
if (anyCustomCards)
|
||||
data.append('customCards', JSON.stringify(customCards, deckExportJsonReplacer));
|
||||
req.send(data.toString());
|
||||
|
||||
localStorage.setItem('lastDeckName', selectedDeck.name);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const maxPlayersBox = document.getElementById('maxPlayersBox') as HTMLSelectElem
|
|||
const turnTimeLimitBox = document.getElementById('turnTimeLimitBox') as HTMLInputElement;
|
||||
const goalWinCountBox = document.getElementById('goalWinCountBox') as HTMLSelectElement;
|
||||
const gameSetupAllowUpcomingCardsBox = document.getElementById('gameSetupAllowUpcomingCardsBox') as HTMLInputElement;
|
||||
const gameSetupAllowCustomCardsBox = document.getElementById('gameSetupAllowCustomCardsBox') as HTMLInputElement;
|
||||
const stageSelectionRuleFirstBox = document.getElementById('stageSelectionRuleFirstBox') as HTMLSelectElement;
|
||||
const stageSelectionRuleAfterWinBox = document.getElementById('stageSelectionRuleAfterWinBox') as HTMLSelectElement;
|
||||
const stageSelectionRuleAfterDrawBox = document.getElementById('stageSelectionRuleAfterDrawBox') as HTMLSelectElement;
|
||||
|
|
@ -174,6 +175,7 @@ function createRoom(useOptionsForm: boolean) {
|
|||
turnTimeLimit: turnTimeLimitBox.value ? turnTimeLimitBox.valueAsNumber : null,
|
||||
goalWinCount: goalWinCountBox.value ? parseInt(goalWinCountBox.value) : null,
|
||||
allowUpcomingCards: gameSetupAllowUpcomingCardsBox.checked,
|
||||
allowCustomCards: gameSetupAllowCustomCardsBox.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],
|
||||
|
|
@ -190,6 +192,7 @@ function createRoom(useOptionsForm: boolean) {
|
|||
if (goalWinCountBox.value)
|
||||
data.append('goalWinCount', goalWinCountBox.value);
|
||||
data.append('allowUpcomingCards', settings.allowUpcomingCards.toString());
|
||||
data.append('allowCustomCards', settings.allowCustomCards.toString());
|
||||
|
||||
const stageSelectionRuleFirst = {
|
||||
method: settings.stageSelectionMethodFirst,
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ class ReplayLoader {
|
|||
turnTimeLimit: null,
|
||||
turnTimeLeft: null,
|
||||
goalWinCount: goalWinCount,
|
||||
allowUpcomingCards: true
|
||||
allowUpcomingCards: true,
|
||||
allowCustomCards: true
|
||||
},
|
||||
me: null,
|
||||
isHost: false,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ function onGameSettingsChange() {
|
|||
if (lobbyTimeLimitBox.value != currentGame.game.turnTimeLimit?.toString() ?? '')
|
||||
lobbyTimeLimitBox.value = currentGame.game.turnTimeLimit?.toString() ?? '';
|
||||
lobbyAllowUpcomingCardsBox.checked = currentGame.game.allowUpcomingCards;
|
||||
lobbyAllowCustomCardsBox.checked = currentGame.game.allowCustomCards;
|
||||
}
|
||||
|
||||
function onGameStateChange(game: any, playerData: PlayerData | null) {
|
||||
|
|
@ -281,7 +282,8 @@ function setupWebSocket(gameID: string) {
|
|||
turnTimeLimit: payload.data.turnTimeLimit,
|
||||
turnTimeLeft: payload.data.turnTimeLeft,
|
||||
goalWinCount: payload.data.goalWinCount,
|
||||
allowUpcomingCards: payload.data.allowUpcomingCards
|
||||
allowUpcomingCards: payload.data.allowUpcomingCards,
|
||||
allowCustomCards: payload.data.allowCustomCards
|
||||
},
|
||||
me: payload.playerData,
|
||||
isHost: payload.isHost,
|
||||
|
|
|
|||
|
|
@ -23,3 +23,43 @@ public class PlayerData(int playerIndex, Card[]? hand, Deck? deck, Move? move, L
|
|||
|
||||
public PlayerData(int playerIndex, Player player) : this(playerIndex, player.Hand, player.CurrentGameData.Deck, player.Move, player.CardsUsed, player.StageSelectionPrompt) { }
|
||||
}
|
||||
|
||||
public record UserCustomCard(string Name, string? Line1, string? Line2, float TextScale, Colour InkColour1, Colour InkColour2, Rarity Rarity, Space[,] Grid) {
|
||||
public bool CheckGrid(out bool hasSpecialSpace, out int size) {
|
||||
size = 0;
|
||||
hasSpecialSpace = false;
|
||||
if (this.Grid is null || this.Grid.GetLength(0) != 8 || this.Grid.GetLength(1) != 8) return false;
|
||||
for (var x = 0; x < 8; x++) {
|
||||
for (var y = 0; y < 8; y++) {
|
||||
switch (this.Grid[x, y]) {
|
||||
case Space.Empty:
|
||||
break;
|
||||
case Space.Ink1:
|
||||
size++;
|
||||
break;
|
||||
case Space.SpecialInactive1:
|
||||
if (hasSpecialSpace) return false;
|
||||
size++;
|
||||
hasSpecialSpace = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Maybe also check that the ink pattern is fully connected.
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
public bool Equals(Card? card) {
|
||||
if (card is null || card.Name != this.Name || card.Rarity != this.Rarity) return false;
|
||||
for (var x = 0; x < 8; x++) {
|
||||
for (var y = 0; y < 8; y++) {
|
||||
if (this.Grid[x, y] != card.GetSpace(x, y, 0)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Card ToCard(int number, int altNumber, int? specialCost) => new(number, altNumber, this.Name, this.Rarity, specialCost, this.TextScale, null, this.Grid) { InkColour1 = this.InkColour1, InkColour2 = this.InkColour2 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public class Game(int maxPlayers) {
|
|||
internal DateTime abandonedSince = DateTime.UtcNow;
|
||||
|
||||
public bool AllowUpcomingCards { get; set; } = true;
|
||||
public bool AllowCustomCards { get; set; }
|
||||
|
||||
public required StageSelectionRules StageSelectionRuleFirst { get; set; }
|
||||
public required StageSelectionRules StageSelectionRuleAfterWin { get; set; }
|
||||
|
|
@ -38,6 +39,8 @@ public class Game(int maxPlayers) {
|
|||
[JsonIgnore]
|
||||
internal List<Deck> deckCache = [];
|
||||
[JsonIgnore]
|
||||
internal List<Card> customCards = [];
|
||||
[JsonIgnore]
|
||||
internal List<int> setStages = [];
|
||||
|
||||
private static readonly PlayerColours[] Colours = [
|
||||
|
|
@ -134,7 +137,7 @@ public class Game(int maxPlayers) {
|
|||
public Deck GetDeck(string name, int sleeves, IEnumerable<int> cardNumbers, IEnumerable<int> cardUpgrades) {
|
||||
var deck = this.deckCache.FirstOrDefault(d => d.Name == name && d.Sleeves == sleeves && cardNumbers.SequenceEqual(from c in d.Cards select c.Number) && cardUpgrades.SequenceEqual(d.Upgrades));
|
||||
if (deck == null) {
|
||||
deck = new(name, sleeves, (from i in cardNumbers select CardDatabase.GetCard(i)).ToArray(), cardUpgrades.ToArray());
|
||||
deck = new(name, sleeves, (from i in cardNumbers select i <= Program.RECEIVED_CUSTOM_CARD_START ? customCards[Program.RECEIVED_CUSTOM_CARD_START - i] : CardDatabase.GetCard(i)).ToArray(), cardUpgrades.ToArray());
|
||||
this.deckCache.Add(deck);
|
||||
}
|
||||
return deck;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public class Player(Game game, string name, Guid token) {
|
|||
internal Move? Move;
|
||||
[JsonIgnore]
|
||||
internal Move? ProvisionalMove;
|
||||
[JsonIgnore]
|
||||
internal readonly Dictionary<int, int> customCardMap = new();
|
||||
|
||||
public int GamesWon { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ internal partial class Program {
|
|||
private const int InactiveGameLimit = 1000;
|
||||
private static readonly TimeSpan InactiveGameTimeout = TimeSpan.FromMinutes(5);
|
||||
internal static readonly char[] DELIMITERS = [',', ' '];
|
||||
internal const int CUSTOM_CARD_START = -10000;
|
||||
internal const int RECEIVED_CUSTOM_CARD_START = -20000;
|
||||
|
||||
private static string? GetClientRootPath() {
|
||||
var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
|
@ -161,13 +163,20 @@ internal partial class Program {
|
|||
} else
|
||||
clientToken = Guid.NewGuid();
|
||||
|
||||
var allowUpcomingCards = false;
|
||||
bool allowUpcomingCards;
|
||||
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;
|
||||
|
||||
bool allowCustomCards;
|
||||
if (d.TryGetValue("allowCustomCards", out var allowCustomCardsString)) {
|
||||
if (!bool.TryParse(allowCustomCardsString, out allowCustomCards))
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "allowCustomCards was invalid."));
|
||||
} else
|
||||
allowCustomCards = false;
|
||||
|
||||
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) {
|
||||
|
|
@ -205,7 +214,7 @@ internal partial class Program {
|
|||
} else
|
||||
spectate = false;
|
||||
|
||||
var game = new Game(maxPlayers) { HostClientToken = clientToken, GoalWinCount = goalWinCount, TurnTimeLimit = turnTimeLimit, AllowUpcomingCards = allowUpcomingCards, StageSelectionRuleFirst = stageSelectionRuleFirst, StageSelectionRuleAfterWin = stageSelectionRuleAfterWin, StageSelectionRuleAfterDraw = stageSelectionRuleAfterDraw, ForceSameDeckAfterDraw = forceSameDeckAfterDraw };
|
||||
var game = new Game(maxPlayers) { HostClientToken = clientToken, GoalWinCount = goalWinCount, TurnTimeLimit = turnTimeLimit, AllowUpcomingCards = allowUpcomingCards, AllowCustomCards = allowCustomCards, StageSelectionRuleFirst = stageSelectionRuleFirst, StageSelectionRuleAfterWin = stageSelectionRuleAfterWin, StageSelectionRuleAfterDraw = stageSelectionRuleAfterDraw, ForceSameDeckAfterDraw = forceSameDeckAfterDraw };
|
||||
if (!spectate)
|
||||
game.TryAddPlayer(new(game, name, clientToken), out _, out _);
|
||||
games.Add(game.ID, game);
|
||||
|
|
@ -351,6 +360,14 @@ internal partial class Program {
|
|||
game.AllowUpcomingCards = allowUpcomingCards;
|
||||
}
|
||||
|
||||
if (d.TryGetValue("allowCustomCards", out var allowCustomCardsString)) {
|
||||
if (!bool.TryParse(allowCustomCardsString, out var allowCustomCards)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidGameSettings", "Invalid allowCustomCards."));
|
||||
return;
|
||||
} else
|
||||
game.AllowCustomCards = allowCustomCards;
|
||||
}
|
||||
|
||||
game.SendEvent("settingsChange", game, false);
|
||||
}
|
||||
break;
|
||||
|
|
@ -431,6 +448,42 @@ internal partial class Program {
|
|||
SetErrorResponse(e.Response, new(HttpStatusCode.BadRequest, "InvalidDeckCards", "Missing deck cards."));
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<int, UserCustomCard>? userCustomCards = null;
|
||||
List<KeyValuePair<int, Card>>? customCardsToAdd = null;
|
||||
if (d.TryGetValue("customCards", out var customCardsString)) {
|
||||
if (!game.AllowCustomCards) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "CustomCardsNotAllowed", "Custom cards cannot be used in this game."));
|
||||
return;
|
||||
}
|
||||
userCustomCards = JsonUtils.Deserialise<Dictionary<int, UserCustomCard>>(customCardsString);
|
||||
|
||||
// Validate custom cards.
|
||||
if (userCustomCards is null || userCustomCards.Count > 15 || userCustomCards.Keys.Any(k => k is not (<= -10000 and >= short.MinValue))) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidCustomCards", "Invalid custom cards."));
|
||||
return;
|
||||
}
|
||||
|
||||
customCardsToAdd = new(userCustomCards.Count);
|
||||
foreach (var (k, v) in userCustomCards) {
|
||||
if (!v.CheckGrid(out var hasSpecialSpace, out var size)) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidCustomCards", $"Custom card {k} is invalid."));
|
||||
return;
|
||||
}
|
||||
// Allow resending the same custom card, but not a different custom card with the same key.
|
||||
if (player.customCardMap.TryGetValue(k, out var existingCustomCardNumber)) {
|
||||
if (!v.Equals(game.customCards[RECEIVED_CUSTOM_CARD_START - existingCustomCardNumber])) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidCustomCards", $"Cannot reuse custom card number {k}."));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// TODO: Consolidate identical custom cards brought by different players.
|
||||
var card = v.ToCard(RECEIVED_CUSTOM_CARD_START - (game.customCards.Count + customCardsToAdd.Count), k, hasSpecialSpace && size >= 8 ? 3 : null);
|
||||
customCardsToAdd.Add(new(k, card));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var array = deckString.Split([',', '+', ' '], 15);
|
||||
if (array.Length != 15) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidDeckCards", "Invalid deck list."));
|
||||
|
|
@ -451,7 +504,7 @@ internal partial class Program {
|
|||
}
|
||||
var cards = new int[15];
|
||||
for (var i = 0; i < 15; i++) {
|
||||
if (!int.TryParse(array[i], out var cardNumber) || !CardDatabase.IsValidCardNumber(cardNumber)) {
|
||||
if (!int.TryParse(array[i], out var cardNumber) || (!CardDatabase.IsValidCardNumber(cardNumber) && (userCustomCards == null || !userCustomCards.ContainsKey(cardNumber)))) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidDeckCards", "Invalid deck list."));
|
||||
return;
|
||||
}
|
||||
|
|
@ -459,13 +512,20 @@ internal partial class Program {
|
|||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "InvalidDeckCards", "Deck cannot have duplicates."));
|
||||
return;
|
||||
}
|
||||
if (!game.AllowUpcomingCards && cardNumber < 0 && CardDatabase.GetCard(cardNumber).Number < 0) {
|
||||
if (!game.AllowUpcomingCards && cardNumber is < 0 and > CUSTOM_CARD_START && CardDatabase.GetCard(cardNumber).Number < 0) {
|
||||
SetErrorResponse(e.Response, new(HttpStatusCode.UnprocessableEntity, "ForbiddenDeck", "Upcoming cards cannot be used in this game."));
|
||||
return;
|
||||
}
|
||||
cards[i] = cardNumber;
|
||||
// Translate custom card numbers from the player to game-scoped card numbers.
|
||||
cards[i] = player.customCardMap.TryGetValue(cardNumber, out var n) ? n : cardNumber <= CUSTOM_CARD_START && customCardsToAdd?.FirstOrDefault(e => e.Key == cardNumber).Value is Card customCard ? customCard.Number : cardNumber;
|
||||
}
|
||||
|
||||
if (customCardsToAdd != null) {
|
||||
foreach (var (userKey, card) in customCardsToAdd) {
|
||||
player.customCardMap.Add(userKey, card.Number);
|
||||
game.customCards.Add(card);
|
||||
}
|
||||
}
|
||||
player.CurrentGameData.Deck = game.GetDeck(deckName, deckSleeves, cards, upgrades ?? Enumerable.Repeat(0, 15));
|
||||
e.Response.StatusCode = (int) HttpStatusCode.NoContent;
|
||||
game.SendPlayerReadyEvent(playerIndex, false);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user