card event refactoring and moved wild-west-show to its own pile

This commit is contained in:
Alberto Xamin 2023-04-02 13:47:08 +01:00
parent f56120d44e
commit e3b168ff57
No known key found for this signature in database
GPG Key ID: 5ABFCD8A22EA6F5D
12 changed files with 3772 additions and 1452 deletions

View File

@ -11,6 +11,7 @@ if TYPE_CHECKING:
from bang.players import Player from bang.players import Player
from bang.game import Game from bang.game import Game
class Suit(IntEnum): class Suit(IntEnum):
DIAMONDS = 0 # ♦ DIAMONDS = 0 # ♦
CLUBS = 1 # ♣ CLUBS = 1 # ♣
@ -20,14 +21,20 @@ class Suit(IntEnum):
class Card(ABC): class Card(ABC):
sym = { sym = {"A": 1, "J": 11, "Q": 12, "K": 13}
'A': 1,
'J': 11,
'Q': 12,
'K': 13
}
def __init__(self, suit: Suit, name: str, number, is_equipment: bool = False, is_weapon: bool = False, vis_mod: int = 0, sight_mod: int = 0, range: int = 99, desc: str = ''): def __init__(
self,
suit: Suit,
name: str,
number,
is_equipment: bool = False,
is_weapon: bool = False,
vis_mod: int = 0,
sight_mod: int = 0,
range: int = 99,
desc: str = "",
):
super().__init__() super().__init__()
self.name = name self.name = name
self.suit = suit self.suit = suit
@ -41,22 +48,24 @@ class Card(ABC):
self.sight_mod = sight_mod self.sight_mod = sight_mod
self.range = range self.range = range
if self.range != 0 and self.range != 99: if self.range != 0 and self.range != 99:
self.alt_text = f'{self.range} 🔍' self.alt_text = f"{self.range} 🔍"
self.desc = desc # deprecated, has been replaced by the card's description in the localization files (see i18n folder) self.desc = desc # deprecated, has been replaced by the card's description in the localization files (see i18n folder)
self.need_target = False # Cards that need a target like Bang self.need_target = False # Cards that need a target like Bang
self.can_target_self = False # for example Panico and CatBalou self.can_target_self = False # for example Panico and CatBalou
self.can_be_used_now = True # to check wether the green card can be used now self.can_be_used_now = True # to check wether the green card can be used now
self.usable_next_turn = False # it will be True for Green Cards self.usable_next_turn = False # it will be True for Green Cards
self.need_with = False # it will be true for cards that require a card to be discarded with self.need_with = (
self.need_with_only = '' # names of the cards allowed to be discarded with False # it will be true for cards that require a card to be discarded with
self.must_be_used = False # used by LeggeDelWest )
self.need_with_only = "" # names of the cards allowed to be discarded with
self.must_be_used = False # used by LeggeDelWest
def __str__(self): def __str__(self):
if str(self.suit).isnumeric(): if str(self.suit).isnumeric():
char = ['♦️', '♣️', '♥️', '♠️', '🤑'][int(self.suit)] char = ["♦️", "♣️", "♥️", "♠️", "🤑"][int(self.suit)]
else: else:
char = self.suit char = self.suit
return f'{self.name} {char}{self.number}' return f"{self.name} {char}{self.number}"
return super().__str__() return super().__str__()
def num_suit(self): def num_suit(self):
@ -70,8 +79,14 @@ class Card(ABC):
if self.must_be_used: if self.must_be_used:
self.must_be_used = False self.must_be_used = False
def play_card(self, player:Player, against:str=None, _with:int=None):#self --> carta def play_card(
if (player.game.check_event(ce.IlGiudice)) and self.usable_next_turn and not self.can_be_used_now: self, player: Player, against: str = None, _with: int = None
): # self --> carta
if (
(player.game.check_event(ce.IlGiudice))
and self.usable_next_turn
and not self.can_be_used_now
):
return False return False
if self.is_equipment: if self.is_equipment:
if (player.game.check_event(ce.IlGiudice)) or not self.can_be_used_now: if (player.game.check_event(ce.IlGiudice)) or not self.can_be_used_now:
@ -86,38 +101,53 @@ class Card(ABC):
break break
if not has_weapon: if not has_weapon:
player.equipment.append(self) player.equipment.append(self)
elif self.name in [c.name for c in player.equipment if not isinstance(c, Dinamite)]: elif self.name in [
c.name for c in player.equipment if not isinstance(c, Dinamite)
]:
return False return False
else: else:
player.equipment.append(self) player.equipment.append(self)
self.must_be_used = False self.must_be_used = False
self.can_be_used_now = False self.can_be_used_now = False
if against: if against:
G.sio.emit('card_against', room=player.game.name, data={'player': player.name, 'target': against, 'card': self.__dict__}) G.sio.emit(
G.sio.emit('chat_message', room=player.game.name, "card_against",
data=f'_play_card_against|{player.name}|{self.name}|{against}') room=player.game.name,
data={"player": player.name, "target": against, "card": self.__dict__},
)
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_against|{player.name}|{self.name}|{against}",
)
else: else:
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_play_card|{player.name}|{self.name}') "chat_message",
room=player.game.name,
data=f"_play_card|{player.name}|{self.name}",
)
return True return True
def use_card(self, player): def use_card(self, player):
pass pass
def is_duplicate_card(self, player:Player): def is_duplicate_card(self, player: Player):
return any(c.name==self.name for c in player.equipment) or any(c.name==self.name for c in player.gold_rush_equipment) return any(c.name == self.name for c in player.equipment) or any(
c.name == self.name for c in player.gold_rush_equipment
)
def check_suit(self, game:Game, accepted:List[Suit]): def check_suit(self, game: Game, accepted: List[Suit]):
if game.check_event(ceh.Benedizione): if game.check_event(ceh.Benedizione):
return Suit.HEARTS in accepted return Suit.HEARTS in accepted
elif game.check_event(ceh.Maledizione): elif game.check_event(ceh.Maledizione):
return Suit.SPADES in accepted return Suit.SPADES in accepted
return self.suit in accepted return self.suit in accepted
class Barile(Card): class Barile(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Barile', number, is_equipment=True) super().__init__(suit, "Barile", number, is_equipment=True)
self.icon = '🛢' self.icon = "🛢"
self.alt_text = "♥️=😅" self.alt_text = "♥️=😅"
# self.desc = "Quando sei bersagliato da un Bang puoi estrarre la prima carta dalla cima del mazzo, se la carta estratta è del seme Cuori allora vale come un Mancato" # self.desc = "Quando sei bersagliato da un Bang puoi estrarre la prima carta dalla cima del mazzo, se la carta estratta è del seme Cuori allora vale come un Mancato"
# self.desc_eng = "When someone plays a Bang against you. You can flip the first card from the deck, if the suit is Hearts then it counts as a Missed card" # self.desc_eng = "When someone plays a Bang against you. You can flip the first card from the deck, if the suit is Hearts then it counts as a Missed card"
@ -125,8 +155,8 @@ class Barile(Card):
class Dinamite(Card): class Dinamite(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Dinamite', number, is_equipment=True) super().__init__(suit, "Dinamite", number, is_equipment=True)
self.icon = '🧨' self.icon = "🧨"
self.alt_text = "2-9♠ = 🤯" self.alt_text = "2-9♠ = 🤯"
# self.desc = "Giocando la Dinamite, posizionala davanti a te, resterà innocua per un intero giro. All'inizio del prossimo turno prima di pescare e prima di una eventuale estrazione (es. Prigione), estrai una carta dalla cima del mazzo. Se esce una carta tra il 2 il 9 di picche (compresi) allora la dinamite esplode: perdi 3 vite e scarta la carta, altrimenti passa la dinamite al giocatore successivo, il quale estrarà a sua volta dopo che tu avrai passato il tuo turno" # self.desc = "Giocando la Dinamite, posizionala davanti a te, resterà innocua per un intero giro. All'inizio del prossimo turno prima di pescare e prima di una eventuale estrazione (es. Prigione), estrai una carta dalla cima del mazzo. Se esce una carta tra il 2 il 9 di picche (compresi) allora la dinamite esplode: perdi 3 vite e scarta la carta, altrimenti passa la dinamite al giocatore successivo, il quale estrarà a sua volta dopo che tu avrai passato il tuo turno"
# self.desc_eng = "When playing Dynamite, place it in front of you, it will remain harmless for a whole round. At the beginning of the next turn before drawing and before any card flip (eg Prison), flip a card from the top of the deck. If a card is between 2 and 9 of spades (inclusive) then the dynamite explodes: you lose 3 lives and discard the card, otherwise pass the dynamite to the next player, who will draw in turn after you have ended your turn" # self.desc_eng = "When playing Dynamite, place it in front of you, it will remain harmless for a whole round. At the beginning of the next turn before drawing and before any card flip (eg Prison), flip a card from the top of the deck. If a card is between 2 and 9 of spades (inclusive) then the dynamite explodes: you lose 3 lives and discard the card, otherwise pass the dynamite to the next player, who will draw in turn after you have ended your turn"
@ -134,8 +164,8 @@ class Dinamite(Card):
class Mirino(Card): class Mirino(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Mirino', number, is_equipment=True, sight_mod=1) super().__init__(suit, "Mirino", number, is_equipment=True, sight_mod=1)
self.icon = '🔎' self.icon = "🔎"
self.alt_text = "-1" self.alt_text = "-1"
# self.desc = "Tu vedi gli altri giocatori a distanza -1" # self.desc = "Tu vedi gli altri giocatori a distanza -1"
# self.desc_eng = "You see the other players at distance -1" # self.desc_eng = "You see the other players at distance -1"
@ -143,8 +173,8 @@ class Mirino(Card):
class Mustang(Card): class Mustang(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Mustang', number, is_equipment=True, vis_mod=1) super().__init__(suit, "Mustang", number, is_equipment=True, vis_mod=1)
self.icon = '🐎' self.icon = "🐎"
self.alt_text = "+1" self.alt_text = "+1"
# self.desc = "Gli altri giocatori ti vedono a distanza +1" # self.desc = "Gli altri giocatori ti vedono a distanza +1"
# self.desc_eng = "The other players see you at distance +1" # self.desc_eng = "The other players see you at distance +1"
@ -152,100 +182,130 @@ class Mustang(Card):
class Prigione(Card): class Prigione(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Prigione', number, is_equipment=True) super().__init__(suit, "Prigione", number, is_equipment=True)
self.icon = '' self.icon = ""
# self.desc = "Equipaggia questa carta a un altro giocatore, tranne lo Sceriffo. Il giocatore scelto all'inizio del suo turno, prima di pescare dovrà estrarre: se esce Cuori scarta questa carta e gioca normalmente il turno, altrimenti scarta questa carta e salta il turno" # self.desc = "Equipaggia questa carta a un altro giocatore, tranne lo Sceriffo. Il giocatore scelto all'inizio del suo turno, prima di pescare dovrà estrarre: se esce Cuori scarta questa carta e gioca normalmente il turno, altrimenti scarta questa carta e salta il turno"
# self.desc_eng = "Equip this card to another player, except the Sheriff. The player chosen at the beginning of his turn, must flip a card before drawing: if it's Hearts, discard this card and play the turn normally, otherwise discard this card and skip the turn" # self.desc_eng = "Equip this card to another player, except the Sheriff. The player chosen at the beginning of his turn, must flip a card before drawing: if it's Hearts, discard this card and play the turn normally, otherwise discard this card and skip the turn"
self.need_target = True self.need_target = True
self.alt_text = "♥️= 🆓" self.alt_text = "♥️= 🆓"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if (player.game.check_event(ce.IlGiudice)): if player.game.check_event(ce.IlGiudice):
return False return False
if against is not None and not isinstance(player.game.get_player_named(against).role, r.Sheriff): if against is not None and not isinstance(
player.game.get_player_named(against).role, r.Sheriff
):
self.can_be_used_now = False self.can_be_used_now = False
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_play_card_against|{player.name}|{self.name}|{against}') "chat_message",
room=player.game.name,
data=f"_play_card_against|{player.name}|{self.name}|{against}",
)
player.game.get_player_named(against).equipment.append(self) player.game.get_player_named(against).equipment.append(self)
player.game.get_player_named(against).notify_self() player.game.get_player_named(against).notify_self()
return True return True
return False return False
class Remington(Card): class Remington(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Remington', number, super().__init__(
is_equipment=True, is_weapon=True, range=3) suit, "Remington", number, is_equipment=True, is_weapon=True, range=3
self.icon = '🔫' )
self.icon = "🔫"
# self.desc = "Puoi sparare a un giocatore che sia distante 3 o meno" # self.desc = "Puoi sparare a un giocatore che sia distante 3 o meno"
# self.desc_eng = "You can shoot another player at distance 3 or less" # self.desc_eng = "You can shoot another player at distance 3 or less"
class RevCarabine(Card): class RevCarabine(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Rev Carabine', number, super().__init__(
is_equipment=True, is_weapon=True, range=4) suit, "Rev Carabine", number, is_equipment=True, is_weapon=True, range=4
self.icon = '🔫' )
self.icon = "🔫"
# self.desc = "Puoi sparare a un giocatore che sia distante 4 o meno" # self.desc = "Puoi sparare a un giocatore che sia distante 4 o meno"
# self.desc_eng = "You can shoot another player at distance 4 or less" # self.desc_eng = "You can shoot another player at distance 4 or less"
class Schofield(Card): class Schofield(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Schofield', number, super().__init__(
is_equipment=True, is_weapon=True, range=2) suit, "Schofield", number, is_equipment=True, is_weapon=True, range=2
self.icon = '🔫' )
self.icon = "🔫"
# self.desc = "Puoi sparare a un giocatore che sia distante 2 o meno" # self.desc = "Puoi sparare a un giocatore che sia distante 2 o meno"
# self.desc_eng = "You can shoot another player at distance 2 or less" # self.desc_eng = "You can shoot another player at distance 2 or less"
class Volcanic(Card): class Volcanic(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Volcanic', number, super().__init__(
is_equipment=True, is_weapon=True, range=1) suit, "Volcanic", number, is_equipment=True, is_weapon=True, range=1
self.icon = '🔫' )
self.icon = "🔫"
# self.desc = "Puoi sparare a un giocatore che sia distante 1 o meno, tuttavia puoi giocare quanti bang vuoi" # self.desc = "Puoi sparare a un giocatore che sia distante 1 o meno, tuttavia puoi giocare quanti bang vuoi"
# self.desc_eng = "You can shoot another player at distance 1 or less, however you no longer have the limit of 1 Bang" # self.desc_eng = "You can shoot another player at distance 1 or less, however you no longer have the limit of 1 Bang"
class Winchester(Card): class Winchester(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Winchester', number, super().__init__(
is_equipment=True, is_weapon=True, range=5) suit, "Winchester", number, is_equipment=True, is_weapon=True, range=5
self.icon = '🔫' )
self.icon = "🔫"
# self.desc = "Puoi sparare a un giocatore che sia distante 5 o meno" # self.desc = "Puoi sparare a un giocatore che sia distante 5 o meno"
# self.desc_eng = "You can shoot another player at distance 5 or less" # self.desc_eng = "You can shoot another player at distance 5 or less"
class Bang(Card): class Bang(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Bang!', number) super().__init__(suit, "Bang!", number)
self.icon = '💥' self.icon = "💥"
# self.desc = "Spara a un giocatore a distanza raggiungibile. Se non hai armi la distanza di default è 1" # self.desc = "Spara a un giocatore a distanza raggiungibile. Se non hai armi la distanza di default è 1"
# self.desc_eng = "Shoot a player in sight. If you do not have weapons, your is sight is 1" # self.desc_eng = "Shoot a player in sight. If you do not have weapons, your is sight is 1"
self.need_target = True self.need_target = True
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if player.game.check_event(ceh.Sermone) and not self.number == 42: # 42 gold rush if (
player.game.check_event(ceh.Sermone) and not self.number == 42
): # 42 gold rush
return False return False
if ((player.has_played_bang and not self.number == 42) and (not any((isinstance(c, Volcanic) for c in player.equipment)) or player.game.check_event(ce.Lazo)) and against is not None): # 42 gold rush: if (
(player.has_played_bang and not self.number == 42)
and (
not any((isinstance(c, Volcanic) for c in player.equipment))
or player.game.check_event(ce.Lazo)
)
and against is not None
): # 42 gold rush:
return False return False
elif against is not None: elif against is not None:
import bang.characters as chars import bang.characters as chars
super().play_card(player, against=against) super().play_card(player, against=against)
if not (self.number == 42 and self.suit == Suit.GOLD): # 42 gold rush if not (self.number == 42 and self.suit == Suit.GOLD): # 42 gold rush
player.bang_used += 1 player.bang_used += 1
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1 player.has_played_bang = (
True
if not player.game.check_event(ceh.Sparatoria)
else player.bang_used > 1
)
if player.character.check(player.game, chars.WillyTheKid): if player.character.check(player.game, chars.WillyTheKid):
player.has_played_bang = False player.has_played_bang = False
player.game.attack(player, against, double=player.character.check(player.game, chars.SlabTheKiller), card_name=self.name) player.game.attack(
player,
against,
double=player.character.check(player.game, chars.SlabTheKiller),
card_name=self.name,
)
return True return True
return False return False
class Birra(Card): class Birra(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Birra', number) super().__init__(suit, "Birra", number)
self.icon = '🍺' self.icon = "🍺"
# self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori" # self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori"
# self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players" # self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players"
@ -254,47 +314,68 @@ class Birra(Card):
return False return False
if not skipChecks: if not skipChecks:
import bang.expansions.gold_rush.characters as grch import bang.expansions.gold_rush.characters as grch
madamYto = [p for p in player.game.get_alive_players() if p.character.check(player.game, grch.MadamYto) and self.number != 42]
madamYto = [
p
for p in player.game.get_alive_players()
if p.character.check(player.game, grch.MadamYto) and self.number != 42
]
for p in madamYto: for p in madamYto:
player.game.deck.draw(True, player=p) player.game.deck.draw(True, player=p)
p.notify_self() p.notify_self()
if 'gold_rush' in player.game.expansions and self.number != 42: if "gold_rush" in player.game.expansions and self.number != 42:
from bang.players import PendingAction from bang.players import PendingAction
player.available_cards = [{
'name': 'Pepita', player.available_cards = [
'icon': '💵️', {"name": "Pepita", "icon": "💵️", "alt_text": "1", "noDesc": True},
'alt_text': '1', self,
'noDesc': True ]
}, self] player.choose_text = "choose_birra_function"
player.choose_text = 'choose_birra_function'
player.pending_action = PendingAction.CHOOSE player.pending_action = PendingAction.CHOOSE
player.notify_self() player.notify_self()
return True return True
if (len(player.game.get_alive_players()) != 2 or self.number == 42) and player.lives < player.max_lives: if (
len(player.game.get_alive_players()) != 2 or self.number == 42
) and player.lives < player.max_lives:
super().play_card(player, against=against) super().play_card(player, against=against)
player.lives = min(player.lives+1, player.max_lives) player.lives = min(player.lives + 1, player.max_lives)
import bang.expansions.dodge_city.characters as chd import bang.expansions.dodge_city.characters as chd
if player.character.check(player.game, chd.TequilaJoe): if player.character.check(player.game, chd.TequilaJoe):
player.lives = min(player.lives+1, player.max_lives) player.lives = min(player.lives + 1, player.max_lives)
return True return True
elif len(player.game.get_alive_players()) == 2 or player.lives == player.max_lives: elif (
G.sio.emit('chat_message', room=player.game.name, len(player.game.get_alive_players()) == 2
data=f'_spilled_beer|{player.name}|{self.name}') or player.lives == player.max_lives
):
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_spilled_beer|{player.name}|{self.name}",
)
return True return True
return False return False
class CatBalou(Card): class CatBalou(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Cat Balou', number) super().__init__(suit, "Cat Balou", number)
self.icon = '💃' self.icon = "💃"
# self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco" # self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
# self.desc_eng = "Choose and discard a card from any other player." # self.desc_eng = "Choose and discard a card from any other player."
self.need_target = True self.need_target = True
self.can_target_self = True self.can_target_self = True
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if against is not None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0 and (player.name != against or len(player.equipment) > 0): if (
against is not None
and (
len(player.game.get_player_named(against).hand)
+ len(player.game.get_player_named(against).equipment)
)
> 0
and (player.name != against or len(player.equipment) > 0)
):
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.steal_discard(player, against, self) player.game.steal_discard(player, against, self)
return True return True
@ -303,25 +384,29 @@ class CatBalou(Card):
class Diligenza(Card): class Diligenza(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Diligenza', number) super().__init__(suit, "Diligenza", number)
self.icon = '🚡' self.icon = "🚡"
self.alt_text = "🎴🎴" self.alt_text = "🎴🎴"
# self.desc = "Pesca 2 carte dalla cima del mazzo" # self.desc = "Pesca 2 carte dalla cima del mazzo"
# self.desc_eng = "Draw 2 cards from the deck." # self.desc_eng = "Draw 2 cards from the deck."
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_diligenza|{player.name}|{self.name}') "chat_message",
room=player.game.name,
data=f"_diligenza|{player.name}|{self.name}",
)
for i in range(2): for i in range(2):
player.game.deck.draw(True, player) player.game.deck.draw(True, player)
player.game.deck.flip_wildwestshow()
return True return True
class Duello(Card): class Duello(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Duello', number) super().__init__(suit, "Duello", number)
self.need_target = True self.need_target = True
self.icon = '⚔️' self.icon = "⚔️"
# self.desc = "Gioca questa carta contro un qualsiasi giocatore. A turno, cominciando dal tuo avversario, potete scartare una carta Bang!, il primo giocatore che non lo fa perde 1 vita" # self.desc = "Gioca questa carta contro un qualsiasi giocatore. A turno, cominciando dal tuo avversario, potete scartare una carta Bang!, il primo giocatore che non lo fa perde 1 vita"
# self.desc_eng = "Play this card against any player. In turn, starting with your opponent, you can discard a Bang! Card, the first player who does not do so loses 1 life." # self.desc_eng = "Play this card against any player. In turn, starting with your opponent, you can discard a Bang! Card, the first player who does not do so loses 1 life."
@ -335,8 +420,8 @@ class Duello(Card):
class Emporio(Card): class Emporio(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Emporio', number) super().__init__(suit, "Emporio", number)
self.icon = '🏪' self.icon = "🏪"
# self.desc = "Scopri dal mazzo tante carte quanto il numero di giocatori vivi, a turno, partendo da te, scegliete una carta e aggiungetela alla vostra mano" # self.desc = "Scopri dal mazzo tante carte quanto il numero di giocatori vivi, a turno, partendo da te, scegliete una carta e aggiungetela alla vostra mano"
# self.desc_eng = "Put on the table N cards from the deck, where N is the number of alive players, in turn, starting with you, choose a card and add it to your hand" # self.desc_eng = "Put on the table N cards from the deck, where N is the number of alive players, in turn, starting with you, choose a card and add it to your hand"
@ -348,8 +433,8 @@ class Emporio(Card):
class Gatling(Card): class Gatling(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Gatling', number) super().__init__(suit, "Gatling", number)
self.icon = '🛰' self.icon = "🛰"
# self.desc = "Spara a tutti gli altri giocatori" # self.desc = "Spara a tutti gli altri giocatori"
# self.desc_eng = "Shoot all the other players" # self.desc_eng = "Shoot all the other players"
self.alt_text = "👥💥" self.alt_text = "👥💥"
@ -362,8 +447,8 @@ class Gatling(Card):
class Indiani(Card): class Indiani(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Indiani!', number) super().__init__(suit, "Indiani!", number)
self.icon = '🏹' self.icon = "🏹"
# self.desc = "Tutti gli altri giocatori devono scartare un Bang! o perdere una vita" # self.desc = "Tutti gli altri giocatori devono scartare un Bang! o perdere una vita"
# self.desc_eng = "All the other players must discard a Bang! or lose 1 Health Point" # self.desc_eng = "All the other players must discard a Bang! or lose 1 Health Point"
@ -375,22 +460,35 @@ class Indiani(Card):
class Mancato(Card): class Mancato(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Mancato!', number) super().__init__(suit, "Mancato!", number)
self.icon = '😅' self.icon = "😅"
# self.desc = "Usa questa carta per annullare un bang" # self.desc = "Usa questa carta per annullare un bang"
# self.desc_eng = "Use this card to cancel the effect of a bang" # self.desc_eng = "Use this card to cancel the effect of a bang"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
import bang.characters as chars import bang.characters as chars
if against is not None and player.character.check(player.game, chars.CalamityJanet):
if player.has_played_bang and (not any((isinstance(c, Volcanic) for c in player.equipment)) or player.game.check_event(ce.Lazo)): if against is not None and player.character.check(
player.game, chars.CalamityJanet
):
if player.has_played_bang and (
not any((isinstance(c, Volcanic) for c in player.equipment))
or player.game.check_event(ce.Lazo)
):
return False return False
if player.game.check_event(ceh.Sermone): if player.game.check_event(ceh.Sermone):
return False return False
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_special_calamity|{player.name}|{self.name}|{against}') "chat_message",
room=player.game.name,
data=f"_special_calamity|{player.name}|{self.name}|{against}",
)
player.bang_used += 1 player.bang_used += 1
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1 player.has_played_bang = (
True
if not player.game.check_event(ceh.Sparatoria)
else player.bang_used > 1
)
player.game.attack(player, against, card_name=self.name) player.game.attack(player, against, card_name=self.name)
return True return True
return False return False
@ -398,15 +496,23 @@ class Mancato(Card):
class Panico(Card): class Panico(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Panico!', number, range=1) super().__init__(suit, "Panico!", number, range=1)
self.icon = '😱' self.icon = "😱"
self.need_target = True self.need_target = True
self.can_target_self = True self.can_target_self = True
# self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco" # self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
# self.desc_eng = "Steal a card from a player at distance 1" # self.desc_eng = "Steal a card from a player at distance 1"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if against is not None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0 and (player.name != against or len(player.equipment) > 0): if (
against is not None
and (
len(player.game.get_player_named(against).hand)
+ len(player.game.get_player_named(against).equipment)
)
> 0
and (player.name != against or len(player.equipment) > 0)
):
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.steal_discard(player, against, self) player.game.steal_discard(player, against, self)
return True return True
@ -415,58 +521,66 @@ class Panico(Card):
class Saloon(Card): class Saloon(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Saloon', number) super().__init__(suit, "Saloon", number)
# self.desc = "Tutti i giocatori recuperano un punto vita compreso chi gioca la carta" # self.desc = "Tutti i giocatori recuperano un punto vita compreso chi gioca la carta"
# self.desc_eng = "Everyone heals 1 Health point" # self.desc_eng = "Everyone heals 1 Health point"
self.icon = '🍻' self.icon = "🍻"
self.alt_text = "👥🍺" self.alt_text = "👥🍺"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_saloon|{player.name}|{self.name}') "chat_message",
room=player.game.name,
data=f"_saloon|{player.name}|{self.name}",
)
for p in player.game.get_alive_players(): for p in player.game.get_alive_players():
p.lives = min(p.lives+1, p.max_lives) p.lives = min(p.lives + 1, p.max_lives)
p.notify_self() p.notify_self()
return True return True
class WellsFargo(Card): class WellsFargo(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'WellsFargo', number) super().__init__(suit, "WellsFargo", number)
# self.desc = "Pesca 3 carte dalla cima del mazzo" # self.desc = "Pesca 3 carte dalla cima del mazzo"
# self.desc_eng = "Draw 3 cards from the deck" # self.desc_eng = "Draw 3 cards from the deck"
self.icon = '💸' self.icon = "💸"
self.alt_text = "🎴🎴🎴" self.alt_text = "🎴🎴🎴"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
G.sio.emit('chat_message', room=player.game.name, G.sio.emit(
data=f'_wellsfargo|{player.name}|{self.name}') "chat_message",
for i in range(3): room=player.game.name,
data=f"_wellsfargo|{player.name}|{self.name}",
)
for _ in range(3):
player.game.deck.draw(True, player) player.game.deck.draw(True, player)
player.game.deck.flip_wildwestshow()
return True return True
def get_starting_deck(expansions:List[str]) -> List[Card]: def get_starting_deck(expansions: List[str]) -> List[Card]:
from bang.expansions import DodgeCity, TheValleyOfShadows from bang.expansions import DodgeCity, TheValleyOfShadows
base_cards = [ base_cards = [
Barile(Suit.SPADES, 'Q'), Barile(Suit.SPADES, "Q"),
Barile(Suit.SPADES, 'K'), Barile(Suit.SPADES, "K"),
Dinamite(Suit.HEARTS, 2), Dinamite(Suit.HEARTS, 2),
Mirino(Suit.SPADES, 'A'), Mirino(Suit.SPADES, "A"),
Mustang(Suit.HEARTS, 8), Mustang(Suit.HEARTS, 8),
Mustang(Suit.HEARTS, 9), Mustang(Suit.HEARTS, 9),
Prigione(Suit.SPADES, 'J'), Prigione(Suit.SPADES, "J"),
Prigione(Suit.HEARTS, 4), Prigione(Suit.HEARTS, 4),
Prigione(Suit.SPADES, 10), Prigione(Suit.SPADES, 10),
Remington(Suit.CLUBS, 'K'), Remington(Suit.CLUBS, "K"),
RevCarabine(Suit.CLUBS, 'A'), RevCarabine(Suit.CLUBS, "A"),
Schofield(Suit.CLUBS, 'J'), Schofield(Suit.CLUBS, "J"),
Schofield(Suit.CLUBS, 'Q'), Schofield(Suit.CLUBS, "Q"),
Schofield(Suit.SPADES, 'K'), Schofield(Suit.SPADES, "K"),
Volcanic(Suit.SPADES, 10), Volcanic(Suit.SPADES, 10),
Volcanic(Suit.CLUBS, 10), Volcanic(Suit.CLUBS, 10),
Winchester(Suit.SPADES, 8), Winchester(Suit.SPADES, 8),
Bang(Suit.SPADES, 'A'), Bang(Suit.SPADES, "A"),
Bang(Suit.DIAMONDS, 2), Bang(Suit.DIAMONDS, 2),
Bang(Suit.DIAMONDS, 3), Bang(Suit.DIAMONDS, 3),
Bang(Suit.DIAMONDS, 4), Bang(Suit.DIAMONDS, 4),
@ -476,10 +590,10 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
Bang(Suit.DIAMONDS, 8), Bang(Suit.DIAMONDS, 8),
Bang(Suit.DIAMONDS, 9), Bang(Suit.DIAMONDS, 9),
Bang(Suit.DIAMONDS, 10), Bang(Suit.DIAMONDS, 10),
Bang(Suit.DIAMONDS, 'J'), Bang(Suit.DIAMONDS, "J"),
Bang(Suit.DIAMONDS, 'Q'), Bang(Suit.DIAMONDS, "Q"),
Bang(Suit.DIAMONDS, 'K'), Bang(Suit.DIAMONDS, "K"),
Bang(Suit.DIAMONDS, 'A'), Bang(Suit.DIAMONDS, "A"),
Bang(Suit.CLUBS, 2), Bang(Suit.CLUBS, 2),
Bang(Suit.CLUBS, 3), Bang(Suit.CLUBS, 3),
Bang(Suit.CLUBS, 4), Bang(Suit.CLUBS, 4),
@ -488,34 +602,34 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
Bang(Suit.CLUBS, 7), Bang(Suit.CLUBS, 7),
Bang(Suit.CLUBS, 8), Bang(Suit.CLUBS, 8),
Bang(Suit.CLUBS, 9), Bang(Suit.CLUBS, 9),
Bang(Suit.HEARTS, 'Q'), Bang(Suit.HEARTS, "Q"),
Bang(Suit.HEARTS, 'K'), Bang(Suit.HEARTS, "K"),
Bang(Suit.HEARTS, 'A'), Bang(Suit.HEARTS, "A"),
Birra(Suit.HEARTS, 6), Birra(Suit.HEARTS, 6),
Birra(Suit.HEARTS, 7), Birra(Suit.HEARTS, 7),
Birra(Suit.HEARTS, 8), Birra(Suit.HEARTS, 8),
Birra(Suit.HEARTS, 9), Birra(Suit.HEARTS, 9),
Birra(Suit.HEARTS, 10), Birra(Suit.HEARTS, 10),
Birra(Suit.HEARTS, 'J'), Birra(Suit.HEARTS, "J"),
CatBalou(Suit.HEARTS, 'K'), CatBalou(Suit.HEARTS, "K"),
CatBalou(Suit.DIAMONDS, 9), CatBalou(Suit.DIAMONDS, 9),
CatBalou(Suit.DIAMONDS, 10), CatBalou(Suit.DIAMONDS, 10),
CatBalou(Suit.DIAMONDS, 'J'), CatBalou(Suit.DIAMONDS, "J"),
Diligenza(Suit.SPADES, 9), Diligenza(Suit.SPADES, 9),
Diligenza(Suit.SPADES, 9), Diligenza(Suit.SPADES, 9),
Duello(Suit.DIAMONDS, 'Q'), Duello(Suit.DIAMONDS, "Q"),
Duello(Suit.SPADES, 'J'), Duello(Suit.SPADES, "J"),
Duello(Suit.CLUBS, 8), Duello(Suit.CLUBS, 8),
Emporio(Suit.CLUBS, 9), Emporio(Suit.CLUBS, 9),
Emporio(Suit.SPADES, 'Q'), Emporio(Suit.SPADES, "Q"),
Gatling(Suit.HEARTS, 10), Gatling(Suit.HEARTS, 10),
Indiani(Suit.DIAMONDS, 'K'), Indiani(Suit.DIAMONDS, "K"),
Indiani(Suit.DIAMONDS, 'A'), Indiani(Suit.DIAMONDS, "A"),
Mancato(Suit.CLUBS, 10), Mancato(Suit.CLUBS, 10),
Mancato(Suit.CLUBS, 'J'), Mancato(Suit.CLUBS, "J"),
Mancato(Suit.CLUBS, 'Q'), Mancato(Suit.CLUBS, "Q"),
Mancato(Suit.CLUBS, 'K'), Mancato(Suit.CLUBS, "K"),
Mancato(Suit.CLUBS, 'A'), Mancato(Suit.CLUBS, "A"),
Mancato(Suit.SPADES, 2), Mancato(Suit.SPADES, 2),
Mancato(Suit.SPADES, 3), Mancato(Suit.SPADES, 3),
Mancato(Suit.SPADES, 4), Mancato(Suit.SPADES, 4),
@ -523,16 +637,15 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
Mancato(Suit.SPADES, 6), Mancato(Suit.SPADES, 6),
Mancato(Suit.SPADES, 7), Mancato(Suit.SPADES, 7),
Mancato(Suit.SPADES, 8), Mancato(Suit.SPADES, 8),
Panico(Suit.HEARTS, 'J'), Panico(Suit.HEARTS, "J"),
Panico(Suit.HEARTS, 'Q'), Panico(Suit.HEARTS, "Q"),
Panico(Suit.HEARTS, 'A'), Panico(Suit.HEARTS, "A"),
Panico(Suit.DIAMONDS, 8), Panico(Suit.DIAMONDS, 8),
Saloon(Suit.HEARTS, 5), Saloon(Suit.HEARTS, 5),
WellsFargo(Suit.HEARTS, 3), WellsFargo(Suit.HEARTS, 3),
] ]
if 'dodge_city' in expansions: if "dodge_city" in expansions:
base_cards.extend(DodgeCity.get_cards()) base_cards.extend(DodgeCity.get_cards())
if 'the_valley_of_shadows' in expansions: if "the_valley_of_shadows" in expansions:
base_cards.extend(TheValleyOfShadows.get_cards()) base_cards.extend(TheValleyOfShadows.get_cards())
return base_cards return base_cards

View File

@ -10,8 +10,12 @@ from globals import G
if TYPE_CHECKING: if TYPE_CHECKING:
from bang.game import Game from bang.game import Game
class Deck: class Deck:
def __init__(self, game: 'Game'): """Class that handles all deck dealing information"""
def __init__(self, game: "Game"):
super().__init__() super().__init__()
self.cards: List[cs.Card] = cs.get_starting_deck(game.expansions) self.cards: List[cs.Card] = cs.get_starting_deck(game.expansions)
self.mancato_cards: List[str] = [] self.mancato_cards: List[str] = []
@ -30,45 +34,74 @@ class Deck:
self.all_cards_str.append(c.name) self.all_cards_str.append(c.name)
self.game = game self.game = game
self.event_cards: List[ce.CardEvent] = [] self.event_cards: List[ce.CardEvent] = []
self.event_cards_wildwestshow: List[ce.CardEvent] = []
endgame_cards: List[ce.CardEvent] = [] endgame_cards: List[ce.CardEvent] = []
if 'fistful_of_cards' in game.expansions: if "fistful_of_cards" in game.expansions:
self.event_cards.extend(ce.get_all_events(game.rng)) self.event_cards.extend(ce.get_all_events(game.rng))
endgame_cards.append(ce.get_endgame_card()) endgame_cards.append(ce.get_endgame_card())
if 'high_noon' in game.expansions: if "high_noon" in game.expansions:
self.event_cards.extend(ceh.get_all_events(game.rng)) self.event_cards.extend(ceh.get_all_events(game.rng))
endgame_cards.append(ceh.get_endgame_card()) endgame_cards.append(ceh.get_endgame_card())
if 'wild_west_show' in game.expansions: if "wild_west_show" in game.expansions:
self.event_cards.extend(cew.get_all_events(game.rng)) self.event_cards_wildwestshow.extend(cew.get_all_events(game.rng))
endgame_cards.append(cew.get_endgame_card()) game.rng.shuffle(self.event_cards_wildwestshow)
self.event_cards_wildwestshow.insert(0, None)
self.event_cards_wildwestshow.append(cew.get_endgame_card())
if len(self.event_cards) > 0: if len(self.event_cards) > 0:
game.rng.shuffle(self.event_cards) game.rng.shuffle(self.event_cards)
self.event_cards = self.event_cards[:12] self.event_cards = self.event_cards[:12]
self.event_cards.insert(0, None) self.event_cards.insert(0, None)
self.event_cards.insert(0, None) # 2 perchè iniziale, e primo flip dallo sceriffo self.event_cards.insert(
0, None
) # 2 perchè iniziale, e primo flip dallo sceriffo
self.event_cards.append(game.rng.choice(endgame_cards)) self.event_cards.append(game.rng.choice(endgame_cards))
game.rng.shuffle(self.cards) game.rng.shuffle(self.cards)
self.shop_deck: List[grc.ShopCard] = [] self.shop_deck: List[grc.ShopCard] = []
self.shop_cards: List[grc.ShopCard] = [] self.shop_cards: List[grc.ShopCard] = []
if 'gold_rush' in game.expansions: if "gold_rush" in game.expansions:
self.shop_cards = [None, None, None] self.shop_cards = [None, None, None]
self.shop_deck = grc.get_cards() self.shop_deck = grc.get_cards()
game.rng.shuffle(self.shop_deck) game.rng.shuffle(self.shop_deck)
self.fill_gold_rush_shop() self.fill_gold_rush_shop()
self.scrap_pile: List[cs.Card] = [] self.scrap_pile: List[cs.Card] = []
print(f'Deck initialized with {len(self.cards)} cards') print(f"Deck initialized with {len(self.cards)} cards")
def flip_event(self): def flip_event(self):
if len(self.event_cards) > 0 and not (isinstance(self.event_cards[0], ce.PerUnPugnoDiCarte) or isinstance(self.event_cards[0], ceh.MezzogiornoDiFuoco) or isinstance(self.event_cards[0], cew.WildWestShow)): """Flip event for regular Sheriff turn (High Noon, Fistful of Cards)"""
if len(self.event_cards) > 0 and not (
isinstance(self.event_cards[0], ce.PerUnPugnoDiCarte)
or isinstance(self.event_cards[0], ceh.MezzogiornoDiFuoco)
):
self.event_cards.append(self.event_cards.pop(0)) self.event_cards.append(self.event_cards.pop(0))
if len(self.event_cards) > 0 and self.event_cards[0] is not None:
self.event_cards[0].on_flipped(self.game)
self.game.notify_event_card() self.game.notify_event_card()
self.game.notify_all() self.game.notify_all()
def flip_wildwestshow(self):
"""Flip event for Wild West Show only"""
if len(self.event_cards_wildwestshow) > 0 and not isinstance(
self.event_cards_wildwestshow[0], cew.WildWestShow
):
self.event_cards_wildwestshow.append(self.event_cards_wildwestshow.pop(0))
if (
len(self.event_cards_wildwestshow) > 0
and self.event_cards_wildwestshow[0] is not None
):
self.event_cards_wildwestshow[0].on_flipped(self.game)
self.game.notify_event_card_wildwestshow()
self.game.notify_all()
def fill_gold_rush_shop(self): def fill_gold_rush_shop(self):
"""
As gold_rush shop cards are stored in a fixed 3 space array,
this function replaces the None values with new cards.
"""
if not any((c is None for c in self.shop_cards)): if not any((c is None for c in self.shop_cards)):
return return
for i in range(3): for i in range(3):
if self.shop_cards[i] is None: if self.shop_cards[i] is None:
print(f'replacing gr-card {i}') print(f"replacing gr-card {i}")
self.shop_cards[i] = self.shop_deck.pop(0) self.shop_cards[i] = self.shop_deck.pop(0)
self.shop_cards[i].reset_card() self.shop_cards[i].reset_card()
self.game.notify_gold_rush_shop() self.game.notify_gold_rush_shop()
@ -102,14 +135,22 @@ class Deck:
def put_on_top(self, card: cs.Card): def put_on_top(self, card: cs.Card):
self.cards.insert(0, card) self.cards.insert(0, card)
def draw(self, ignore_event = False, player=None) -> cs.Card: def draw(self, ignore_event=False, player=None) -> cs.Card:
if self.game.check_event(ce.MinieraAbbandonata) and len(self.scrap_pile) > 0 and not ignore_event: if (
self.game.check_event(ce.MinieraAbbandonata)
and len(self.scrap_pile) > 0
and not ignore_event
):
return self.draw_from_scrap_pile() return self.draw_from_scrap_pile()
card = self.cards.pop(0) card = self.cards.pop(0)
if len(self.cards) == 0: if len(self.cards) == 0:
self.reshuffle() self.reshuffle()
if player is not None and self.game.replay_speed > 0: if player is not None and self.game.replay_speed > 0:
G.sio.emit('card_drawn', room=self.game.name, data={'player': player.name, 'pile': 'deck'}) G.sio.emit(
"card_drawn",
room=self.game.name,
data={"player": player.name, "pile": "deck"},
)
player.hand.append(card) player.hand.append(card)
return card return card
@ -127,15 +168,24 @@ class Deck:
else: else:
return self.draw() return self.draw()
def scrap(self, card: cs.Card, ignore_event = False, player=None): def scrap(self, card: cs.Card, ignore_event=False, player=None):
if card.number == 42: return if card.number == 42:
return
card.reset_card() card.reset_card()
if self.game.check_event(ce.MinieraAbbandonata) and not ignore_event: if self.game.check_event(ce.MinieraAbbandonata) and not ignore_event:
self.put_on_top(card) self.put_on_top(card)
else: else:
self.scrap_pile.append(card) self.scrap_pile.append(card)
if player is not None and self.game.replay_speed > 0: if player is not None and self.game.replay_speed > 0:
G.sio.emit('card_scrapped', room=self.game.name, data={'player': player.name, 'card':card.__dict__, 'pile': 'scrap'}) G.sio.emit(
"card_scrapped",
room=self.game.name,
data={
"player": player.name,
"card": card.__dict__,
"pile": "scrap",
},
)
G.sio.sleep(0.6) G.sio.sleep(0.6)
self.game.notify_scrap_pile() self.game.notify_scrap_pile()
else: else:

View File

@ -1,106 +1,198 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import random import random
import bang.players as players
import bang.roles as r
import bang.cards as cs
from globals import G
class CardEvent(ABC): class CardEvent(ABC):
"""Base class for all event cards"""
def __init__(self, name, icon): def __init__(self, name, icon):
self.name = name self.name = name
self.icon = icon self.icon = icon
def on_flipped(self, game):
"""Default on flipped event
Args:
game (Game): the game object
"""
print(f"{game.name}: flip new event {self.name}")
G.sio.emit(
"chat_message",
room=game.name,
data={
"color": "orange",
"text": f"_flip_event|{self.name}",
},
)
return
def on_clicked(self, game, player):
"""Default on clicked event
Args:
game (Game): the game object
player (Player): the player that clicked the card
"""
print(f"{game.name}: {player.name} clicked event {self.name}")
return
class Agguato(CardEvent): class Agguato(CardEvent):
"""La distanza base tra 2 qualsiasi giocatori è 1
The distance between 2 players is always 1"""
def __init__(self): def __init__(self):
super().__init__("Agguato", "🛁") super().__init__("Agguato", "🛁")
#self.desc = "La distanza base tra 2 qualsiasi giocatori è 1"
#self.desc_eng = "The base distance from any 2 players is 1"
class Cecchino(CardEvent): class Cecchino(CardEvent):
"""Nel proprio turno i giocatori possono
scartare 2 Bang assieme per sparare un bang che necessita 2 mancato (clicca la carta)
In your turn you can discard 2 Bang together to shoot a bang that needs 2 miss (click the card)
"""
def __init__(self): def __init__(self):
super().__init__("Cecchino", "👁") super().__init__("Cecchino", "👁")
#self.desc = "Nel proprio turno i giocatori possono scartare 2 Bang assieme per sparare un bang che necessita 2 mancato (clicca la carta)"
#self.desc_eng = "During their turn, players can discard 2 Bang! to shoot a bang that requires 2 missed (click the card)"
class DeadMan(CardEvent): class DeadMan(CardEvent):
"""Al proprio turno il giocatore che è morto per primo torna in vita con 2 vite e 2 carte
The first player that died returns back to life with 2 hp and 2 cards"""
def __init__(self): def __init__(self):
super().__init__("Dead Man", "⚰️") super().__init__("Dead Man", "⚰️")
#self.desc = "Al proprio turno il giocatore che è morto per primo torna in vita con 2 vite e 2 carte"
#self.desc_eng = "The first player that died returns back to life with 2 hp and 2 cards" def on_flipped(self, game):
game.did_resuscitate_deadman = False
return super().on_flipped(game)
class FratelliDiSangue(CardEvent): class FratelliDiSangue(CardEvent):
"""All'inizio del proprio turno, i giocatori possono perdere 1 vita (tranne l'ultimo) per darla a un altro giocatore"""
def __init__(self): def __init__(self):
super().__init__("Fratelli Di Sangue", "💉") super().__init__("Fratelli Di Sangue", "💉")
#self.desc = "All'inizio del proprio turno, i giocatori possono perdere 1 vita (tranne l'ultimo) per darla a un altro giocatore"
#self.desc_eng = "At the begin of their turn, payers can lose 1 hp (except the last one) to give it to another player"
class IlGiudice(CardEvent): class IlGiudice(CardEvent):
"""Non si possono equipaggiare carte a se stessi o agli altri"""
def __init__(self): def __init__(self):
super().__init__("Il Giudice", "👨‍⚖️") super().__init__("Il Giudice", "👨‍⚖️")
#self.desc = "Non si possono equipaggiare carte a se stessi o agli altri"
#self.desc_eng = "You can't equip cards on your or other players"
class Lazo(CardEvent): class Lazo(CardEvent):
"""Le carte equipaggiate non hanno effetto"""
def __init__(self): def __init__(self):
super().__init__("Lazo", "📿") super().__init__("Lazo", "📿")
#self.desc = "Le carte equipaggiate non hanno effetto"
#self.desc_eng = "Cards in the equipment slot do not work"
class LeggeDelWest(CardEvent): class LeggeDelWest(CardEvent):
"""I giocatori mostrano la seconda carta che pescano e sono obbligati a usarla in quel turno (se possibile)"""
def __init__(self): def __init__(self):
super().__init__("Legge Del West", "⚖️") super().__init__("Legge Del West", "⚖️")
#self.desc = "I giocatori mostrano la seconda carta che pescano e sono obbligati a usarla in quel turno (se possibile)"
#self.desc_eng = "Every player shows the second card that they draw and must use it in that round (if it is possible)"
class LiquoreForte(CardEvent): class LiquoreForte(CardEvent):
"""I giocatori possono evitare di pescare per recuperare 1 vita (clicca sulla carta evento per farlo)"""
def __init__(self): def __init__(self):
super().__init__("Liquore Forte", "🥃") super().__init__("Liquore Forte", "🥃")
#self.desc = "I giocatori possono evitare di pescare per recuperare 1 vita (clicca sulla carta evento per farlo)"
#self.desc_eng = "Players can skip drawing to regain 1 HP (click on the event card to use)"
class MinieraAbbandonata(CardEvent): class MinieraAbbandonata(CardEvent):
"""I giocatori pescano dagli scarti nella loro fase 1 e scartano in cima al mazzo nella loro fase 3 (se gli scarti finiscono, è necessario pescare e scartare in cima al mazzo)"""
def __init__(self): def __init__(self):
super().__init__("Miniera Abbandonata", "") super().__init__("Miniera Abbandonata", "")
#self.desc = "I giocatori pescano dagli scarti nella loro fase 1 e scartano in cima al mazzo nella loro fase 3 (se gli scarti finiscono, è necessario pescare e scartare in cima al mazzo)"
#self.desc_eng = "Players draw from the discarded pile in their phase 1 and discard to the top of the deck during phase 3 (if the discaded pile runs out, they must draw and discard on top of the deck)"
class PerUnPugnoDiCarte(CardEvent): class PerUnPugnoDiCarte(CardEvent):
"""All'inizio del proprio turno, il giocatore subisce tanti bang quante carte ha in mano"""
def __init__(self): def __init__(self):
super().__init__("Per Un Pugno Di Carte", "🎴") super().__init__("Per Un Pugno Di Carte", "🎴")
#self.desc = "All'inizio del proprio turno, il giocatore subisce tanti bang quante carte ha in mano"
#self.desc_eng = "On the beginning of his turn, the player is target of as many Bang as how many cards he has in his hand"
class Peyote(CardEvent): class Peyote(CardEvent):
"""Invece che pescare il giocatore prova a indovinare il colore del seme, se lo indovina aggiunge la carta alla mano e continua provando ad indovinare la carta successiva"""
def __init__(self): def __init__(self):
super().__init__("Peyote", "🌵") super().__init__("Peyote", "🌵")
#self.desc = "Invece che pescare il giocatore prova a indovinare il colore del seme, se lo indovina aggiunge la carta alla mano e continua provando ad indovinare la carta successiva"
#self.desc_eng = "Instead of drawing, the player tries to guess the color of the suit, if he's right he adds the card to the hand and continues trying to guess the next card"
class Ranch(CardEvent): class Ranch(CardEvent):
"""Dopo aver pescato il giocatore può scartare quante carte vuole dalla mano e pescarne altrettante dal mazzo"""
def __init__(self): def __init__(self):
super().__init__("Ranch", "🐮") super().__init__("Ranch", "🐮")
#self.desc = "Dopo aver pescato il giocatore può scartare quante carte vuole dalla mano e pescarne altrettante dal mazzo"
#self.desc_eng = "After drawing, the player can discard as many cards as he wants from his hand and draw as many from the deck"
class Rimbalzo(CardEvent): class Rimbalzo(CardEvent):
"""Il giocatore di turno può giocare bang contro le carte equipaggiate dagli altri giocatori, se non giocano mancato vengono scartate (clicca la carta evento)"""
def __init__(self): def __init__(self):
super().__init__("Rimbalzo", "") super().__init__("Rimbalzo", "")
#self.desc = "Il giocatore di turno può giocare bang contro le carte equipaggiate dagli altri giocatori, se non giocano mancato vengono scartate (clicca la carta evento)"
#self.desc_eng = "The player can play bang against the cards equipped by the other players, if they do not play miss they are discarded (click the event card)" def on_clicked(self, game, player):
super().on_clicked(game, player)
if any((c.name == cs.Bang(0, 0).name for c in player.hand)):
player.available_cards = [
{
"name": p.name,
"icon": p.role.icon
if (game.initial_players == 3)
else "⭐️"
if isinstance(p.role, r.Sheriff)
else "🤠",
"is_character": True,
"avatar": p.avatar,
"is_player": True,
}
for p in game.get_alive_players()
if len(p.equipment) > 0 and p != player
]
player.available_cards.append({"icon": "", "noDesc": True})
player.choose_text = "choose_rimbalzo_player"
player.pending_action = players.PendingAction.CHOOSE
player.using_rimbalzo = 1
player.notify_self()
class RouletteRussa(CardEvent): class RouletteRussa(CardEvent):
"""A partire dallo sceriffo, ogni giocatore scarta 1 mancato, il primo che non lo fa perde 2 vite"""
def __init__(self): def __init__(self):
super().__init__("Roulette Russa", "🇷🇺") super().__init__("Roulette Russa", "🇷🇺")
#self.desc = "A partire dallo sceriffo, ogni giocatore scarta 1 mancato, il primo che non lo fa perde 2 vite" # self.desc_eng = "Starting from the sheriff, every player discards 1 missed, the first one that doesn't loses 2 HP"
#self.desc_eng = "Starting from the sheriff, every player discards 1 missed, the first one that doesn't loses 2 HP"
class Vendetta(CardEvent): class Vendetta(CardEvent):
"""Alla fine del proprio turno il giocatore estrae dal mazzo, se esce ♥️ gioca un altro turno (ma non estrae di nuovo)"""
def __init__(self): def __init__(self):
super().__init__("Vendetta", "😤") super().__init__("Vendetta", "😤")
#self.desc = "Alla fine del proprio turno il giocatore estrae dal mazzo, se esce ♥️ gioca un altro turno (ma non estrae di nuovo)" # self.desc_eng = "When ending the turn, the player flips a card from the deck, if it's ♥️ he plays another turn (but he does not flip another card)"
#self.desc_eng = "When ending the turn, the player flips a card from the deck, if it's ♥️ he plays another turn (but he does not flip another card)"
def get_endgame_card(): def get_endgame_card():
end_game = PerUnPugnoDiCarte() end_game = PerUnPugnoDiCarte()
end_game.expansion = 'fistful-of-cards' end_game.expansion = ( # pylint: disable=attribute-defined-outside-init
"fistful-of-cards"
)
return end_game return end_game
def get_all_events(rng=random): def get_all_events(rng=random):
cards = [ cards = [
Agguato(), Agguato(),
@ -119,6 +211,8 @@ def get_all_events(rng=random):
Vendetta(), Vendetta(),
] ]
rng.shuffle(cards) rng.shuffle(cards)
for c in cards: for card in cards:
c.expansion = 'fistful-of-cards' card.expansion = ( # pylint: disable=attribute-defined-outside-init
return cards "fistful-of-cards"
)
return cards

View File

@ -1,119 +1,184 @@
import random import random
from globals import G
from bang.expansions.fistful_of_cards.card_events import CardEvent from bang.expansions.fistful_of_cards.card_events import CardEvent
class Benedizione(CardEvent): class Benedizione(CardEvent):
"""Tutte le carte sono considerate di cuori ♥️"""
def __init__(self): def __init__(self):
super().__init__("Benedizione", "🙏") super().__init__("Benedizione", "🙏")
#self.desc = "Tutte le carte sono considerate di cuori ♥️" # self.desc_eng = "All cards are of hearts ♥️"
#self.desc_eng = "All cards are of hearts ♥️"
class Maledizione(CardEvent): class Maledizione(CardEvent):
"""Tutte le carte sono considerate di picche ♠"""
def __init__(self): def __init__(self):
super().__init__("Maledizione", "🤬") super().__init__("Maledizione", "🤬")
#self.desc = "Tutte le carte sono considerate di picche ♠" # self.desc_eng = "All cards are of spades ♠"
#self.desc_eng = "All cards are of spades ♠"
class Sbornia(CardEvent): class Sbornia(CardEvent):
"""I personaggi perdono le loro abilità speciali"""
def __init__(self): def __init__(self):
super().__init__("Sbornia", "🥴") super().__init__("Sbornia", "🥴")
#self.desc = "I personaggi perdono le loro abilità speciali" # self.desc_eng = "The characters lose their special abilities"
#self.desc_eng = "The characters lose their special abilities"
class Sete(CardEvent): class Sete(CardEvent):
"""I giocatori pescano 1 carta in meno nella loro fase 1"""
def __init__(self): def __init__(self):
super().__init__("Sete", "🥵") super().__init__("Sete", "🥵")
#self.desc = "I giocatori pescano 1 carta in meno nella loro fase 1" # self.desc_eng = "Players only draw 1 card at the start of their turn"
#self.desc_eng = "Players only draw 1 card at the start of their turn"
class IlTreno(CardEvent): class IlTreno(CardEvent):
"""I giocatori pescano 1 carta extra nella loro fase 1"""
def __init__(self): def __init__(self):
super().__init__("Il Treno", "🚂") super().__init__("Il Treno", "🚂")
#self.desc = "I giocatori pescano 1 carta extra nella loro fase 1" # self.desc_eng = "Players draw 1 extra card"
#self.desc_eng = "Players draw 1 extra card"
class IlReverendo(CardEvent): class IlReverendo(CardEvent):
"""Non si possono giocare le carte Birra"""
def __init__(self): def __init__(self):
super().__init__("Il Reverendo", "⛪️") super().__init__("Il Reverendo", "⛪️")
#self.desc = "Non si possono giocare le carte Birra" # self.desc_eng = "Beers can't be played"
#self.desc_eng = "Beers can't be played"
class IlDottore(CardEvent): class IlDottore(CardEvent):
"""Il/i giocatore/i con meno vite ne recupera/no una"""
def __init__(self): def __init__(self):
super().__init__("Il Dottore", "👨‍⚕️") super().__init__("Il Dottore", "👨‍⚕️")
#self.desc = "Il/i giocatore/i con meno vite ne recupera/no una" # self.desc_eng = "The player with the least amount of HP gets healed 1"
#self.desc_eng = "The player with the least amount of HP gets healed 1"
def on_flipped(self, game):
super().on_flipped(game)
most_hurt = [
p.lives for p in game.players if p.lives > 0 and p.max_lives > p.lives
]
if len(most_hurt) > 0:
hurt_players = [p for p in game.players if p.lives == min(most_hurt)]
for p in hurt_players:
if p.lives != p.max_lives:
p.lives += 1
G.sio.emit(
"chat_message",
room=game.name,
data=f"_doctor_heal|{p.name}",
)
p.notify_self()
return
class Sermone(CardEvent): class Sermone(CardEvent):
"""I giocatori non possono giocare Bang! durante il loro turno"""
def __init__(self): def __init__(self):
super().__init__("Sermone", "✝️") super().__init__("Sermone", "✝️")
#self.desc = "I giocatori non possono giocare Bang! durante il loro turno" # self.desc_eng = "Players can't play Bang! during their turn"
#self.desc_eng = "Players can't play Bang! during their turn"
class Sparatoria(CardEvent): class Sparatoria(CardEvent):
"""Il limite di Bang! per turno è 2 invece che 1"""
def __init__(self): def __init__(self):
super().__init__("Sparatoria", "🔫🔫") super().__init__("Sparatoria", "🔫🔫")
#self.desc = "Il limite di Bang! per turno è 2 invece che 1" # self.desc_eng = "The turn Bang! limit is 2"
#self.desc_eng = "The turn Bang! limit is 2"
class CorsaAllOro(CardEvent): class CorsaAllOro(CardEvent):
"""Si gioca per un intero giro in senso antiorario, tuttavia gli effetti delle carte rimangono invariati"""
def __init__(self): def __init__(self):
super().__init__("Corsa All Oro", "🌟") super().__init__("Corsa All Oro", "🌟")
#self.desc = "Si gioca per un intero giro in senso antiorario, tuttavia gli effetti delle carte rimangono invariati" # self.desc_eng = "Turns are played counter clockwise"
#self.desc_eng = "Turns are played counter clockwise"
class IDalton(CardEvent): class IDalton(CardEvent):
"""Chi ha carte blu in gioco ne scarta 1 a sua scelta"""
def __init__(self): def __init__(self):
super().__init__("I Dalton", "🙇‍♂️") super().__init__("I Dalton", "🙇‍♂️")
#self.desc = "Chi ha carte blu in gioco ne scarta 1 a sua scelta" # self.desc_eng = "Players that have blue cards equipped, discard 1 of those card of their choice"
#self.desc_eng = "Players that have blue cards equipped, discard 1 of those card of their choice"
def on_flipped(self, game):
game.waiting_for = 0
game.ready_count = 0
game.dalton_on = True
for p in game.players:
if p.get_dalton():
game.waiting_for += 1
p.notify_self()
if game.waiting_for != 0:
return
game.dalton_on = False
return super().on_flipped(game)
class Manette(CardEvent): class Manette(CardEvent):
"""Dopo aver pescato in fase 1, il giocatore di turno dichiara un seme: potrà usare solamente carte di quel seme nel suo turno"""
def __init__(self): def __init__(self):
super().__init__("Manette", "🔗") super().__init__("Manette", "🔗")
#self.desc = "Dopo aver pescato in fase 1, il giocatore di turno dichiara un seme: potrà usare solamente carte di quel seme nel suo turno" # self.desc_eng = "After drawing in phase 1, the player declares a suit. He will be able to use only cards of that suit for that turn"
#self.desc_eng = "After drawing in phase 1, the player declares a suit. He will be able to use only cards of that suit for that turn"
class NuovaIdentita(CardEvent): class NuovaIdentita(CardEvent):
"""All'inizio del proprio turno, ogni giocatore potrà decidere se sostituire il suo personaggio attuale con quello era stato proposto ad inizio partita, se lo fa riparte con 2 punti vita"""
def __init__(self): def __init__(self):
super().__init__("Nuova Identita", "🕶") super().__init__("Nuova Identita", "🕶")
#self.desc = "All'inizio del proprio turno, ogni giocatore potrà decidere se sostituire il suo personaggio attuale con quello era stato proposto ad inizio partita, se lo fa riparte con 2 punti vita" # self.desc_eng = "At the beginning of their turn, each player can choose to change its character with the other shown at the game start. If he does so he restarts from 2 HP."
#self.desc_eng = "At the beginning of their turn, each player can choose to change its character with the other shown at the game start. If he does so he restarts from 2 HP."
class CittaFantasma(CardEvent): class CittaFantasma(CardEvent):
"""Tutti i giocatori morti tornano in vita al proprio turno, non possono morire e pescano 3 carte invece che 2. Quando terminano il turno tornano morti."""
def __init__(self): def __init__(self):
super().__init__("Città Fantasma", "👻") super().__init__("Città Fantasma", "👻")
#self.desc = "Tutti i giocatori morti tornano in vita al proprio turno, non possono morire e pescano 3 carte invece che 2. Quando terminano il turno tornano morti." # self.desc_eng = "All dead players come back to life in their turn, they can't die and draw 3 cards instead of 2. When they end their turn the die."
#self.desc_eng = "All dead players come back to life in their turn, they can't die and draw 3 cards instead of 2. When they end their turn the die."
class MezzogiornoDiFuoco(CardEvent): class MezzogiornoDiFuoco(CardEvent):
"""Ogni giocatore perde 1 punto vita all'inizio del turno"""
def __init__(self): def __init__(self):
super().__init__("Mezzogiorno di Fuoco", "🔥") super().__init__("Mezzogiorno di Fuoco", "🔥")
#self.desc = "Ogni giocatore perde 1 punto vita all'inizio del turno" # self.desc_eng = "Every player loses 1 HP when their turn starts"
#self.desc_eng = "Every player loses 1 HP when their turn starts"
def get_endgame_card(): def get_endgame_card():
end_game = MezzogiornoDiFuoco() end_game = MezzogiornoDiFuoco()
end_game.expansion = 'high-noon' end_game.expansion = "high-noon" # pylint: disable=attribute-defined-outside-init
return end_game return end_game
def get_all_events(rng=random): def get_all_events(rng=random):
cards = [ cards = [
Benedizione(), Benedizione(),
Maledizione(), Maledizione(),
CittaFantasma(), CittaFantasma(),
CorsaAllOro(), CorsaAllOro(),
IDalton(), IDalton(),
IlDottore(), IlDottore(),
IlReverendo(), IlReverendo(),
IlTreno(), IlTreno(),
Sbornia(), Sbornia(),
Sermone(), Sermone(),
Sete(), Sete(),
Sparatoria(), Sparatoria(),
Manette(), Manette(),
NuovaIdentita(), NuovaIdentita(),
] ]
rng.shuffle(cards) rng.shuffle(cards)
for c in cards: for c in cards:
c.expansion = 'high-noon' c.expansion = "high-noon" # pylint: disable=attribute-defined-outside-init
return cards return cards

View File

@ -1,4 +1,8 @@
import random import random
import bang.cards as cs
import bang.roles as roles
import bang.players as players
from globals import G
from bang.expansions.fistful_of_cards.card_events import CardEvent from bang.expansions.fistful_of_cards.card_events import CardEvent
@ -9,74 +13,143 @@ from bang.expansions.fistful_of_cards.card_events import CardEvent
# # I giocatori non possono parlare (ma possono gesticolare, mugugnare...). Chi parla perde 1 punto vita. # # I giocatori non possono parlare (ma possono gesticolare, mugugnare...). Chi parla perde 1 punto vita.
# # NOT IMPLEMENTED # # NOT IMPLEMENTED
class Camposanto(CardEvent): class Camposanto(CardEvent):
""" """
All'inizio del proprio turno, ogni giocatore eliminato torna in gioco con 1 punto vita. Pesca il ruolo a caso fra quelli dei giocatori eliminati. All'inizio del proprio turno, ogni giocatore eliminato torna in gioco con 1 punto vita. Pesca il ruolo a caso fra quelli dei giocatori eliminati.
""" """
def __init__(self): def __init__(self):
super().__init__("Camposanto", "") super().__init__("Camposanto", "")
class DarlingValentine(CardEvent): class DarlingValentine(CardEvent):
""" """
All'inizio del proprio turno, ogni giocatore scarta le carte in mano e ne pesca dal mazzo altrettante. All'inizio del proprio turno, ogni giocatore scarta le carte in mano e ne pesca dal mazzo altrettante.
""" """
def __init__(self): def __init__(self):
super().__init__("Darling Valentine", "💋") super().__init__("Darling Valentine", "💋")
class DorothyRage(CardEvent): class DorothyRage(CardEvent):
""" """
Nel proprio turno, ogni giocatore può obbligarne un altro a giocare una carta. Nel proprio turno, ogni giocatore può obbligarne un altro a giocare una carta.
""" """
def __init__(self): def __init__(self):
super().__init__("Dorothy Rage", "👩‍⚖️") super().__init__("Dorothy Rage", "👩‍⚖️")
class HelenaZontero(CardEvent): class HelenaZontero(CardEvent):
""" """
Quando Helena entra in gioco, "estrai!": se esce Cuori o Quadri, rimescola i ruoli attivi tranne lo Sceriffo, e ridistribuiscili a caso. Quando Helena entra in gioco, "estrai!": se esce Cuori o Quadri, rimescola i ruoli attivi tranne lo Sceriffo, e ridistribuiscili a caso.
""" """
def __init__(self): def __init__(self):
super().__init__("Helena Zontero", "💞") super().__init__("Helena Zontero", "💞")
def on_flipped(self, game):
c = game.deck.pick_and_scrap()
G.sio.emit(
"chat_message",
room=game.name,
data=f"_flipped|Helena Zontero|{c.name}|{c.num_suit()}",
)
if c.check_suit(game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]):
G.sio.emit(
"chat_message",
room=game.name,
data=f"_swapped_roles|Helena Zontero|{c.name}|{c.num_suit()}",
)
pls = [p for p in game.players if not isinstance(p.role, roles.Sheriff)]
newroles = [p.role for p in pls]
random.shuffle(newroles)
for p in pls:
p.set_role(newroles.pop(random.randint(0, len(newroles) - 1)))
return super().on_flipped(game)
class LadyRosaDelTexas(CardEvent): class LadyRosaDelTexas(CardEvent):
""" """
Nel proprio turno, ogni giocatore può scambiarsi di posto con quello alla sua destra, il quale salta il prossimo turno. Nel proprio turno, ogni giocatore può scambiarsi di posto con quello alla sua destra, il quale salta il prossimo turno.
""" """
def __init__(self): def __init__(self):
super().__init__("Lady Rosa del Texas", "🩰") super().__init__("Lady Rosa del Texas", "🩰")
def on_clicked(self, game, player):
super().on_clicked(game, player)
nextp = game.next_player()
i, j = game.players_map[player.name], game.players_map[nextp.name]
game.players[i], game.players[j] = nextp, player
game.players_map[player.name], game.players_map[nextp.name] = j, i
game.turn = j
game.notify_all()
class MissSusanna(CardEvent): class MissSusanna(CardEvent):
""" """
Nel proprio turno ogni giocatore deve giocare almeno 3 carte. Se non lo fa, perde 1 punto vita. Nel proprio turno ogni giocatore deve giocare almeno 3 carte. Se non lo fa, perde 1 punto vita.
""" """
def __init__(self): def __init__(self):
super().__init__("Miss Susanna", "👩‍🎤") super().__init__("Miss Susanna", "👩‍🎤")
class RegolamentoDiConti(CardEvent): class RegolamentoDiConti(CardEvent):
""" """
Tutte le carte possono essere giocate come se fossero BANG!. Le carte BANG! come se fossero Mancato! Tutte le carte possono essere giocate come se fossero BANG!. Le carte BANG! come se fossero Mancato!
""" """
def __init__(self): def __init__(self):
super().__init__("Regolamento di Conti", "🤠") super().__init__("Regolamento di Conti", "🤠")
def on_clicked(self, game, player):
super().on_clicked(game, player)
if len(player.hand) > 0:
if not player.has_played_bang and any(
(
player.get_sight() >= p["dist"]
for p in game.get_visible_players(player)
)
):
player.available_cards = player.hand.copy()
player.pending_action = players.PendingAction.CHOOSE
player.choose_text = "choose_play_as_bang"
player.notify_self()
class Sacagaway(CardEvent): class Sacagaway(CardEvent):
""" """
Tutti i giocatori giocano a carte scoperte (tranne il ruolo!). Tutti i giocatori giocano a carte scoperte (tranne il ruolo!).
""" """
def __init__(self): def __init__(self):
super().__init__("Sacagaway", "🌄") super().__init__("Sacagaway", "🌄")
class WildWestShow(CardEvent): class WildWestShow(CardEvent):
""" """
L'obiettivo di ogni giocatore diventa: "Rimani l'ultimo in gioco!" L'obiettivo di ogni giocatore diventa: "Rimani l'ultimo in gioco!"
""" """
def __init__(self): def __init__(self):
super().__init__("Wild West Show", "🎪") super().__init__("Wild West Show", "🎪")
def on_flipped(self, game):
for player in game.players:
player.set_role(roles.Renegade())
return super().on_flipped(game)
def get_endgame_card(): def get_endgame_card():
end_game = WildWestShow() end_game = WildWestShow()
end_game.expansion = 'wild-west-show' end_game.expansion = "wild-west-show"
return end_game return end_game
def get_all_events(rng=random): def get_all_events(rng=random):
cards = [ cards = [
Camposanto(), Camposanto(),
@ -90,5 +163,5 @@ def get_all_events(rng=random):
] ]
rng.shuffle(cards) rng.shuffle(cards)
for c in cards: for c in cards:
c.expansion = 'wild-west-show' c.expansion = "wild-west-show"
return cards return cards

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,18 +4,21 @@ from bang.game import Game
from bang.players import Player from bang.players import Player
from tests.dummy_socket import DummySocket from tests.dummy_socket import DummySocket
from globals import G from globals import G
G.sio = DummySocket() G.sio = DummySocket()
def started_game(expansions, players=4, character=Character('test_char', 4)): def started_game(expansions, players=4, character=Character("test_char", 4)):
g = Game('test') g = Game("test")
g.expansions = expansions g.expansions = expansions
ps = [Player(f'p{i}', f'p{i}') for i in range(players)] ps = [Player(f"p{i}", f"p{i}") for i in range(players)]
for p in ps: for p in ps:
g.add_player(p) g.add_player(p)
g.start_game() g.start_game()
for p in ps: for p in ps:
p.available_characters = [character] p.available_characters = [character]
if "high_noon" in expansions:
p.available_characters.append(Character("test_char2", 4))
p.set_character(p.available_characters[0].name) p.set_character(p.available_characters[0].name)
return g return g
@ -34,6 +37,6 @@ def next_player(g: Game):
def current_player_with_cards(g: Game, cards): def current_player_with_cards(g: Game, cards):
p = current_player(g) p = current_player(g)
p.draw('') p.draw("")
p.hand = cards p.hand = cards
return p return p

View File

@ -3,16 +3,17 @@ from bang.game import Game
from bang.players import Player, PendingAction from bang.players import Player, PendingAction
from bang.roles import * from bang.roles import *
from bang.cards import * from bang.cards import *
from tests import started_game
# test that game can start # test that game can start
def test_game_start(): def test_game_start():
g = Game("test")
g = Game('test') p1 = Player("p1", "p1")
p1 = Player('p1', 'p1')
g.add_player(p1) g.add_player(p1)
p2 = Player('p2', 'p2') p2 = Player("p2", "p2")
g.add_player(p2) g.add_player(p2)
p3 = Player('p3', 'p3') p3 = Player("p3", "p3")
g.add_player(p3) g.add_player(p3)
assert p1.role is None assert p1.role is None
assert p2.role is None assert p2.role is None
@ -34,57 +35,68 @@ def test_game_start():
assert p3.character is not None assert p3.character is not None
assert g.players[g.turn].pending_action == PendingAction.DRAW assert g.players[g.turn].pending_action == PendingAction.DRAW
# test that dodge_city is added to games with more than 8 players # test that dodge_city is added to games with more than 8 players
def test_dodge_city(): def test_dodge_city():
g = Game("test")
g = Game('test')
for i in range(9): for i in range(9):
p = Player(f'p{i}', f'p{i}') p = Player(f"p{i}", f"p{i}")
g.add_player(p) g.add_player(p)
assert 'dodge_city' in g.expansions assert "dodge_city" in g.expansions
# test that a game with 2 players has only renegade as role # test that a game with 2 players has only renegade as role
def test_renegade_only(): def test_renegade_only():
g = Game("test")
g = Game('test') p1 = Player("p1", "p1")
p1 = Player('p1', 'p1')
g.add_player(p1) g.add_player(p1)
p2 = Player('p2', 'p2') p2 = Player("p2", "p2")
g.add_player(p2) g.add_player(p2)
g.start_game() g.start_game()
assert isinstance(g.players[0].role, Renegade) assert isinstance(g.players[0].role, Renegade)
assert isinstance(g.players[1].role, Renegade) assert isinstance(g.players[1].role, Renegade)
# test that a game with 3 player has Renegade, Vice and Outlaw as roles # test that a game with 3 player has Renegade, Vice and Outlaw as roles
def test_renegade_vice_outlaw(): def test_renegade_vice_outlaw():
g = Game("test")
g = Game('test')
for i in range(3): for i in range(3):
p = Player(f'p{i}', f'p{i}') p = Player(f"p{i}", f"p{i}")
g.add_player(p) g.add_player(p)
g.start_game() g.start_game()
roles = {p.role.name for p in g.players} roles = {p.role.name for p in g.players}
assert len(roles) == 3 assert len(roles) == 3
# test that a game with 4 players has all roles except the deputy # test that a game with 4 players has all roles except the deputy
def test_4_players_roles(): def test_4_players_roles():
g = Game("test")
g = Game('test')
for i in range(4): for i in range(4):
p = Player(f'p{i}', f'p{i}') p = Player(f"p{i}", f"p{i}")
g.add_player(p) g.add_player(p)
g.start_game() g.start_game()
roles = {p.role.name for p in g.players} roles = {p.role.name for p in g.players}
assert len(roles) == 3 assert len(roles) == 3
# test that a game with 5 players has all roles # test that a game with 5 players has all roles
def test_5_players_roles(): def test_5_players_roles():
g = Game("test")
g = Game('test')
for i in range(5): for i in range(5):
p = Player(f'p{i}', f'p{i}') p = Player(f"p{i}", f"p{i}")
g.add_player(p) g.add_player(p)
g.start_game() g.start_game()
roles = {p.role.name for p in g.players} roles = {p.role.name for p in g.players}
assert len(roles) == 4 assert len(roles) == 4
def test_expansions():
started_game(
[
"high_noon",
"dodge_city",
"gold_rush",
"the_valley_of_shadows",
"wild_west_show",
]
)

View File

@ -14,7 +14,12 @@
<div v-if="eventCard" style="position:relative"> <div v-if="eventCard" style="position:relative">
<div class="card fistful-of-cards" style="position:relative; bottom:-3pt;right:-3pt;"/> <div class="card fistful-of-cards" style="position:relative; bottom:-3pt;right:-3pt;"/>
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/> <div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="eventCard" :key="eventCard.name" :class="eventClasses" @click.native="event"/> <card :card="eventCard" :key="eventCard.name" :class="eventClasses" @click.native="() => event('event')"/>
</div>
<div v-if="eventCardWildWestShow" style="position:relative">
<div class="card wild-west-show back" style="position:relative; bottom:-3pt;right:-3pt;"/>
<div class="card wild-west-show back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="eventCardWildWestShow" :key="eventCardWildWestShow.name" :class="eventWwsClasses" @click.native="() => event('event_wildwestshow')"/>
</div> </div>
<div style="position:relative" class="deck"> <div style="position:relative" class="deck">
<div style="position:relative" id="actual-deck"> <div style="position:relative" id="actual-deck">
@ -34,6 +39,9 @@
<transition name="list"> <transition name="list">
<p v-if="eventCard" class="center-stuff"><b>{{eventDesc}}</b></p> <p v-if="eventCard" class="center-stuff"><b>{{eventDesc}}</b></p>
</transition> </transition>
<transition name="list">
<p v-if="eventCardWildWestShow && !eventCardWildWestShow.back" class="center-stuff">🎪 <b>{{eventDescWildWestShow}}</b> 🎪</p>
</transition>
<transition name="list"> <transition name="list">
<div v-if="goldRushDesc"> <div v-if="goldRushDesc">
<p class="center-stuff">🤑 <i>{{$t(`cards.${goldRushDesc.name}.desc`)}}</i> 🤑</p> <p class="center-stuff">🤑 <i>{{$t(`cards.${goldRushDesc.name}.desc`)}}</i> 🤑</p>
@ -71,6 +79,7 @@ export default {
}, },
lastScrap: null, lastScrap: null,
eventCard: null, eventCard: null,
eventCardWildWestShow: null,
previousScrap: null, previousScrap: null,
pending_action: false, pending_action: false,
isPlaying: true, isPlaying: true,
@ -102,6 +111,14 @@ export default {
expansion: 'fistful-of-cards', expansion: 'fistful-of-cards',
} : card } : card
}, },
event_card_wildwestshow(card) {
this.eventCardWildWestShow = card == false ? {
name: 'Wild West Show',
icon: '🎪',
back: true,
expansion: 'wild-west-show',
} : card
},
gold_rush_shop(cards) { gold_rush_shop(cards) {
console.log('GOLD RUSH:'+ cards) console.log('GOLD RUSH:'+ cards)
this.goldRushCards = JSON.parse(cards) this.goldRushCards = JSON.parse(cards)
@ -122,13 +139,28 @@ export default {
classes[this.eventCard.expansion] = true classes[this.eventCard.expansion] = true
return classes return classes
}, },
eventWwsClasses() {
let classes = {
'last-event':true,
'back':this.eventCardWildWestShow.back,
'wild-west-show':true,
}
return classes
},
eventDesc() { eventDesc() {
this.eventCard; this.eventCard;
if (this.eventCard.name !== 'PewPew!'){ if (this.eventCard.name !== 'PewPew!'){
return this.$t(`cards.${this.eventCard.name}.desc`) return this.$t(`cards.${this.eventCard.name}.desc`)
} }
return "" return ""
} },
eventDescWildWestShow() {
this.eventCardWildWestShow;
if (this.eventCardWildWestShow.name !== 'PewPew!'){
return this.$t(`cards.${this.eventCardWildWestShow.name}.desc`)
}
return ""
},
}, },
methods: { methods: {
action(pile) { action(pile) {
@ -143,9 +175,9 @@ export default {
buy_gold_rush_card(index) { buy_gold_rush_card(index) {
this.$socket.emit('buy_gold_rush_card', index) this.$socket.emit('buy_gold_rush_card', index)
}, },
event() { event(pile='event') {
if (this.pending_action !== false) { if (this.pending_action !== false) {
this.$socket.emit('draw', 'event') this.$socket.emit('draw', pile)
} }
}, },
setdesc() { setdesc() {

View File

@ -66,6 +66,7 @@
:donotlocalize="true" :donotlocalize="true"
:card="startGameCard" :card="startGameCard"
@click.native="startGame" @click.native="startGame"
style="margin-top: 36pt;"
/> />
<div <div
v-for="p in playersTable" v-for="p in playersTable"
@ -126,7 +127,7 @@
@click.native="drawFromPlayer(p.name)" @click.native="drawFromPlayer(p.name)"
:ismyturn="p.pending_action === 2" :ismyturn="p.pending_action === 2"
/> />
<span style="position: absolute; top: 10pt" class="center-stuff">{{ <span style="position: absolute; top: 3pt" class="center-stuff">{{
getActionEmoji(p) getActionEmoji(p)
}}</span> }}</span>
<div class="tiny-equipment"> <div class="tiny-equipment">
@ -186,6 +187,7 @@
:donotlocalize="true" :donotlocalize="true"
:card="shufflePlayersCard" :card="shufflePlayersCard"
@click.native="shufflePlayers" @click.native="shufflePlayers"
style="margin-top: 36pt;"
class="fistful-of-cards" class="fistful-of-cards"
/> />
</transition-group> </transition-group>
@ -803,13 +805,18 @@ export default {
right: -35pt; right: -35pt;
transform: scale(0.45); transform: scale(0.45);
transform-origin: 50% 0%; transform-origin: 50% 0%;
top: 10pt; top: 4pt;
} }
.tiny-health { .tiny-health {
display: flex; display: flex;
justify-content: space-evenly; justify-content: space-evenly;
transform: scale(0.8); transform: scale(0.8);
margin-bottom: -4pt; margin-top: -16pt;
position: absolute;
z-index: 1;
top: 0;
left: 0;
right: 0;
} }
.tiny-equipment .card { .tiny-equipment .card {
transform: rotate(2deg); transform: rotate(2deg);
@ -823,7 +830,7 @@ export default {
} }
.tiny-character { .tiny-character {
position: absolute; position: absolute;
transform: scale(0.5) translate(-80px, -40px); transform: translate(-30pt, -30pt) scale(0.5);
top: 0; top: 0;
} }
.players-table { .players-table {
@ -888,6 +895,7 @@ export default {
} }
.player-in-table { .player-in-table {
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
margin-top: 26pt;
} }
.player-in-table:hover { .player-in-table:hover {
transform: translateY(-5px) scale(1.05); transform: translateY(-5px) scale(1.05);