From 89ec68ae326e897515fe0c49eaedf81fdf7148de Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 21 Nov 2023 11:18:38 +1100 Subject: [PATCH] Allow custom cards and bots simultaneously --- .../MassiveDecks/Pages/Lobby/Configure.elm | 82 ++++++++----------- client/src/elm/MassiveDecks/Strings.elm | 4 +- .../elm/MassiveDecks/Strings/Languages/De.elm | 15 ++-- .../Strings/Languages/DeXInformal.elm | 13 +-- .../Strings/Languages/En/Internal.elm | 21 ++--- .../elm/MassiveDecks/Strings/Languages/Es.elm | 14 ++-- .../elm/MassiveDecks/Strings/Languages/Id.elm | 13 +-- .../elm/MassiveDecks/Strings/Languages/It.elm | 13 +-- .../elm/MassiveDecks/Strings/Languages/Ko.elm | 13 +-- .../elm/MassiveDecks/Strings/Languages/Pl.elm | 13 +-- .../MassiveDecks/Strings/Languages/PtBR.elm | 13 +-- .../ts/action/game-action/player/discard.ts | 5 +- server/src/ts/action/game-action/redraw.ts | 5 +- server/src/ts/games/cards/decks.ts | 32 +++++--- server/src/ts/games/game.ts | 12 ++- server/src/ts/index.ts | 5 +- 16 files changed, 120 insertions(+), 153 deletions(-) diff --git a/client/src/elm/MassiveDecks/Pages/Lobby/Configure.elm b/client/src/elm/MassiveDecks/Pages/Lobby/Configure.elm index 11a94592..fce70c12 100644 --- a/client/src/elm/MassiveDecks/Pages/Lobby/Configure.elm +++ b/client/src/elm/MassiveDecks/Pages/Lobby/Configure.elm @@ -455,21 +455,43 @@ startGameProblems shared wrap wrapLobby users model remote = Nothing -> summaries .responses + -- Catch the case where "only blank white cards" is enabled. + numberOfNonBlankResponses = + case hr.comedyWriter of + Just { exclusive } -> + if exclusive then + 0 + + else + summaries .responses + + Nothing -> + summaries .responses + humanPlayerCount = users |> Dict.values |> List.filter (\u -> u.role == User.Player && u.presence == User.Joined && u.control == User.Human) |> List.length - computerPlayers = + computerPlayerCount = config.rules.houseRules.rando |> Maybe.map .number |> Maybe.withDefault 0 playerCount = - humanPlayerCount + computerPlayers + humanPlayerCount + computerPlayerCount -- 5 is arbitrary, but we have to deal with an additional cards for each slot. - requiredResponses = - playerCount * 5 + playerCount * rules.handSize + requiredHumanResponses = + humanPlayerCount * 5 + humanPlayerCount * rules.handSize + + -- We need enough non-blank responses for every player to have a full hand + -- otherwise there is a chance humans will hog all the non-blank responses. + requiredComputerResponses = + if computerPlayerCount > 0 then + playerCount * 5 + playerCount * rules.handSize + + else + 0 deckIssues = if noDecks then @@ -489,7 +511,7 @@ startGameProblems shared wrap wrapLobby users model remote = else let diff = - requiredResponses - numberOfResponses + requiredHumanResponses - numberOfResponses old = config.rules.houseRules.comedyWriter |> Maybe.map .number |> Maybe.withDefault 0 @@ -515,9 +537,12 @@ startGameProblems shared wrap wrapLobby users model remote = |> Message.error |> Maybe.justIf (summaries .calls < 1) , Message.errorWithFix - (Strings.NotEnoughCardsOfType { cardType = Strings.Response, needed = requiredResponses, have = numberOfResponses }) + (Strings.NotEnoughCardsOfType { cardType = Strings.Response, needed = requiredHumanResponses, have = numberOfResponses }) [ Message.Fix (Strings.AddBlankCards { amount = diff }) Icon.add fixMsg ] - |> Maybe.justIf (numberOfResponses < requiredResponses) + |> Maybe.justIf (numberOfResponses < requiredHumanResponses) + , Message.error + (Strings.NotEnoughNonBlankCardsOfType { cardType = Strings.Response, needed = requiredComputerResponses, have = numberOfNonBlankResponses }) + |> Maybe.justIf (numberOfResponses >= requiredHumanResponses && numberOfNonBlankResponses < requiredComputerResponses) ] rando = @@ -560,47 +585,6 @@ startGameProblems shared wrap wrapLobby users model remote = |> Maybe.justIf (humanPlayerCount < 1) ] - disableRandoConfig = - { config | rules = { rules | houseRules = { houseRules | rando = Nothing } } } - - disableRandoFixMsg = - ApplyChange - (Rando.Enabled - |> HouseRules.RandoId - |> Rules.HouseRulesId - |> RulesId - ) - disableRandoConfig - |> wrap - - disableComedyWriterConfig = - { config | rules = { rules | houseRules = { houseRules | comedyWriter = Nothing } } } - - disableComedyWriterFixMsg = - ApplyChange - (ComedyWriter.Enabled - |> HouseRules.ComedyWriterId - |> Rules.HouseRulesId - |> RulesId - ) - disableComedyWriterConfig - |> wrap - - aisNoWriteGoodIssues = - [ Message.errorWithFix - Strings.RandoCantWrite - [ { description = Strings.DisableRando - , icon = Icon.disableAi - , action = disableRandoFixMsg - } - , { description = Strings.DisableComedyWriter - , icon = Icon.disableEdit - , action = disableComedyWriterFixMsg - } - ] - |> Maybe.justIf (hr.rando /= Nothing && hr.comedyWriter /= Nothing) - ] - configurationIssues = [ Message.errorWithFix Strings.UnsavedChangesWarning (List.filterMap identity @@ -619,7 +603,7 @@ startGameProblems shared wrap wrapLobby users model remote = |> Maybe.justIf (config /= remote) ] in - [ deckIssues, playerIssues, aisNoWriteGoodIssues, configurationIssues ] |> List.concat |> List.filterMap identity + [ deckIssues, playerIssues, configurationIssues ] |> List.concat |> List.filterMap identity tabs : List Tab diff --git a/client/src/elm/MassiveDecks/Strings.elm b/client/src/elm/MassiveDecks/Strings.elm index 7d280b1d..006a66e3 100644 --- a/client/src/elm/MassiveDecks/Strings.elm +++ b/client/src/elm/MassiveDecks/Strings.elm @@ -252,6 +252,7 @@ type MdString | WaitForDecks -- A hint that the user has to wait for decks to load before starting the game. | MissingCardType { cardType : Noun } -- An error explaining that the user needs a deck with the given type of card (call/response). | NotEnoughCardsOfType { cardType : Noun, needed : Int, have : Int } -- An error explaining that the user needs to add more cards of the given type for the number of players. + | NotEnoughNonBlankCardsOfType { cardType : Noun, needed : Int, have : Int } -- An error explaining that the user needs to add more non-blank white cards for the number of players. | AddBlankCards { amount : Int } -- A description of the action of adding the given number of blank cards to the game. | AddDeck -- A description of the action of adding a deck to the game configuration. | RemoveDeck -- A description of the action of removing a deck from the game configuration. @@ -281,9 +282,6 @@ type MdString | NeedAtLeastOneDeck -- A description of the problem that the game needs at least one deck to start. | NeedAtLeastThreePlayers -- A description of the problem that the game needs at least three players to start. | NeedAtLeastOneHuman -- A description of the problem that the game needs at least one human player. - | RandoCantWrite -- A description of the problem that the AI players can't use blank cards. - | DisableRando -- A description of disabling the "Rando Cardrissian" house rule. - | DisableComedyWriter -- A description of disabling the "Comedy Writer" house rule. | AddAnAiPlayer -- A description of adding an AI player to the game. | PasswordShared -- A warning that game passwords are visible to anyone else in the game. | PasswordNotSecured -- A warning that game passwords are not stored securely and should not be used elsewhere. diff --git a/client/src/elm/MassiveDecks/Strings/Languages/De.elm b/client/src/elm/MassiveDecks/Strings/Languages/De.elm index 00e3237c..1f00e72e 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/De.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/De.elm @@ -814,6 +814,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Ergänze " , amount |> String.fromInt |> Text @@ -919,15 +923,6 @@ translate _ mdString = , Text " (auch wenn nur ein einzelner menschlicher Spieler ein bisschen langweilig sein mag!)" ] - RandoCantWrite -> - [ Text "Computerspieler können ihre Karten nicht selbst schreiben." ] - - DisableComedyWriter -> - [ Text "Deaktiviere ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Deaktiviere ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Fügen Sie dem Spiel einen KI-Spieler hinzu." ] @@ -1380,10 +1375,10 @@ translate _ mdString = Spanish -> [ Text "Spanisch" ] - Korean -> [ Text "Koreanisch" ] + an : Maybe Int -> String an amount = case amount of diff --git a/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm b/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm index e5e60abc..0de9caba 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm @@ -812,6 +812,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Ergänze " , amount |> String.fromInt |> Text @@ -917,15 +921,6 @@ translate _ mdString = , Text " (auch wenn nur ein einzelner menschlicher Spieler ein bisschen langweilig sein mag!)" ] - RandoCantWrite -> - [ Text "Computerspieler können ihre Karten nicht selbst schreiben." ] - - DisableComedyWriter -> - [ Text "Deaktiviere ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Deaktiviere ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Füge dem Spiel einen KI-Spieler hinzu." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm b/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm index 62bf8f00..8bd34e65 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm @@ -753,7 +753,7 @@ translate _ mdString = ] NotEnoughCardsOfType { cardType, needed, have } -> - [ Text "For the number of players in the game, you need at least " + [ Text "For the number of humans in the game, you need at least " , Text (needed |> String.fromInt) , Text " " , ref (noun cardType needed) @@ -762,6 +762,16 @@ translate _ mdString = , Text "." ] + NotEnoughNonBlankCardsOfType { cardType, needed, have } -> + [ Text "For the number of bots in the game, you need at least " + , Text (needed |> String.fromInt) + , Text " non-blank " + , ref (noun cardType needed) + , Text " but you only have " + , Text (have |> String.fromInt) + , Text "." + ] + AddBlankCards { amount } -> [ Text "Add " , amount |> String.fromInt |> Text @@ -866,15 +876,6 @@ translate _ mdString = , Text " (Although only one human might be a bit boring!)" ] - RandoCantWrite -> - [ Text "Computer players can't write their own cards." ] - - DisableComedyWriter -> - [ Text "Disable ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Disable ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Add an AI player to the game." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Es.elm b/client/src/elm/MassiveDecks/Strings/Languages/Es.elm index b404d00a..358b5c1e 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Es.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Es.elm @@ -796,6 +796,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Añadir " , amount |> String.fromInt |> Text @@ -900,15 +904,6 @@ translate _ mdString = , Text " (¡Aunque solo un humano puede ser un poco aburrido!)" ] - RandoCantWrite -> - [ Text "Los bots no pueden escribir sus propias cartas." ] - - DisableComedyWriter -> - [ Text "Desactivar ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Desactivar ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Añadir un bot a la partida." ] @@ -1363,6 +1358,7 @@ translate _ mdString = Korean -> [ Text "Coreano" ] + raw : MdString -> Translation.Result never raw = Raw Nothing diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Id.elm b/client/src/elm/MassiveDecks/Strings/Languages/Id.elm index 9233c73b..014a69ff 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Id.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Id.elm @@ -803,6 +803,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Tambahkan " , amount |> String.fromInt |> Text @@ -908,15 +912,6 @@ translate _ mdString = , Text " (Meskipun hanya satu manusia yang mungkin sedikit membosankan!)" ] - RandoCantWrite -> - [ Text "Pemain komputer tidak bisa menulis kartunya sendiri." ] - - DisableComedyWriter -> - [ Text "Nonaktifkan" ] - - DisableRando -> - [ Text "Nonaktifkan ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Tambahkan pemain AI ke dalam game." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/It.elm b/client/src/elm/MassiveDecks/Strings/Languages/It.elm index 836acf80..76ed5cc3 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/It.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/It.elm @@ -823,6 +823,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + -- TODO: Translate AddBlankCards { amount } -> [ Missing ] @@ -932,15 +936,6 @@ translate _ mdString = , Text " (Anche se un solo giocatore umano potrebbe essere piuttosto noioso!)" ] - RandoCantWrite -> - [ Text "I giocatori computer non possono scrivere le loro carte." ] - - DisableComedyWriter -> - [ Text "Disabilita ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Disabilita ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Aggiungi un giocatore IA al gioco." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Ko.elm b/client/src/elm/MassiveDecks/Strings/Languages/Ko.elm index e7461e26..48a9a5b5 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Ko.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Ko.elm @@ -779,6 +779,10 @@ translate _ mdString = , Text "장입니다." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ amount |> String.fromInt |> Text , Text "장의 공백 " @@ -882,15 +886,6 @@ translate _ mdString = , Text " (인간 1명은 좀 재미가 없을 것 같기도 하지만요!)" ] - RandoCantWrite -> - [ Text "컴퓨터 플레이어들은 자신의 커스텀 카드는 쓸 수 없습니다." ] - - DisableComedyWriter -> - [ ref HouseRuleComedyWriter, Text " 비활성화" ] - - DisableRando -> - [ ref HouseRuleRandoCardrissian, Text "비활성화" ] - AddAnAiPlayer -> [ Text "게임에 AI 플레이어를 추가합니다." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm b/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm index db4c9dd9..94a782f3 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm @@ -893,6 +893,10 @@ translate maybeDeclCase mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Dodaj " , Text (asWord amount Feminine Accusative) @@ -1012,15 +1016,6 @@ translate maybeDeclCase mdString = , Text " (Chociaż z tylko jednym człowiekiem może być nudno!)" ] - RandoCantWrite -> - [ Text "Boty nie mogą pisać własnych kart." ] - - DisableComedyWriter -> - [ Text "Wyłącz Memiarza" ] - - DisableRando -> - [ Text "Wyłącz Rando Cardrissiana" ] - AddAnAiPlayer -> [ Text "Dodaj bota do gry." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm b/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm index de79ffa3..ea57a455 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm @@ -810,6 +810,10 @@ translate _ mdString = , Text "." ] + -- TODO: Translate + NotEnoughNonBlankCardsOfType _ -> + [ Missing ] + AddBlankCards { amount } -> [ Text "Adicionar " , amount |> String.fromInt |> Text @@ -914,15 +918,6 @@ translate _ mdString = , Text " (apesar de que só um humano seja meio chato!)" ] - RandoCantWrite -> - [ Text "Jogadores IA não podem escrever cartas." ] - - DisableComedyWriter -> - [ Text "Desativar ", ref HouseRuleComedyWriter ] - - DisableRando -> - [ Text "Desativar ", ref HouseRuleRandoCardrissian ] - AddAnAiPlayer -> [ Text "Adicionar um jogador IA ao jogo." ] diff --git a/server/src/ts/action/game-action/player/discard.ts b/server/src/ts/action/game-action/player/discard.ts index a614cc18..ec625415 100644 --- a/server/src/ts/action/game-action/player/discard.ts +++ b/server/src/ts/action/game-action/player/discard.ts @@ -42,7 +42,10 @@ class DiscardActions extends Actions.Implementation< throw new InvalidActionError("Must have the card to discard it."); } player.hand = player.hand.filter((c) => c.id !== action.card); - const [replacement] = lobby.game.decks.responses.replace(card); + const [replacement] = lobby.game.decks.responses.replace( + lobby.config.rules.houseRules.rando.current.includes(id), + card, + ); player.hand.push(replacement); const events = Util.asOptionalIterable( Event.playerSpecificAddition(CardDiscarded.of(id, card), (to) => diff --git a/server/src/ts/action/game-action/redraw.ts b/server/src/ts/action/game-action/redraw.ts index 7de2e6f1..c3d3b3b9 100644 --- a/server/src/ts/action/game-action/redraw.ts +++ b/server/src/ts/action/game-action/redraw.ts @@ -41,7 +41,10 @@ class RedrawActions extends Actions.Implementation< throw new InvalidActionError("Can't afford to redraw."); } player.score -= cost; - player.hand = game.decks.responses.replace(...player.hand); + player.hand = game.decks.responses.replace( + lobby.config.rules.houseRules.rando.current.includes(auth.uid), + ...player.hand, + ); return { lobby, events: [ diff --git a/server/src/ts/games/cards/decks.ts b/server/src/ts/games/cards/decks.ts index b7bb09c0..be67d637 100644 --- a/server/src/ts/games/cards/decks.ts +++ b/server/src/ts/games/cards/decks.ts @@ -43,29 +43,35 @@ export abstract class Deck { this.discarded.add(card); } - public draw(cards: 1): [C]; - public draw(cards: 2): [C, C]; - public draw(cards: 3): [C, C, C]; - public draw(cards: number): C[]; - public draw(cards: number): C[] { + public draw(cards: 1, bot?: boolean): [C]; + public draw(cards: 2, bot?: boolean): [C, C]; + public draw(cards: 3, bot?: boolean): [C, C, C]; + public draw(cards: number, bot?: boolean): C[]; + public draw(cards: number, bot?: boolean): C[] { const cardsLeft = this.cards.length; const toDraw = Math.min(cardsLeft, cards); - const result = this.cards.splice(0, toDraw); + const result = this.cards + .splice(0, toDraw) + .filter((card) => card.source.source !== "Custom"); + // Only reshuffle if we don;t have enough cards left to draw. if (toDraw < cards) { this.reshuffle(); - return [...result, ...this.draw(cards - toDraw)]; + } + // If there weren't enough cards, or a bot drew custom cards, draw more. + if (result.length < cards) { + return [...result, ...this.draw(cards - result.length, bot)]; } else { return result; } } - public replace(...cards: [C]): [C]; - public replace(...cards: [C, C]): [C, C]; - public replace(...cards: [C, C, C]): [C, C, C]; - public replace(...cards: C[]): C[]; - public replace(...cards: C[]): C[] { + public replace(bot: boolean, ...cards: [C]): [C]; + public replace(bot: boolean, ...cards: [C, C]): [C, C]; + public replace(bot: boolean, ...cards: [C, C, C]): [C, C, C]; + public replace(bot: boolean, ...cards: C[]): C[]; + public replace(bot: boolean, ...cards: C[]): C[] { this.discard(cards); - return this.draw(cards.length); + return this.draw(cards.length, bot); } protected reshuffle(): void { diff --git a/server/src/ts/games/game.ts b/server/src/ts/games/game.ts index 85e83c06..e15e00e7 100644 --- a/server/src/ts/games/game.ts +++ b/server/src/ts/games/game.ts @@ -202,7 +202,12 @@ export class Game { .filter(([_, user]) => user.role === "Player") .map(([id, _]) => [ id, - Player.initial(gameDecks.responses.draw(rules.handSize)), + Player.initial( + gameDecks.responses.draw( + rules.handSize, + rules.houseRules.rando.current.includes(id), + ), + ), ]), ); const czar = Game.internalNextCzar(0, users, playerMap, playerOrder); @@ -447,7 +452,10 @@ export class Game { const responseDeck = this.decks.responses; for (const [id, playerState] of Object.entries(this.players)) { if (Player.role(id, this) === "Player") { - const drawn = responseDeck.draw(slotCount - 1); + const drawn = responseDeck.draw( + slotCount - 1, + this.rules.houseRules.rando.current.includes(id), + ); if (!first) { additionallyByPlayer.set(id, { drawn }); } diff --git a/server/src/ts/index.ts b/server/src/ts/index.ts index 4542dae8..d4f3fcfe 100644 --- a/server/src/ts/index.ts +++ b/server/src/ts/index.ts @@ -158,7 +158,10 @@ async function main(): Promise { game.playerOrder.push(id); if (newUser.role === "Player") { game.players[id] = Player.initial( - game.decks.responses.draw(game.rules.handSize), + game.decks.responses.draw( + game.rules.handSize, + game.rules.houseRules.rando.current.includes(id), + ), ); } }