diff --git a/backend/bang/cards.py b/backend/bang/cards.py index df8ceec..0a3d03c 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -67,7 +67,7 @@ class Card(ABC): def __str__(self) -> str: if str(self.suit).isnumeric(): - char = ["♦️", "♣️", "♥️", "♠️", "🤑"][int(self.suit)] + char = ["♦️", "♣️", "♥️", "♠️", "🤑", "🚋"][int(self.suit)] else: char = self.suit return f"{self.name} {char}{self.number}" diff --git a/backend/bang/expansions/train_robbery/stations.py b/backend/bang/expansions/train_robbery/stations.py index e9c50ef..bbbfc49 100644 --- a/backend/bang/expansions/train_robbery/stations.py +++ b/backend/bang/expansions/train_robbery/stations.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING import bang.cards as cs +import bang.players as pl if TYPE_CHECKING: from bang.players import Player @@ -10,6 +11,22 @@ class StationCard: self.name = name self.expansion = "train_robbery" self.price: list[dict] = [] + self.attached_train = None + + def discard_and_buy_train(self, player: "Player", card_index: int): + """Discard the card and buy the train""" + card = player.available_cards.pop(card_index) + for i, card in enumerate(player.hand): + if card == self: + player.hand.pop(i) + break + else: + player.lives -= 1 + card = player.hand.pop(card_index) + player.game.deck.scrap(card, True, player=player) + player.hand.append(self.attached_train) + self.attached_train = None + player.pending_action = pl.PendingAction.PLAY def check_price(self, player: "Player") -> bool: """Check if the card can be used to rob the train""" @@ -28,6 +45,12 @@ class BoomTown(StationCard): not isinstance(c, cs.Bang) for c in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if isinstance(c, cs.Bang)], + self.discard_and_buy_train, + ) + return True class Caticor(StationCard): @@ -43,6 +66,16 @@ class Caticor(StationCard): for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [ + c + for c in player.hand + if isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico) + ], + self.discard_and_buy_train, + ) + return True class CreepyCreek(StationCard): @@ -57,6 +90,12 @@ class CreepyCreek(StationCard): card.suit != cs.Suit.SPADES for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.suit == cs.Suit.SPADES], + self.discard_and_buy_train, + ) + return True class CrownsHole(StationCard): @@ -71,6 +110,12 @@ class CrownsHole(StationCard): not isinstance(card, cs.Birra) for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if isinstance(c, cs.Birra)], + self.discard_and_buy_train, + ) + return True class Deadwood(StationCard): @@ -85,6 +130,12 @@ class Deadwood(StationCard): not card.is_equipment for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.is_equipment], + self.discard_and_buy_train, + ) + return True class Dodgeville(StationCard): @@ -99,6 +150,12 @@ class Dodgeville(StationCard): not isinstance(card, cs.Mancato) for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if isinstance(c, cs.Mancato)], + self.discard_and_buy_train, + ) + return True class FortWorth(StationCard): @@ -113,6 +170,12 @@ class FortWorth(StationCard): card.number not in {1, 10, 11, 12, 13} for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.number in {1, 10, 11, 12, 13}], + self.discard_and_buy_train, + ) + return True class Frisco(StationCard): @@ -127,6 +190,12 @@ class Frisco(StationCard): card.suit != cs.Suit.CLUBS for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.suit == cs.Suit.CLUBS], + self.discard_and_buy_train, + ) + return True class MinersOath(StationCard): @@ -141,6 +210,12 @@ class MinersOath(StationCard): card.suit != cs.Suit.DIAMONDS for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.suit == cs.Suit.DIAMONDS], + self.discard_and_buy_train, + ) + return True class SanTafe(StationCard): @@ -155,6 +230,12 @@ class SanTafe(StationCard): card.suit != cs.Suit.HEARTS for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if c.suit == cs.Suit.HEARTS], + self.discard_and_buy_train, + ) + return True class Tombrock(StationCard): @@ -167,6 +248,12 @@ class Tombrock(StationCard): def check_price(self, player: "Player"): if player.lives <= 1: return False + player.set_choose_action( + "choose_buy_train", + [{"icon": "💔"}], + self.discard_and_buy_train, + ) + return True class Yooma(StationCard): @@ -181,6 +268,12 @@ class Yooma(StationCard): not (2 <= card.number <= 9) for card in player.hand ): return False + player.set_choose_action( + "choose_buy_train", + [c for c in player.hand if 2 <= c.number <= 9], + self.discard_and_buy_train, + ) + return True class VirginiaTown(StationCard): @@ -193,6 +286,12 @@ class VirginiaTown(StationCard): def check_price(self, player: "Player"): if super().check_price(player) and len(player.hand < 2): return False + player.set_choose_action( + "choose_buy_train", + player.hand.copy(), + self.discard_and_buy_train, + ) + return True def get_all_stations(): diff --git a/backend/bang/expansions/train_robbery/trains.py b/backend/bang/expansions/train_robbery/trains.py index 0193e0b..f2a4d72 100644 --- a/backend/bang/expansions/train_robbery/trains.py +++ b/backend/bang/expansions/train_robbery/trains.py @@ -9,6 +9,7 @@ class TrainCard(Card): self.is_equipment = True self.is_locomotive = is_locomotive self.expansion = "train_robbery" + self.type = "train" # Circus Wagon: gli altri giocatori diff --git a/backend/bang/players.py b/backend/bang/players.py index 47230e1..71d3e83 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -19,7 +19,7 @@ import bang.expansions.the_valley_of_shadows.cards as tvosc import bang.expansions.the_valley_of_shadows.characters as tvosch import bang.expansions.train_robbery.stations as trs import bang.expansions.train_robbery.trains as trt -from typing import List, TYPE_CHECKING +from typing import List, TYPE_CHECKING, Callable from metrics import Metrics from globals import G import sys @@ -104,6 +104,7 @@ class Player: self.is_bot = bot self.discord_token = discord_token self.discord_id = None + self.did_choose_callback = None self.played_cards = 0 self.avatar = "" self.last_played_card: cs.Card = None @@ -1402,6 +1403,9 @@ class Player: self.target_p = self.rissa_targets.pop(0).name print(f"rissa targets: {self.rissa_targets}") self.notify_self() + elif self.did_choose_callback is not None: + self.did_choose_callback(self, card_index) + self.notify_self() elif self.choose_text == "choose_ricercato": player = self.game.get_player_named( self.available_cards[card_index]["name"] @@ -2569,12 +2573,26 @@ class Player: if train is not None and not train.is_locomotive: if station.check_price(self): print(f"{station=} {train=}") + station.attached_train = train + # shift train forward + for i in range(train_index, len(self.game.deck.current_train) - 1): + self.game.deck.current_train[i] = self.game.deck.current_train[ + i + 1 + ] + self.game.notify_stations() + # self.game.deck.current_train[train_index] = None self.notify_self() - def set_choose_action(self, choose_text: str, available_cards: List): + def set_choose_action( + self, + choose_text: str, + available_cards: List, + did_choose_callback: Callable(Player, int) = None, + ): self.pending_action = PendingAction.CHOOSE self.choose_text = choose_text self.available_cards = available_cards + self.did_choose_callback = did_choose_callback def check_can_end_turn(self): must_be_used_cards = [c for c in self.hand if c.must_be_used] diff --git a/frontend/src/components/Card.vue b/frontend/src/components/Card.vue index 4b7b9b2..620c7a3 100644 --- a/frontend/src/components/Card.vue +++ b/frontend/src/components/Card.vue @@ -11,6 +11,7 @@ 'gold-rush': card.expansion === 'gold_rush', brown: card.kind === 0, black: card.kind === 1, + 'train-piece': card.type && card.type === 'train', }" >