From 1ee92afb9a546cffb84f0bdf2e911d3a281f5d38 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 20 Apr 2023 21:27:19 +0100 Subject: [PATCH] add trains --- backend/bang/deck.py | 16 ++ .../bang/expansions/train_robbery/trains.py | 205 +++++++++++++++++- backend/bang/game.py | 11 +- backend/server.py | 15 ++ frontend/src/components/Card.vue | 3 + frontend/src/components/Deck.vue | 27 ++- 6 files changed, 261 insertions(+), 16 deletions(-) diff --git a/backend/bang/deck.py b/backend/bang/deck.py index 52354c8..ef1a25a 100644 --- a/backend/bang/deck.py +++ b/backend/bang/deck.py @@ -6,6 +6,7 @@ import bang.expansions.wild_west_show.card_events as cew import bang.expansions.wild_west_show.characters as chw import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.train_robbery.stations as trs +import bang.expansions.train_robbery.trains as trt from globals import G if TYPE_CHECKING: @@ -36,6 +37,8 @@ class Deck: self.event_cards: List[ce.CardEvent] = [] self.event_cards_wildwestshow: List[ce.CardEvent] = [] self.stations: List[trs.StationCard] = [] + self.train_pile: List[trt.TrainCard] = [] + self.current_train: List[trt.TrainCard] = [] endgame_cards: List[ce.CardEvent] = [] if "fistful_of_cards" in game.expansions: self.event_cards.extend(ce.get_all_events(game.rng)) @@ -50,6 +53,10 @@ class Deck: self.event_cards_wildwestshow.append(cew.get_endgame_card()) if "train_robbery" in game.expansions: self.stations = game.rng.sample(trs.get_all_stations(), len(game.players)) + self.train_pile = trt.get_all_cards(game.rng) + self.current_train = [trt.get_locomotives(game.rng)[0]] + self.train_pile[ + :3 + ] if len(self.event_cards) > 0: game.rng.shuffle(self.event_cards) self.event_cards = self.event_cards[:12] @@ -109,6 +116,15 @@ class Deck: self.shop_cards[i].reset_card() self.game.notify_gold_rush_shop() + def move_train_forward(self): + if len(self.stations) == 0: + return + if len(self.current_train) == len(self.stations) + 4: + return + if len(self.current_train) > 0: + self.current_train.append(None) + self.game.notify_stations() + def peek(self, n_cards: int) -> list: return self.cards[:n_cards] diff --git a/backend/bang/expansions/train_robbery/trains.py b/backend/bang/expansions/train_robbery/trains.py index 53d93db..0193e0b 100644 --- a/backend/bang/expansions/train_robbery/trains.py +++ b/backend/bang/expansions/train_robbery/trains.py @@ -1,9 +1,10 @@ +import random from bang.cards import Card class TrainCard(Card): def __init__(self, name: str, is_locomotive: bool = False): - super().__init__(suit=5, number="", name=name) + super().__init__(suit=5, number=0, name=name) self.expansion_icon = "๐Ÿš‚" self.is_equipment = True self.is_locomotive = is_locomotive @@ -92,10 +93,212 @@ class Leland(TrainCard): return True +class BaggageCar(TrainCard): + """Scartalo: ottieni l'effetto di un Mancato!, Panico!, Cat Balou o di un BANG! extra. + Discard this for a Missed!Panic!, Cat Balou, or an extra BANG!""" + + def __init__(self): + super().__init__("Baggage Car") + self.icon = "๐Ÿš‹๐Ÿ›„" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class Caboose(TrainCard): + """Pro scartare un aura tua carta bordo bin incuso un vagone come se fosse un Mancato!""" + + def __init__(self): + super().__init__("Caboose") + self.icon = "๐Ÿš‹" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class CattleTruck(TrainCard): + """Scartalo: guarda le 3 carte in cima agli scarti e pescane I""" + + def __init__(self): + super().__init__("Cattle Truck") + self.icon = "๐Ÿš‹๐Ÿ„" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + class CircusWagon(TrainCard): + """Scartalo: ogni altro giocatore deve scartare una carta che ha in gioco.""" + def __init__(self): super().__init__("Circus Wagon", is_locomotive=True) self.icon = "๐Ÿš‹๐ŸŽช" def play_card(self, player, against=None, _with=None) -> bool: return True + + +class CoalHopper(TrainCard): + """Scartalo: pesca una carta e scarta un vagone in gioco davanti a un giocatore a ma scelta.""" + + def __init__(self): + super().__init__("Coal Hopper") + self.icon = "๐Ÿš‹๐Ÿ”ฅ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class DiningCar(TrainCard): + """A inizio turno, "estrai!": se รจ Cnori, recuperi I punto vita.""" + + def __init__(self): + super().__init__("Dining Car") + self.icon = "๐Ÿš‹๐Ÿฝ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class ExpressCar(TrainCard): + """Scarta tutte le carte in mano, poi gioca un altro turno""" + + def __init__(self): + super().__init__("Express Car") + self.icon = "๐Ÿš‹โšก" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class GhostCar(TrainCard): + """Giocalo su chiunque tranne lo Sceritfo. Se vieni eliminato, invece resti in gioco, ma non puol guada nare ne perdere punk vita.""" + + def __init__(self): + super().__init__("Ghost Car") + self.icon = "๐Ÿš‹๐Ÿ‘ป" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class LoungeCar(TrainCard): + """Scartalo: pesca 2 vagoni dal mazzo, mettine I in gioco di fronte a te e 1 di fronte a un altro giocatore.""" + + def __init__(self): + super().__init__("Lounge Car") + self.icon = "๐Ÿš‹๐Ÿ›‹" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class LumberFlatcar(TrainCard): + """Giocalo su un qualsiasi giocatore (compreso te). Finchรฉ questa carta รจ in gioco, quel giocatore vede gli altri giocatori a distanza aumentata di 1.""" + + def __init__(self): + super().__init__("Lumber Flatcar") + self.icon = "๐Ÿš‹๐Ÿชต" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class MailCar(TrainCard): + """Scartalo: pesca 3 carte e dai 1 di esse a un altro giocatore a tua scelta.""" + + def __init__(self): + super().__init__("Mail Car") + self.icon = "๐Ÿš‹๐Ÿ“ฎ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class ObservationCar(TrainCard): + """Tu vedi gli altri a distanza -1. Gli altri a vedono a distanza +1.""" + + def __init__(self): + super().__init__("Observation Car") + self.icon = "๐Ÿš‹๐Ÿ‘€" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class PassengerCar(TrainCard): + """Scartalo: pesca una carta (in mano o in gioco) da un altro giocatore""" + + def __init__(self): + super().__init__("Passenger Car") + self.icon = "๐Ÿš‹๐Ÿšถ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class PrisonerCar(TrainCard): + """Le carte Duello e Indiani! giocate dagli altri giocatori non hanno effetto su di te.""" + + def __init__(self): + super().__init__("Prisoner Car") + self.icon = "๐Ÿš‹๐Ÿ‘ฎ๐Ÿปโ€โ™‚๏ธ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class PrivateCar(TrainCard): + """se non hai carte in mano. non puoi essere bersaelio di carte BANG""" + + def __init__(self): + super().__init__("Private Car") + self.icon = "๐Ÿš‹๐Ÿ’๐Ÿป" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +class SleeperCar(TrainCard): + """Una volta per turno, puoi scartare un'altra tua carta a bordo blu incluso.""" + + def __init__(self): + super().__init__("Sleeper Car") + self.icon = "๐Ÿš‹๐Ÿ›Œ" + + def play_card(self, player, against=None, _with=None) -> bool: + return True + + +def get_all_cards(rng=random): + """Return a list of all train cards in the expansion""" + cars = [ + BaggageCar(), + Caboose(), + CattleTruck(), + CircusWagon(), + CoalHopper(), + DiningCar(), + ExpressCar(), + GhostCar(), + LoungeCar(), + LumberFlatcar(), + MailCar(), + ObservationCar(), + PassengerCar(), + PrisonerCar(), + PrivateCar(), + SleeperCar(), + ] + rng.shuffle(cars) + return cars + + +def get_locomotives(rng=random): + """Return a list of all locomotive cards in the expansion""" + locs = [ + Ironhorse(), + Leland(), + ] + rng.shuffle(locs) + return locs diff --git a/backend/bang/game.py b/backend/bang/game.py index dcc4894..ae3fcaa 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -59,6 +59,8 @@ debug_commands = [ {"cmd": "/getcard", "help": "Get a brand new card - sample /getcard Birra"}, {"cmd": "/meinfo", "help": "Get player data"}, {"cmd": "/gameinfo", "help": "Get game data"}, + {"cmd": "/deckinfo", "help": "Get deck data"}, + {"cmd": "/trainfw", "help": "move train forward"}, {"cmd": "/playerinfo", "help": "Get player data - sample /playerinfo player"}, {"cmd": "/cardinfo", "help": "Get card data - sample /cardinfo handindex"}, {"cmd": "/mebot", "help": "Toggles bot mode"}, @@ -844,6 +846,7 @@ class Game: ) ): self.deck.flip_event() + self.deck.move_train_forward() if self.check_event(ce.RouletteRussa): self.is_russian_roulette_on = True if self.players[self.turn].get_banged(self.deck.event_cards[0]): @@ -925,7 +928,13 @@ class Game: G.sio.emit( "stations", room=room, - data=json.dumps(self.deck.stations, default=lambda o: o.__dict__), + data=json.dumps( + { + "stations": self.deck.stations, + "current_train": self.deck.current_train, + }, + default=lambda o: o.__dict__, + ), ) def notify_scrap_pile(self, sid=None): diff --git a/backend/server.py b/backend/server.py index ba45db7..f7e7274 100644 --- a/backend/server.py +++ b/backend/server.py @@ -1089,6 +1089,21 @@ def chat_message(sid, msg, pl=None): "type": "json", }, ) + elif "/deckinfo" in msg: + sio.emit( + "chat_message", + room=sid, + data={ + "color": "", + "text": json.dumps( + ses.game.deck.__dict__, + default=lambda o: f"<{o.__class__.__name__}() not serializable>", + ), + "type": "json", + }, + ) + elif "/trainfw" in msg: + ses.game.deck.move_train_forward() elif "/status" in msg and ses.is_admin(): sio.emit("mount_status", room=sid) elif "/meinfo" in msg: diff --git a/frontend/src/components/Card.vue b/frontend/src/components/Card.vue index 2f17766..4b7b9b2 100644 --- a/frontend/src/components/Card.vue +++ b/frontend/src/components/Card.vue @@ -355,6 +355,9 @@ export default { transform: scaleX(-1); /* filter: grayscale(1); */ } +.train-piece .suit, .train-piece .expansion { + display: none; +} .train-piece h4 { position: absolute; text-align: center; diff --git a/frontend/src/components/Deck.vue b/frontend/src/components/Deck.vue index 157b20f..0f9c771 100644 --- a/frontend/src/components/Deck.vue +++ b/frontend/src/components/Deck.vue @@ -11,18 +11,8 @@ -
- +
+
@@ -105,7 +95,8 @@ export default { goldRushDesc: null, can_gold_rush_discard: false, gold_rush_discount: 0, - current_stations: [], + currentStations: [], + currentTrain: [], }), sockets: { self(self){ @@ -140,7 +131,9 @@ export default { this.goldRushCards = JSON.parse(cards) }, stations(stations) { - this.current_stations = JSON.parse(stations) + let msg = JSON.parse(stations) + this.currentStations = msg.stations + this.currentTrain = msg.current_train }, }, computed: { @@ -182,6 +175,12 @@ export default { }, }, methods: { + trainPieceForStation(i) { + let index = this.currentTrain.length-5-i; + if (index < 0 || index >= this.currentTrain.length) + return null; + return this.currentTrain[index]; + }, action(pile) { if (this.pending_action !== false && this.pending_action < 2) { // console.log('action')