also add new animations

This commit is contained in:
Alberto Xamin 2024-06-14 18:09:24 +03:00
parent c171739db8
commit a474dfbc24
No known key found for this signature in database
GPG Key ID: 5ABFCD8A22EA6F5D
11 changed files with 104 additions and 20 deletions

View File

@ -16,6 +16,7 @@ class TrainCard(Card):
self.is_locomotive = is_locomotive self.is_locomotive = is_locomotive
self.expansion = "train_robbery" self.expansion = "train_robbery"
self.type = "train" self.type = "train"
self.implemented = True
# Circus Wagon: gli altri giocatori # Circus Wagon: gli altri giocatori
@ -122,7 +123,7 @@ class BaggageCar(TrainCard):
class Caboose(TrainCard): class Caboose(TrainCard):
"""Pro scartare un altra tua carta bordo blu incuso un vagone come se fosse un Mancato!""" """Puoi scartare un altra tua carta bordo blu incuso un vagone come se fosse un Mancato!"""
def __init__(self): def __init__(self):
super().__init__("Caboose") super().__init__("Caboose")
@ -177,18 +178,21 @@ class CircusWagon(TrainCard):
class CoalHopper(TrainCard): class CoalHopper(TrainCard):
"""Scartalo: pesca una carta e scarta un vagone in gioco davanti a un giocatore a ma scelta.""" """Scartalo: pesca una carta e scarta un vagone in gioco davanti a un giocatore a tua scelta."""
def __init__(self): def __init__(self):
super().__init__("Coal Hopper") super().__init__("Coal Hopper")
self.icon = "🚋🔥" self.icon = "🚋🔥"
self.need_target = True
def play_card(self, player, against=None, _with=None) -> bool: def play_card(self, player, against=None, _with=None) -> bool:
if against is not None and len(player.game.get_player_named(against).equipment) > 0:
player.game.steal_discard(player, against, self)
return True return True
class DiningCar(TrainCard): class DiningCar(TrainCard):
"""A inizio turno, "estrai!": se è Cnori, recuperi I punto vita.""" """A inizio turno, "estrai!": se è Cuori, recuperi I punto vita."""
def __init__(self): def __init__(self):
super().__init__("Dining Car") super().__init__("Dining Car")
@ -214,14 +218,15 @@ class ExpressCar(TrainCard):
class GhostCar(TrainCard): class GhostCar(TrainCard):
"""Giocalo su chiunque tranne lo Sceritfo. Se vieni eliminato, invece resti in gioco, ma non puo guadagnare ne perdere punti vita.""" """Giocalo su chiunque tranne lo Sceritfo. Se vieni eliminato, invece resta in gioco, ma non puo guadagnare ne perdere punti vita."""
def __init__(self): def __init__(self):
super().__init__("Ghost Car") super().__init__("Ghost Car")
self.icon = "🚋👻" self.icon = "🚋👻"
self.implemented = False
def play_card(self, player, against=None, _with=None) -> bool: def play_card(self, player, against=None, _with=None) -> bool:
return True return False
class LoungeCar(TrainCard): class LoungeCar(TrainCard):
@ -230,6 +235,7 @@ class LoungeCar(TrainCard):
def __init__(self): def __init__(self):
super().__init__("Lounge Car") super().__init__("Lounge Car")
self.icon = "🚋🛋" self.icon = "🚋🛋"
self.implemented = False
def play_card(self, player, against=None, _with=None) -> bool: def play_card(self, player, against=None, _with=None) -> bool:
return True return True
@ -299,14 +305,23 @@ class ObservationCar(TrainCard):
class PassengerCar(TrainCard): class PassengerCar(TrainCard):
"""Scartalo: pesca una carta (in mano o in gioco) da un altro giocatore""" """Scartalo: pesca una carta (o in mano o in gioco) da un altro giocatore"""
def __init__(self): def __init__(self):
super().__init__("Passenger Car") super().__init__("Passenger Car")
self.icon = "🚋🚶" self.icon = "🚋🚶"
self.range = 99
self.need_target = True
def play_card(self, player, against=None, _with=None) -> bool: def play_card(self, player, against=None, _with=None) -> bool:
return True if (
against is not None
and (len(player.equipment) > 0 or len(player.equipment) > 0)
):
player.game.steal_discard(player, against, self)
return True
return False
class PrisonerCar(TrainCard): class PrisonerCar(TrainCard):
@ -321,7 +336,7 @@ class PrisonerCar(TrainCard):
class PrivateCar(TrainCard): class PrivateCar(TrainCard):
"""se non hai carte in mano. non puoi essere bersaelio di carte BANG""" """Se non hai carte in mano, non puoi essere bersaglio di carte BANG"""
def __init__(self): def __init__(self):
super().__init__("Private Car") super().__init__("Private Car")
@ -376,6 +391,7 @@ def get_all_cards(rng=random):
PrivateCar(), PrivateCar(),
SleeperCar(), SleeperCar(),
] ]
cars = [c for c in cars if c.implemented]
rng.shuffle(cars) rng.shuffle(cars)
return cars return cars

View File

@ -17,6 +17,7 @@ import bang.expansions.wild_west_show.card_events as cew
import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.shop_cards as grc
import bang.expansions.gold_rush.characters as grch import bang.expansions.gold_rush.characters as grch
import bang.expansions.the_valley_of_shadows.cards as tvosc import bang.expansions.the_valley_of_shadows.cards as tvosc
import bang.expansions.train_robbery.trains as trt
from metrics import Metrics from metrics import Metrics
from globals import G, PendingAction from globals import G, PendingAction
@ -558,7 +559,7 @@ class Game:
if p != attacker and p.get_discarded( if p != attacker and p.get_discarded(
attacker, attacker,
card_name=card.name, card_name=card.name,
action="steal" if isinstance(card, cs.Panico) else "discard", action="steal" if (isinstance(card, cs.Panico) or isinstance(card, trt.PassengerCar)) else "discard",
): ):
self.ready_count = 0 self.ready_count = 0
self.waiting_for = 1 self.waiting_for = 1

View File

@ -737,6 +737,9 @@ class Player:
self.has_played_bang = False self.has_played_bang = False
self.special_use_count = 0 self.special_use_count = 0
self.bang_used = 0 self.bang_used = 0
if any((isinstance(c, trt.DiningCar) for c in self.equipment)):
if self.game.deck.pick_and_scrap().suit == cs.Suit.HEARTS:
self.lives = min(self.lives + 1, self.max_lives)
if self.game.check_event(cew.DarlingValentine): if self.game.check_event(cew.DarlingValentine):
hand = len(self.hand) hand = len(self.hand)
for _ in range(hand): for _ in range(hand):
@ -1866,6 +1869,8 @@ class Player:
self.expected_response.append(cs.Bang(0, 0).name) self.expected_response.append(cs.Bang(0, 0).name)
if self.character.check(self.game, chw.BigSpencer): if self.character.check(self.game, chw.BigSpencer):
self.expected_response = [] self.expected_response = []
if any((isinstance(c, trt.Caboose) for c in self.equipment)):
self.expected_response.append([c.name for c in self.equipment if not c.usable_next_turn])
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
self.notify_self() self.notify_self()
@ -1997,6 +2002,17 @@ class Player:
self.attacker = attacker self.attacker = attacker
self.attacking_card = card_name self.attacking_card = card_name
print(f"attacker -> {attacker}") print(f"attacker -> {attacker}")
# check for trt.PrivateCar
if (card_name == "Bang!" and any(
(isinstance(c, trt.PrivateCar) for c in self.equipment)
) and len(self.hand) == 0):
self.take_no_damage_response()
G.sio.emit(
"chat_message",
room=self.game.name,
data=f"_in_private_car|{self.name}|{attacker.name}",
)
return False
if ( if (
isinstance(attacker, Player) isinstance(attacker, Player)
and attacker.character.check(self.game, tvosch.ColoradoBill) and attacker.character.check(self.game, tvosch.ColoradoBill)
@ -2112,6 +2128,11 @@ class Player:
(isinstance(c, grc.Calumet) for c in self.gold_rush_equipment) (isinstance(c, grc.Calumet) for c in self.gold_rush_equipment)
): ):
return False return False
# check for trt.PrisonerCar
if any(
(isinstance(c, trt.PrisonerCar) for c in self.equipment)
):
return False
if ( if (
not self.game.is_competitive not self.game.is_competitive
and not any( and not any(
@ -2145,6 +2166,12 @@ class Player:
def get_dueled(self, attacker): def get_dueled(self, attacker):
self.attacker = attacker self.attacker = attacker
self.attacking_card = "Duello" self.attacking_card = "Duello"
if any(
not self.is_my_turn and
(isinstance(c, trt.PrisonerCar) for c in self.equipment)
):
self.take_no_damage_response()
return False
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or ( if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (
not self.game.is_competitive not self.game.is_competitive
and not any( and not any(
@ -2551,6 +2578,11 @@ class Player:
self.game.deck.shop_deck.append(card) self.game.deck.shop_deck.append(card)
self.game.deck.shop_cards[index] = None self.game.deck.shop_cards[index] = None
self.game.deck.fill_gold_rush_shop() self.game.deck.fill_gold_rush_shop()
G.sio.emit(
"card_scrapped",
room=self.game.name,
data={"player": self.name, "pile": "gold_rush", "card": card.__dict__},
)
self.notify_self() self.notify_self()
def buy_train(self, index): def buy_train(self, index):
@ -2569,6 +2601,16 @@ class Player:
if station.check_price(self): if station.check_price(self):
print(f"{station=} {train=}") print(f"{station=} {train=}")
station.attached_train = train station.attached_train = train
G.sio.emit(
"chat_message",
room=self.game.name,
data=f"_bought_train|{self.name}|{station.name}|{train.name}",
)
G.sio.emit(
"card_scrapped",
room=self.game.name,
data={"player": self.name, "pile": "train_robbery", "card": train.__dict__},
)
# shift train forward # shift train forward
for i in range(train_index, len(self.game.deck.current_train) - 1): for i in range(train_index, len(self.game.deck.current_train) - 1):
self.game.deck.current_train[i] = self.game.deck.current_train[ self.game.deck.current_train[i] = self.game.deck.current_train[

View File

@ -1317,7 +1317,6 @@ def get_trainrobberycards(sid):
import bang.expansions.train_robbery.trains as trt import bang.expansions.train_robbery.trains as trt
chs = [] chs = []
chs.extend(trs.get_all_stations())
chs.extend(trt.get_locomotives()) chs.extend(trt.get_locomotives())
chs.extend(trt.get_all_cards()) chs.extend(trt.get_all_cards())
sio.emit( sio.emit(

View File

@ -65,7 +65,7 @@ export default {
}, },
suit() { suit() {
if (this.card && !isNaN(this.card.suit)) { if (this.card && !isNaN(this.card.suit)) {
let x = ["♦️", "♣️", "♥️", "♠️", "🤑"]; let x = ["♦️", "♣️", "♥️", "♠️", "🤑", "🚂"];
return x[this.card.suit]; return x[this.card.suit];
} else if (this.card.suit) { } else if (this.card.suit) {
return this.card.suit; return this.card.suit;
@ -232,6 +232,18 @@ export default {
padding: 4pt; padding: 4pt;
border-radius: 12pt; border-radius: 12pt;
} }
.wip::after {
content: "WIP";
position: absolute;
bottom: -12pt;
right: -12pt;
background: red;
font-size: 10pt;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
padding: 4pt;
border-radius: 12pt;
}
.avatar { .avatar {
position: absolute; position: absolute;
width: 36pt; width: 36pt;

View File

@ -1,7 +1,7 @@
<template> <template>
<div > <div >
<div class="deck"> <div class="deck">
<div class="deck" :style="`position:relative;${goldRushShopOpen?'border: 2px dashed #6a6a6a42;border-radius:8pt':''}`" v-if="goldRushCards.length > 0" > <div class="deck" id="gold-rush-deck" :style="`position:relative;${goldRushShopOpen?'border: 2px dashed #6a6a6a42;border-radius:8pt':''}`" v-if="goldRushCards.length > 0" >
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[0])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(95deg) translate(30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 0" :key="goldRushCards[0].name" :card="goldRushCards[0]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[0].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(0)}"/> <card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[0])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(95deg) translate(30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 0" :key="goldRushCards[0].name" :card="goldRushCards[0]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[0].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(0)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[1])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(90deg) translate(0, -40px) scale(0.6)`" v-if="goldRushCards.length > 1" :key="goldRushCards[1].name" :card="goldRushCards[1]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[1].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(1)}"/> <card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[1])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(90deg) translate(0, -40px) scale(0.6)`" v-if="goldRushCards.length > 1" :key="goldRushCards[1].name" :card="goldRushCards[1]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[1].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(1)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[2])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(85deg) translate(-30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 2" :key="goldRushCards[2].name" :card="goldRushCards[2]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[2].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(2)}"/> <card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[2])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(85deg) translate(-30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 2" :key="goldRushCards[2].name" :card="goldRushCards[2]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[2].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(2)}"/>
@ -11,7 +11,7 @@
<card :card="goldRushCardBack" :donotlocalize="true" class="gold-rush back last-event" @click.native="goldRushShopOpen = !goldRushShopOpen"/> <card :card="goldRushCardBack" :donotlocalize="true" class="gold-rush back last-event" @click.native="goldRushShopOpen = !goldRushShopOpen"/>
</div> </div>
</div> </div>
<div v-if="currentStations.length > 0" class="deck" :style="`position:relative;border: 2px dashed #6a6a6a42;border-radius:8pt;align-items: flex-end;flex-direction:row;`" > <div v-if="currentStations.length > 0" id="train-robbery-deck" class="deck" :style="`position:relative;border: 2px dashed #6a6a6a42;border-radius:8pt;align-items: flex-end;flex-direction:row;`" >
<station-card @click.native="()=>{buyTrain(i)}" v-for="station, i in currentStations" :key="station.name" :card="station" :price="station.price" :trainPiece="trainPieceForStation(i)" <station-card @click.native="()=>{buyTrain(i)}" v-for="station, i in currentStations" :key="station.name" :card="station" :price="station.price" :trainPiece="trainPieceForStation(i)"
@pointerenter.native="()=>{setStationDesc(i)}" @pointerleave.native="stationDesc = null"/> @pointerenter.native="()=>{setStationDesc(i)}" @pointerleave.native="stationDesc = null"/>
</div> </div>

View File

@ -646,8 +646,15 @@ export default {
console.log("card_scrapped no deck"); console.log("card_scrapped no deck");
return; return;
} }
let decelOffset = cumulativeOffset(decel);
let phand = document.getElementById(`${data.player}-hand`); let phand = document.getElementById(`${data.player}-hand`);
if (data.pile == "train_robbery") {
decel = phand
phand = document.getElementById("train-robbery-deck");
} else if (data.pile == "gold_rush") {
decel = phand
phand = document.getElementById("gold-rush-deck");
}
let decelOffset = cumulativeOffset(decel);
if (!phand) { if (!phand) {
console.log("card_scrapped no phand"); console.log("card_scrapped no phand");
return; return;

View File

@ -104,6 +104,7 @@
"choose_mail_car": "Choose which card to give to another player", "choose_mail_car": "Choose which card to give to another player",
"choose_other_player": "Choose the player to give the card to", "choose_other_player": "Choose the player to give the card to",
"choose_sleeper_car": "Choose a card to discard for Sleeper Car", "choose_sleeper_car": "Choose a card to discard for Sleeper Car",
"choose_buy_train": "Discard a card to rob the train, and get the carriage",
"chat": { "chat": {
"spectators": " | A spectator is watching the game | {n} spectators are watching the game", "spectators": " | A spectator is watching the game | {n} spectators are watching the game",
"chat": "Chat", "chat": "Chat",
@ -157,7 +158,9 @@
"choose_emporio": ";{0}; has chosen ;{1}; from General Store.", "choose_emporio": ";{0}; has chosen ;{1}; from General Store.",
"shotgun_scrap": "When the shotgun hit ;{0}; a card flew away from his hand (;{1};)", "shotgun_scrap": "When the shotgun hit ;{0}; a card flew away from his hand (;{1};)",
"taglia_reward": "💰 ;{1}; got a card from the bounty on ;{0};", "taglia_reward": "💰 ;{1}; got a card from the bounty on ;{0};",
"snake_bit": "🐍 ;{0}; was bitten by the Rattle Snake." "snake_bit": "🐍 ;{0}; was bitten by the Rattle Snake.",
"in_private_car": "🚋💁🏻 ;{0}; is in a private car and couldn't get Bang!ed by ;{1};",
"bought_train": "🚂 ;{0}; robbed the train at ;{1}; and got the ;{2};"
}, },
"foc": { "foc": {
"leggedelwest": "He must play this card on this turn if possible." "leggedelwest": "He must play this card on this turn if possible."
@ -942,7 +945,7 @@
}, },
"Dining Car": { "Dining Car": {
"name": "Dining Car", "name": "Dining Car",
"desc": "At the beginning of your turn, \"draw!\": if it's Hearts, regain 1 life point." "desc": "At the beginning of your turn, \"pick!\": if it's Hearts, regain 1 life point."
}, },
"Express Car": { "Express Car": {
"name": "Express Car", "name": "Express Car",
@ -1034,7 +1037,9 @@
"highnooncards": "High Noon - Event Cards", "highnooncards": "High Noon - Event Cards",
"foccards": "Fistful of Cards - Event Cards", "foccards": "Fistful of Cards - Event Cards",
"goldrushcards": "Gold Rush Cards", "goldrushcards": "Gold Rush Cards",
"valleyofshadowscards": "The Valley of Shadows Cards" "valleyofshadowscards": "The Valley of Shadows Cards",
"wildwestshowcards": "Wild West Show Cards",
"trainrobberycards": "Train Robbery Cards"
}, },
"theme": { "theme": {
"sepia": "Sepia", "sepia": "Sepia",

View File

@ -153,7 +153,8 @@
"choose_emporio": ";{0}; ha scelto ;{1}; da Emporio.", "choose_emporio": ";{0}; ha scelto ;{1}; da Emporio.",
"shotgun_scrap": "Quando lo shotgun ha colpito ;{0}; gli ha tolto una carta (;{1};)", "shotgun_scrap": "Quando lo shotgun ha colpito ;{0}; gli ha tolto una carta (;{1};)",
"taglia_reward": "💰 ;{1}; ha ottenuto ricompensa dalla taglia su ;{0};", "taglia_reward": "💰 ;{1}; ha ottenuto ricompensa dalla taglia su ;{0};",
"snake_bit": "🐍 ;{0}; è stato morso dal Serpente a Sonagli." "snake_bit": "🐍 ;{0}; è stato morso dal Serpente a Sonagli.",
"in_private_car": "🚋💁🏻 ;{0}; è in una carrozza privata e non è stato attaccato da ;{1};"
}, },
"foc": { "foc": {
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile" "leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"
@ -938,7 +939,7 @@
}, },
"Dining Car": { "Dining Car": {
"name": "Dining Car", "name": "Dining Car",
"desc": "A inizio turno, \"estrai!\": se è Cnori, recuperi I punto vita." "desc": "A inizio turno, \"estrai!\": se è Cuori, recuperi I punto vita."
}, },
"Express Car": { "Express Car": {
"name": "Express Car", "name": "Express Car",

View File

@ -5,4 +5,5 @@ export const emojiMap = {
'fistful_of_cards': '🎴', 'fistful_of_cards': '🎴',
'the_valley_of_shadows': '👻', 'the_valley_of_shadows': '👻',
'wild_west_show': '🎪', 'wild_west_show': '🎪',
'train_robbery': '🚂',
} }

View File

@ -41,6 +41,6 @@ export const expansionsMap = {
icon: '🚂', icon: '🚂',
back: true, back: true,
expansion: 'train-roobbery', expansion: 'train-roobbery',
status: 'alpha', status: 'wip',
} }
} }