card event refactoring and moved wild-west-show to its own pile
This commit is contained in:
parent
f56120d44e
commit
e3b168ff57
@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
||||
from bang.players import Player
|
||||
from bang.game import Game
|
||||
|
||||
|
||||
class Suit(IntEnum):
|
||||
DIAMONDS = 0 # ♦
|
||||
CLUBS = 1 # ♣
|
||||
@ -20,14 +21,20 @@ class Suit(IntEnum):
|
||||
|
||||
|
||||
class Card(ABC):
|
||||
sym = {
|
||||
'A': 1,
|
||||
'J': 11,
|
||||
'Q': 12,
|
||||
'K': 13
|
||||
}
|
||||
sym = {"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__()
|
||||
self.name = name
|
||||
self.suit = suit
|
||||
@ -41,22 +48,24 @@ class Card(ABC):
|
||||
self.sight_mod = sight_mod
|
||||
self.range = range
|
||||
if self.range != 0 and self.range != 99:
|
||||
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.need_target = False # Cards that need a target like Bang
|
||||
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.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_only = '' # names of the cards allowed to be discarded with
|
||||
self.must_be_used = False # used by LeggeDelWest
|
||||
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.need_target = False # Cards that need a target like Bang
|
||||
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.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_only = "" # names of the cards allowed to be discarded with
|
||||
self.must_be_used = False # used by LeggeDelWest
|
||||
|
||||
def __str__(self):
|
||||
if str(self.suit).isnumeric():
|
||||
char = ['♦️', '♣️', '♥️', '♠️', '🤑'][int(self.suit)]
|
||||
char = ["♦️", "♣️", "♥️", "♠️", "🤑"][int(self.suit)]
|
||||
else:
|
||||
char = self.suit
|
||||
return f'{self.name} {char}{self.number}'
|
||||
return f"{self.name} {char}{self.number}"
|
||||
return super().__str__()
|
||||
|
||||
def num_suit(self):
|
||||
@ -70,8 +79,14 @@ class Card(ABC):
|
||||
if self.must_be_used:
|
||||
self.must_be_used = False
|
||||
|
||||
def play_card(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:
|
||||
def play_card(
|
||||
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
|
||||
if self.is_equipment:
|
||||
if (player.game.check_event(ce.IlGiudice)) or not self.can_be_used_now:
|
||||
@ -86,38 +101,53 @@ class Card(ABC):
|
||||
break
|
||||
if not has_weapon:
|
||||
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
|
||||
else:
|
||||
player.equipment.append(self)
|
||||
self.must_be_used = False
|
||||
self.can_be_used_now = False
|
||||
if against:
|
||||
G.sio.emit('card_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}')
|
||||
G.sio.emit(
|
||||
"card_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:
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_play_card|{player.name}|{self.name}')
|
||||
G.sio.emit(
|
||||
"chat_message",
|
||||
room=player.game.name,
|
||||
data=f"_play_card|{player.name}|{self.name}",
|
||||
)
|
||||
return True
|
||||
|
||||
def use_card(self, player):
|
||||
pass
|
||||
|
||||
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)
|
||||
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
|
||||
)
|
||||
|
||||
def check_suit(self, game:Game, accepted:List[Suit]):
|
||||
def check_suit(self, game: Game, accepted: List[Suit]):
|
||||
if game.check_event(ceh.Benedizione):
|
||||
return Suit.HEARTS in accepted
|
||||
elif game.check_event(ceh.Maledizione):
|
||||
return Suit.SPADES in accepted
|
||||
return self.suit in accepted
|
||||
|
||||
|
||||
class Barile(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Barile', number, is_equipment=True)
|
||||
self.icon = '🛢'
|
||||
super().__init__(suit, "Barile", number, is_equipment=True)
|
||||
self.icon = "🛢"
|
||||
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_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):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Dinamite', number, is_equipment=True)
|
||||
self.icon = '🧨'
|
||||
super().__init__(suit, "Dinamite", number, is_equipment=True)
|
||||
self.icon = "🧨"
|
||||
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_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):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Mirino', number, is_equipment=True, sight_mod=1)
|
||||
self.icon = '🔎'
|
||||
super().__init__(suit, "Mirino", number, is_equipment=True, sight_mod=1)
|
||||
self.icon = "🔎"
|
||||
self.alt_text = "-1"
|
||||
# self.desc = "Tu vedi gli altri giocatori a distanza -1"
|
||||
# self.desc_eng = "You see the other players at distance -1"
|
||||
@ -143,8 +173,8 @@ class Mirino(Card):
|
||||
|
||||
class Mustang(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Mustang', number, is_equipment=True, vis_mod=1)
|
||||
self.icon = '🐎'
|
||||
super().__init__(suit, "Mustang", number, is_equipment=True, vis_mod=1)
|
||||
self.icon = "🐎"
|
||||
self.alt_text = "+1"
|
||||
# self.desc = "Gli altri giocatori ti vedono a distanza +1"
|
||||
# self.desc_eng = "The other players see you at distance +1"
|
||||
@ -152,100 +182,130 @@ class Mustang(Card):
|
||||
|
||||
class Prigione(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Prigione', number, is_equipment=True)
|
||||
self.icon = '⛓'
|
||||
super().__init__(suit, "Prigione", number, is_equipment=True)
|
||||
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_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.alt_text = "♥️= 🆓"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if (player.game.check_event(ce.IlGiudice)):
|
||||
return False
|
||||
if against is not None and not isinstance(player.game.get_player_named(against).role, r.Sheriff):
|
||||
if player.game.check_event(ce.IlGiudice):
|
||||
return False
|
||||
if against is not None and not isinstance(
|
||||
player.game.get_player_named(against).role, r.Sheriff
|
||||
):
|
||||
self.can_be_used_now = False
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_play_card_against|{player.name}|{self.name}|{against}')
|
||||
G.sio.emit(
|
||||
"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).notify_self()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Remington(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Remington', number,
|
||||
is_equipment=True, is_weapon=True, range=3)
|
||||
self.icon = '🔫'
|
||||
super().__init__(
|
||||
suit, "Remington", number, is_equipment=True, is_weapon=True, range=3
|
||||
)
|
||||
self.icon = "🔫"
|
||||
# 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"
|
||||
|
||||
|
||||
class RevCarabine(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Rev Carabine', number,
|
||||
is_equipment=True, is_weapon=True, range=4)
|
||||
self.icon = '🔫'
|
||||
super().__init__(
|
||||
suit, "Rev Carabine", number, is_equipment=True, is_weapon=True, range=4
|
||||
)
|
||||
self.icon = "🔫"
|
||||
# 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"
|
||||
|
||||
|
||||
class Schofield(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Schofield', number,
|
||||
is_equipment=True, is_weapon=True, range=2)
|
||||
self.icon = '🔫'
|
||||
super().__init__(
|
||||
suit, "Schofield", number, is_equipment=True, is_weapon=True, range=2
|
||||
)
|
||||
self.icon = "🔫"
|
||||
# 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"
|
||||
|
||||
|
||||
class Volcanic(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Volcanic', number,
|
||||
is_equipment=True, is_weapon=True, range=1)
|
||||
self.icon = '🔫'
|
||||
super().__init__(
|
||||
suit, "Volcanic", number, is_equipment=True, is_weapon=True, range=1
|
||||
)
|
||||
self.icon = "🔫"
|
||||
# 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"
|
||||
|
||||
|
||||
class Winchester(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Winchester', number,
|
||||
is_equipment=True, is_weapon=True, range=5)
|
||||
self.icon = '🔫'
|
||||
super().__init__(
|
||||
suit, "Winchester", number, is_equipment=True, is_weapon=True, range=5
|
||||
)
|
||||
self.icon = "🔫"
|
||||
# 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"
|
||||
|
||||
|
||||
class Bang(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Bang!', number)
|
||||
self.icon = '💥'
|
||||
super().__init__(suit, "Bang!", number)
|
||||
self.icon = "💥"
|
||||
# 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.need_target = True
|
||||
|
||||
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
|
||||
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
|
||||
elif against is not None:
|
||||
import bang.characters as chars
|
||||
|
||||
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.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):
|
||||
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 False
|
||||
|
||||
|
||||
class Birra(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Birra', number)
|
||||
self.icon = '🍺'
|
||||
super().__init__(suit, "Birra", number)
|
||||
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_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
|
||||
if not skipChecks:
|
||||
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:
|
||||
player.game.deck.draw(True, player=p)
|
||||
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
|
||||
player.available_cards = [{
|
||||
'name': 'Pepita',
|
||||
'icon': '💵️',
|
||||
'alt_text': '1',
|
||||
'noDesc': True
|
||||
}, self]
|
||||
player.choose_text = 'choose_birra_function'
|
||||
|
||||
player.available_cards = [
|
||||
{"name": "Pepita", "icon": "💵️", "alt_text": "1", "noDesc": True},
|
||||
self,
|
||||
]
|
||||
player.choose_text = "choose_birra_function"
|
||||
player.pending_action = PendingAction.CHOOSE
|
||||
player.notify_self()
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
||||
elif len(player.game.get_alive_players()) == 2 or player.lives == player.max_lives:
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_spilled_beer|{player.name}|{self.name}')
|
||||
elif (
|
||||
len(player.game.get_alive_players()) == 2
|
||||
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 False
|
||||
|
||||
|
||||
class CatBalou(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Cat Balou', number)
|
||||
self.icon = '💃'
|
||||
super().__init__(suit, "Cat Balou", number)
|
||||
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_eng = "Choose and discard a card from any other player."
|
||||
self.need_target = True
|
||||
self.can_target_self = True
|
||||
|
||||
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)
|
||||
player.game.steal_discard(player, against, self)
|
||||
return True
|
||||
@ -303,25 +384,29 @@ class CatBalou(Card):
|
||||
|
||||
class Diligenza(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Diligenza', number)
|
||||
self.icon = '🚡'
|
||||
super().__init__(suit, "Diligenza", number)
|
||||
self.icon = "🚡"
|
||||
self.alt_text = "🎴🎴"
|
||||
# self.desc = "Pesca 2 carte dalla cima del mazzo"
|
||||
# self.desc_eng = "Draw 2 cards from the deck."
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_diligenza|{player.name}|{self.name}')
|
||||
G.sio.emit(
|
||||
"chat_message",
|
||||
room=player.game.name,
|
||||
data=f"_diligenza|{player.name}|{self.name}",
|
||||
)
|
||||
for i in range(2):
|
||||
player.game.deck.draw(True, player)
|
||||
player.game.deck.flip_wildwestshow()
|
||||
return True
|
||||
|
||||
|
||||
class Duello(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Duello', number)
|
||||
super().__init__(suit, "Duello", number)
|
||||
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_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):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Emporio', number)
|
||||
self.icon = '🏪'
|
||||
super().__init__(suit, "Emporio", number)
|
||||
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_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):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Gatling', number)
|
||||
self.icon = '🛰'
|
||||
super().__init__(suit, "Gatling", number)
|
||||
self.icon = "🛰"
|
||||
# self.desc = "Spara a tutti gli altri giocatori"
|
||||
# self.desc_eng = "Shoot all the other players"
|
||||
self.alt_text = "👥💥"
|
||||
@ -362,8 +447,8 @@ class Gatling(Card):
|
||||
|
||||
class Indiani(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Indiani!', number)
|
||||
self.icon = '🏹'
|
||||
super().__init__(suit, "Indiani!", number)
|
||||
self.icon = "🏹"
|
||||
# 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"
|
||||
|
||||
@ -375,22 +460,35 @@ class Indiani(Card):
|
||||
|
||||
class Mancato(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Mancato!', number)
|
||||
self.icon = '😅'
|
||||
super().__init__(suit, "Mancato!", number)
|
||||
self.icon = "😅"
|
||||
# self.desc = "Usa questa carta per annullare un bang"
|
||||
# self.desc_eng = "Use this card to cancel the effect of a bang"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
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
|
||||
if player.game.check_event(ceh.Sermone):
|
||||
return False
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_special_calamity|{player.name}|{self.name}|{against}')
|
||||
G.sio.emit(
|
||||
"chat_message",
|
||||
room=player.game.name,
|
||||
data=f"_special_calamity|{player.name}|{self.name}|{against}",
|
||||
)
|
||||
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)
|
||||
return True
|
||||
return False
|
||||
@ -398,15 +496,23 @@ class Mancato(Card):
|
||||
|
||||
class Panico(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Panico!', number, range=1)
|
||||
self.icon = '😱'
|
||||
super().__init__(suit, "Panico!", number, range=1)
|
||||
self.icon = "😱"
|
||||
self.need_target = 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_eng = "Steal a card from a player at distance 1"
|
||||
|
||||
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)
|
||||
player.game.steal_discard(player, against, self)
|
||||
return True
|
||||
@ -415,58 +521,66 @@ class Panico(Card):
|
||||
|
||||
class Saloon(Card):
|
||||
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_eng = "Everyone heals 1 Health point"
|
||||
self.icon = '🍻'
|
||||
self.icon = "🍻"
|
||||
self.alt_text = "👥🍺"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_saloon|{player.name}|{self.name}')
|
||||
G.sio.emit(
|
||||
"chat_message",
|
||||
room=player.game.name,
|
||||
data=f"_saloon|{player.name}|{self.name}",
|
||||
)
|
||||
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()
|
||||
return True
|
||||
|
||||
|
||||
class WellsFargo(Card):
|
||||
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_eng = "Draw 3 cards from the deck"
|
||||
self.icon = '💸'
|
||||
self.icon = "💸"
|
||||
self.alt_text = "🎴🎴🎴"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
G.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_wellsfargo|{player.name}|{self.name}')
|
||||
for i in range(3):
|
||||
G.sio.emit(
|
||||
"chat_message",
|
||||
room=player.game.name,
|
||||
data=f"_wellsfargo|{player.name}|{self.name}",
|
||||
)
|
||||
for _ in range(3):
|
||||
player.game.deck.draw(True, player)
|
||||
player.game.deck.flip_wildwestshow()
|
||||
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
|
||||
|
||||
base_cards = [
|
||||
Barile(Suit.SPADES, 'Q'),
|
||||
Barile(Suit.SPADES, 'K'),
|
||||
Barile(Suit.SPADES, "Q"),
|
||||
Barile(Suit.SPADES, "K"),
|
||||
Dinamite(Suit.HEARTS, 2),
|
||||
Mirino(Suit.SPADES, 'A'),
|
||||
Mirino(Suit.SPADES, "A"),
|
||||
Mustang(Suit.HEARTS, 8),
|
||||
Mustang(Suit.HEARTS, 9),
|
||||
Prigione(Suit.SPADES, 'J'),
|
||||
Prigione(Suit.SPADES, "J"),
|
||||
Prigione(Suit.HEARTS, 4),
|
||||
Prigione(Suit.SPADES, 10),
|
||||
Remington(Suit.CLUBS, 'K'),
|
||||
RevCarabine(Suit.CLUBS, 'A'),
|
||||
Schofield(Suit.CLUBS, 'J'),
|
||||
Schofield(Suit.CLUBS, 'Q'),
|
||||
Schofield(Suit.SPADES, 'K'),
|
||||
Remington(Suit.CLUBS, "K"),
|
||||
RevCarabine(Suit.CLUBS, "A"),
|
||||
Schofield(Suit.CLUBS, "J"),
|
||||
Schofield(Suit.CLUBS, "Q"),
|
||||
Schofield(Suit.SPADES, "K"),
|
||||
Volcanic(Suit.SPADES, 10),
|
||||
Volcanic(Suit.CLUBS, 10),
|
||||
Winchester(Suit.SPADES, 8),
|
||||
Bang(Suit.SPADES, 'A'),
|
||||
Bang(Suit.SPADES, "A"),
|
||||
Bang(Suit.DIAMONDS, 2),
|
||||
Bang(Suit.DIAMONDS, 3),
|
||||
Bang(Suit.DIAMONDS, 4),
|
||||
@ -476,10 +590,10 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
|
||||
Bang(Suit.DIAMONDS, 8),
|
||||
Bang(Suit.DIAMONDS, 9),
|
||||
Bang(Suit.DIAMONDS, 10),
|
||||
Bang(Suit.DIAMONDS, 'J'),
|
||||
Bang(Suit.DIAMONDS, 'Q'),
|
||||
Bang(Suit.DIAMONDS, 'K'),
|
||||
Bang(Suit.DIAMONDS, 'A'),
|
||||
Bang(Suit.DIAMONDS, "J"),
|
||||
Bang(Suit.DIAMONDS, "Q"),
|
||||
Bang(Suit.DIAMONDS, "K"),
|
||||
Bang(Suit.DIAMONDS, "A"),
|
||||
Bang(Suit.CLUBS, 2),
|
||||
Bang(Suit.CLUBS, 3),
|
||||
Bang(Suit.CLUBS, 4),
|
||||
@ -488,34 +602,34 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
|
||||
Bang(Suit.CLUBS, 7),
|
||||
Bang(Suit.CLUBS, 8),
|
||||
Bang(Suit.CLUBS, 9),
|
||||
Bang(Suit.HEARTS, 'Q'),
|
||||
Bang(Suit.HEARTS, 'K'),
|
||||
Bang(Suit.HEARTS, 'A'),
|
||||
Bang(Suit.HEARTS, "Q"),
|
||||
Bang(Suit.HEARTS, "K"),
|
||||
Bang(Suit.HEARTS, "A"),
|
||||
Birra(Suit.HEARTS, 6),
|
||||
Birra(Suit.HEARTS, 7),
|
||||
Birra(Suit.HEARTS, 8),
|
||||
Birra(Suit.HEARTS, 9),
|
||||
Birra(Suit.HEARTS, 10),
|
||||
Birra(Suit.HEARTS, 'J'),
|
||||
CatBalou(Suit.HEARTS, 'K'),
|
||||
Birra(Suit.HEARTS, "J"),
|
||||
CatBalou(Suit.HEARTS, "K"),
|
||||
CatBalou(Suit.DIAMONDS, 9),
|
||||
CatBalou(Suit.DIAMONDS, 10),
|
||||
CatBalou(Suit.DIAMONDS, 'J'),
|
||||
CatBalou(Suit.DIAMONDS, "J"),
|
||||
Diligenza(Suit.SPADES, 9),
|
||||
Diligenza(Suit.SPADES, 9),
|
||||
Duello(Suit.DIAMONDS, 'Q'),
|
||||
Duello(Suit.SPADES, 'J'),
|
||||
Duello(Suit.DIAMONDS, "Q"),
|
||||
Duello(Suit.SPADES, "J"),
|
||||
Duello(Suit.CLUBS, 8),
|
||||
Emporio(Suit.CLUBS, 9),
|
||||
Emporio(Suit.SPADES, 'Q'),
|
||||
Emporio(Suit.SPADES, "Q"),
|
||||
Gatling(Suit.HEARTS, 10),
|
||||
Indiani(Suit.DIAMONDS, 'K'),
|
||||
Indiani(Suit.DIAMONDS, 'A'),
|
||||
Indiani(Suit.DIAMONDS, "K"),
|
||||
Indiani(Suit.DIAMONDS, "A"),
|
||||
Mancato(Suit.CLUBS, 10),
|
||||
Mancato(Suit.CLUBS, 'J'),
|
||||
Mancato(Suit.CLUBS, 'Q'),
|
||||
Mancato(Suit.CLUBS, 'K'),
|
||||
Mancato(Suit.CLUBS, 'A'),
|
||||
Mancato(Suit.CLUBS, "J"),
|
||||
Mancato(Suit.CLUBS, "Q"),
|
||||
Mancato(Suit.CLUBS, "K"),
|
||||
Mancato(Suit.CLUBS, "A"),
|
||||
Mancato(Suit.SPADES, 2),
|
||||
Mancato(Suit.SPADES, 3),
|
||||
Mancato(Suit.SPADES, 4),
|
||||
@ -523,16 +637,15 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
|
||||
Mancato(Suit.SPADES, 6),
|
||||
Mancato(Suit.SPADES, 7),
|
||||
Mancato(Suit.SPADES, 8),
|
||||
Panico(Suit.HEARTS, 'J'),
|
||||
Panico(Suit.HEARTS, 'Q'),
|
||||
Panico(Suit.HEARTS, 'A'),
|
||||
Panico(Suit.HEARTS, "J"),
|
||||
Panico(Suit.HEARTS, "Q"),
|
||||
Panico(Suit.HEARTS, "A"),
|
||||
Panico(Suit.DIAMONDS, 8),
|
||||
Saloon(Suit.HEARTS, 5),
|
||||
WellsFargo(Suit.HEARTS, 3),
|
||||
]
|
||||
if 'dodge_city' in expansions:
|
||||
if "dodge_city" in expansions:
|
||||
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())
|
||||
return base_cards
|
||||
|
||||
|
@ -10,8 +10,12 @@ from globals import G
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bang.game import Game
|
||||
|
||||
|
||||
class Deck:
|
||||
def __init__(self, game: 'Game'):
|
||||
"""Class that handles all deck dealing information"""
|
||||
|
||||
def __init__(self, game: "Game"):
|
||||
super().__init__()
|
||||
self.cards: List[cs.Card] = cs.get_starting_deck(game.expansions)
|
||||
self.mancato_cards: List[str] = []
|
||||
@ -30,45 +34,74 @@ class Deck:
|
||||
self.all_cards_str.append(c.name)
|
||||
self.game = game
|
||||
self.event_cards: List[ce.CardEvent] = []
|
||||
self.event_cards_wildwestshow: 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))
|
||||
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))
|
||||
endgame_cards.append(ceh.get_endgame_card())
|
||||
if 'wild_west_show' in game.expansions:
|
||||
self.event_cards.extend(cew.get_all_events(game.rng))
|
||||
endgame_cards.append(cew.get_endgame_card())
|
||||
if "wild_west_show" in game.expansions:
|
||||
self.event_cards_wildwestshow.extend(cew.get_all_events(game.rng))
|
||||
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:
|
||||
game.rng.shuffle(self.event_cards)
|
||||
self.event_cards = self.event_cards[:12]
|
||||
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))
|
||||
game.rng.shuffle(self.cards)
|
||||
self.shop_deck: 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_deck = grc.get_cards()
|
||||
game.rng.shuffle(self.shop_deck)
|
||||
self.fill_gold_rush_shop()
|
||||
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):
|
||||
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))
|
||||
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_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):
|
||||
"""
|
||||
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)):
|
||||
return
|
||||
for i in range(3):
|
||||
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].reset_card()
|
||||
self.game.notify_gold_rush_shop()
|
||||
@ -102,14 +135,22 @@ class Deck:
|
||||
def put_on_top(self, card: cs.Card):
|
||||
self.cards.insert(0, 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:
|
||||
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
|
||||
):
|
||||
return self.draw_from_scrap_pile()
|
||||
card = self.cards.pop(0)
|
||||
if len(self.cards) == 0:
|
||||
self.reshuffle()
|
||||
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)
|
||||
return card
|
||||
|
||||
@ -127,15 +168,24 @@ class Deck:
|
||||
else:
|
||||
return self.draw()
|
||||
|
||||
def scrap(self, card: cs.Card, ignore_event = False, player=None):
|
||||
if card.number == 42: return
|
||||
def scrap(self, card: cs.Card, ignore_event=False, player=None):
|
||||
if card.number == 42:
|
||||
return
|
||||
card.reset_card()
|
||||
if self.game.check_event(ce.MinieraAbbandonata) and not ignore_event:
|
||||
self.put_on_top(card)
|
||||
else:
|
||||
self.scrap_pile.append(card)
|
||||
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)
|
||||
self.game.notify_scrap_pile()
|
||||
else:
|
||||
|
@ -1,106 +1,198 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import random
|
||||
import bang.players as players
|
||||
import bang.roles as r
|
||||
import bang.cards as cs
|
||||
|
||||
from globals import G
|
||||
|
||||
|
||||
class CardEvent(ABC):
|
||||
"""Base class for all event cards"""
|
||||
|
||||
def __init__(self, name, icon):
|
||||
self.name = name
|
||||
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):
|
||||
"""La distanza base tra 2 qualsiasi giocatori è 1
|
||||
|
||||
The distance between 2 players is always 1"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""All'inizio del proprio turno, i giocatori possono perdere 1 vita (tranne l'ultimo) per darla a un altro giocatore"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Non si possono equipaggiare carte a se stessi o agli altri"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Le carte equipaggiate non hanno effetto"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I giocatori mostrano la seconda carta che pescano e sono obbligati a usarla in quel turno (se possibile)"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I giocatori possono evitare di pescare per recuperare 1 vita (clicca sulla carta evento per farlo)"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""All'inizio del proprio turno, il giocatore subisce tanti bang quante carte ha in mano"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""Dopo aver pescato il giocatore può scartare quante carte vuole dalla mano e pescarne altrettante dal mazzo"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""A partire dallo sceriffo, ogni giocatore scarta 1 mancato, il primo che non lo fa perde 2 vite"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Alla fine del proprio turno il giocatore estrae dal mazzo, se esce ♥️ gioca un altro turno (ma non estrae di nuovo)"""
|
||||
|
||||
def __init__(self):
|
||||
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():
|
||||
end_game = PerUnPugnoDiCarte()
|
||||
end_game.expansion = 'fistful-of-cards'
|
||||
end_game.expansion = ( # pylint: disable=attribute-defined-outside-init
|
||||
"fistful-of-cards"
|
||||
)
|
||||
return end_game
|
||||
|
||||
|
||||
def get_all_events(rng=random):
|
||||
cards = [
|
||||
Agguato(),
|
||||
@ -119,6 +211,8 @@ def get_all_events(rng=random):
|
||||
Vendetta(),
|
||||
]
|
||||
rng.shuffle(cards)
|
||||
for c in cards:
|
||||
c.expansion = 'fistful-of-cards'
|
||||
return cards
|
||||
for card in cards:
|
||||
card.expansion = ( # pylint: disable=attribute-defined-outside-init
|
||||
"fistful-of-cards"
|
||||
)
|
||||
return cards
|
||||
|
@ -1,119 +1,184 @@
|
||||
import random
|
||||
from globals import G
|
||||
|
||||
from bang.expansions.fistful_of_cards.card_events import CardEvent
|
||||
|
||||
|
||||
class Benedizione(CardEvent):
|
||||
"""Tutte le carte sono considerate di cuori ♥️"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Tutte le carte sono considerate di picche ♠"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I personaggi perdono le loro abilità speciali"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I giocatori pescano 1 carta in meno nella loro fase 1"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I giocatori pescano 1 carta extra nella loro fase 1"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Non si possono giocare le carte Birra"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Il/i giocatore/i con meno vite ne recupera/no una"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""I giocatori non possono giocare Bang! durante il loro turno"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Il limite di Bang! per turno è 2 invece che 1"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Si gioca per un intero giro in senso antiorario, tuttavia gli effetti delle carte rimangono invariati"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""Chi ha carte blu in gioco ne scarta 1 a sua scelta"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
"""Ogni giocatore perde 1 punto vita all'inizio del turno"""
|
||||
|
||||
def __init__(self):
|
||||
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():
|
||||
end_game = MezzogiornoDiFuoco()
|
||||
end_game.expansion = 'high-noon'
|
||||
end_game.expansion = "high-noon" # pylint: disable=attribute-defined-outside-init
|
||||
return end_game
|
||||
|
||||
|
||||
def get_all_events(rng=random):
|
||||
cards = [
|
||||
Benedizione(),
|
||||
Maledizione(),
|
||||
CittaFantasma(),
|
||||
CorsaAllOro(),
|
||||
IDalton(),
|
||||
IlDottore(),
|
||||
IlReverendo(),
|
||||
IlTreno(),
|
||||
Sbornia(),
|
||||
Sermone(),
|
||||
Sete(),
|
||||
Sparatoria(),
|
||||
Manette(),
|
||||
NuovaIdentita(),
|
||||
Benedizione(),
|
||||
Maledizione(),
|
||||
CittaFantasma(),
|
||||
CorsaAllOro(),
|
||||
IDalton(),
|
||||
IlDottore(),
|
||||
IlReverendo(),
|
||||
IlTreno(),
|
||||
Sbornia(),
|
||||
Sermone(),
|
||||
Sete(),
|
||||
Sparatoria(),
|
||||
Manette(),
|
||||
NuovaIdentita(),
|
||||
]
|
||||
rng.shuffle(cards)
|
||||
for c in cards:
|
||||
c.expansion = 'high-noon'
|
||||
return cards
|
||||
c.expansion = "high-noon" # pylint: disable=attribute-defined-outside-init
|
||||
return cards
|
||||
|
@ -1,4 +1,8 @@
|
||||
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
|
||||
|
||||
@ -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.
|
||||
# # NOT IMPLEMENTED
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Camposanto", "⚰")
|
||||
|
||||
|
||||
class DarlingValentine(CardEvent):
|
||||
"""
|
||||
All'inizio del proprio turno, ogni giocatore scarta le carte in mano e ne pesca dal mazzo altrettante.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Darling Valentine", "💋")
|
||||
|
||||
|
||||
class DorothyRage(CardEvent):
|
||||
"""
|
||||
Nel proprio turno, ogni giocatore può obbligarne un altro a giocare una carta.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Dorothy Rage", "👩⚖️")
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""
|
||||
Nel proprio turno, ogni giocatore può scambiarsi di posto con quello alla sua destra, il quale salta il prossimo turno.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""
|
||||
Nel proprio turno ogni giocatore deve giocare almeno 3 carte. Se non lo fa, perde 1 punto vita.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Miss Susanna", "👩🎤")
|
||||
|
||||
|
||||
class RegolamentoDiConti(CardEvent):
|
||||
"""
|
||||
Tutte le carte possono essere giocate come se fossero BANG!. Le carte BANG! come se fossero Mancato!
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
"""
|
||||
Tutti i giocatori giocano a carte scoperte (tranne il ruolo!).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Sacagaway", "🌄")
|
||||
|
||||
|
||||
class WildWestShow(CardEvent):
|
||||
"""
|
||||
L'obiettivo di ogni giocatore diventa: "Rimani l'ultimo in gioco!"
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
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():
|
||||
end_game = WildWestShow()
|
||||
end_game.expansion = 'wild-west-show'
|
||||
end_game.expansion = "wild-west-show"
|
||||
return end_game
|
||||
|
||||
|
||||
def get_all_events(rng=random):
|
||||
cards = [
|
||||
Camposanto(),
|
||||
@ -90,5 +163,5 @@ def get_all_events(rng=random):
|
||||
]
|
||||
rng.shuffle(cards)
|
||||
for c in cards:
|
||||
c.expansion = 'wild-west-show'
|
||||
return cards
|
||||
c.expansion = "wild-west-show"
|
||||
return cards
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1097
backend/server.py
1097
backend/server.py
File diff suppressed because it is too large
Load Diff
@ -4,18 +4,21 @@ from bang.game import Game
|
||||
from bang.players import Player
|
||||
from tests.dummy_socket import DummySocket
|
||||
from globals import G
|
||||
|
||||
G.sio = DummySocket()
|
||||
|
||||
|
||||
def started_game(expansions, players=4, character=Character('test_char', 4)):
|
||||
g = Game('test')
|
||||
def started_game(expansions, players=4, character=Character("test_char", 4)):
|
||||
g = Game("test")
|
||||
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:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
for p in ps:
|
||||
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)
|
||||
return g
|
||||
|
||||
@ -34,6 +37,6 @@ def next_player(g: Game):
|
||||
|
||||
def current_player_with_cards(g: Game, cards):
|
||||
p = current_player(g)
|
||||
p.draw('')
|
||||
p.draw("")
|
||||
p.hand = cards
|
||||
return p
|
||||
|
@ -3,16 +3,17 @@ from bang.game import Game
|
||||
from bang.players import Player, PendingAction
|
||||
from bang.roles import *
|
||||
from bang.cards import *
|
||||
from tests import started_game
|
||||
|
||||
|
||||
# test that game can start
|
||||
def test_game_start():
|
||||
|
||||
g = Game('test')
|
||||
p1 = Player('p1', 'p1')
|
||||
g = Game("test")
|
||||
p1 = Player("p1", "p1")
|
||||
g.add_player(p1)
|
||||
p2 = Player('p2', 'p2')
|
||||
p2 = Player("p2", "p2")
|
||||
g.add_player(p2)
|
||||
p3 = Player('p3', 'p3')
|
||||
p3 = Player("p3", "p3")
|
||||
g.add_player(p3)
|
||||
assert p1.role is None
|
||||
assert p2.role is None
|
||||
@ -34,57 +35,68 @@ def test_game_start():
|
||||
assert p3.character is not None
|
||||
assert g.players[g.turn].pending_action == PendingAction.DRAW
|
||||
|
||||
|
||||
# test that dodge_city is added to games with more than 8 players
|
||||
def test_dodge_city():
|
||||
|
||||
g = Game('test')
|
||||
g = Game("test")
|
||||
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)
|
||||
assert 'dodge_city' in g.expansions
|
||||
assert "dodge_city" in g.expansions
|
||||
|
||||
|
||||
# test that a game with 2 players has only renegade as role
|
||||
def test_renegade_only():
|
||||
|
||||
g = Game('test')
|
||||
p1 = Player('p1', 'p1')
|
||||
g = Game("test")
|
||||
p1 = Player("p1", "p1")
|
||||
g.add_player(p1)
|
||||
p2 = Player('p2', 'p2')
|
||||
p2 = Player("p2", "p2")
|
||||
g.add_player(p2)
|
||||
g.start_game()
|
||||
assert isinstance(g.players[0].role, Renegade)
|
||||
assert isinstance(g.players[1].role, Renegade)
|
||||
|
||||
|
||||
# test that a game with 3 player has Renegade, Vice and Outlaw as roles
|
||||
def test_renegade_vice_outlaw():
|
||||
|
||||
g = Game('test')
|
||||
g = Game("test")
|
||||
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.start_game()
|
||||
roles = {p.role.name for p in g.players}
|
||||
assert len(roles) == 3
|
||||
|
||||
|
||||
# test that a game with 4 players has all roles except the deputy
|
||||
def test_4_players_roles():
|
||||
|
||||
g = Game('test')
|
||||
g = Game("test")
|
||||
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.start_game()
|
||||
roles = {p.role.name for p in g.players}
|
||||
assert len(roles) == 3
|
||||
|
||||
|
||||
# test that a game with 5 players has all roles
|
||||
def test_5_players_roles():
|
||||
|
||||
g = Game('test')
|
||||
g = Game("test")
|
||||
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.start_game()
|
||||
roles = {p.role.name for p in g.players}
|
||||
assert len(roles) == 4
|
||||
|
||||
|
||||
def test_expansions():
|
||||
started_game(
|
||||
[
|
||||
"high_noon",
|
||||
"dodge_city",
|
||||
"gold_rush",
|
||||
"the_valley_of_shadows",
|
||||
"wild_west_show",
|
||||
]
|
||||
)
|
||||
|
@ -14,7 +14,12 @@
|
||||
<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: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 style="position:relative" class="deck">
|
||||
<div style="position:relative" id="actual-deck">
|
||||
@ -34,6 +39,9 @@
|
||||
<transition name="list">
|
||||
<p v-if="eventCard" class="center-stuff"><b>{{eventDesc}}</b></p>
|
||||
</transition>
|
||||
<transition name="list">
|
||||
<p v-if="eventCardWildWestShow && !eventCardWildWestShow.back" class="center-stuff">🎪 <b>{{eventDescWildWestShow}}</b> 🎪</p>
|
||||
</transition>
|
||||
<transition name="list">
|
||||
<div v-if="goldRushDesc">
|
||||
<p class="center-stuff">🤑️ <i>{{$t(`cards.${goldRushDesc.name}.desc`)}}</i> 🤑️</p>
|
||||
@ -71,6 +79,7 @@ export default {
|
||||
},
|
||||
lastScrap: null,
|
||||
eventCard: null,
|
||||
eventCardWildWestShow: null,
|
||||
previousScrap: null,
|
||||
pending_action: false,
|
||||
isPlaying: true,
|
||||
@ -102,6 +111,14 @@ export default {
|
||||
expansion: 'fistful-of-cards',
|
||||
} : 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) {
|
||||
console.log('GOLD RUSH:'+ cards)
|
||||
this.goldRushCards = JSON.parse(cards)
|
||||
@ -122,13 +139,28 @@ export default {
|
||||
classes[this.eventCard.expansion] = true
|
||||
return classes
|
||||
},
|
||||
eventWwsClasses() {
|
||||
let classes = {
|
||||
'last-event':true,
|
||||
'back':this.eventCardWildWestShow.back,
|
||||
'wild-west-show':true,
|
||||
}
|
||||
return classes
|
||||
},
|
||||
eventDesc() {
|
||||
this.eventCard;
|
||||
if (this.eventCard.name !== 'PewPew!'){
|
||||
return this.$t(`cards.${this.eventCard.name}.desc`)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
},
|
||||
eventDescWildWestShow() {
|
||||
this.eventCardWildWestShow;
|
||||
if (this.eventCardWildWestShow.name !== 'PewPew!'){
|
||||
return this.$t(`cards.${this.eventCardWildWestShow.name}.desc`)
|
||||
}
|
||||
return ""
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action(pile) {
|
||||
@ -143,9 +175,9 @@ export default {
|
||||
buy_gold_rush_card(index) {
|
||||
this.$socket.emit('buy_gold_rush_card', index)
|
||||
},
|
||||
event() {
|
||||
event(pile='event') {
|
||||
if (this.pending_action !== false) {
|
||||
this.$socket.emit('draw', 'event')
|
||||
this.$socket.emit('draw', pile)
|
||||
}
|
||||
},
|
||||
setdesc() {
|
||||
|
@ -66,6 +66,7 @@
|
||||
:donotlocalize="true"
|
||||
:card="startGameCard"
|
||||
@click.native="startGame"
|
||||
style="margin-top: 36pt;"
|
||||
/>
|
||||
<div
|
||||
v-for="p in playersTable"
|
||||
@ -126,7 +127,7 @@
|
||||
@click.native="drawFromPlayer(p.name)"
|
||||
: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)
|
||||
}}</span>
|
||||
<div class="tiny-equipment">
|
||||
@ -186,6 +187,7 @@
|
||||
:donotlocalize="true"
|
||||
:card="shufflePlayersCard"
|
||||
@click.native="shufflePlayers"
|
||||
style="margin-top: 36pt;"
|
||||
class="fistful-of-cards"
|
||||
/>
|
||||
</transition-group>
|
||||
@ -803,13 +805,18 @@ export default {
|
||||
right: -35pt;
|
||||
transform: scale(0.45);
|
||||
transform-origin: 50% 0%;
|
||||
top: 10pt;
|
||||
top: 4pt;
|
||||
}
|
||||
.tiny-health {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
transform: scale(0.8);
|
||||
margin-bottom: -4pt;
|
||||
margin-top: -16pt;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.tiny-equipment .card {
|
||||
transform: rotate(2deg);
|
||||
@ -823,7 +830,7 @@ export default {
|
||||
}
|
||||
.tiny-character {
|
||||
position: absolute;
|
||||
transform: scale(0.5) translate(-80px, -40px);
|
||||
transform: translate(-30pt, -30pt) scale(0.5);
|
||||
top: 0;
|
||||
}
|
||||
.players-table {
|
||||
@ -888,6 +895,7 @@ export default {
|
||||
}
|
||||
.player-in-table {
|
||||
transition: all 0.2s ease-in-out;
|
||||
margin-top: 26pt;
|
||||
}
|
||||
.player-in-table:hover {
|
||||
transform: translateY(-5px) scale(1.05);
|
||||
|
Loading…
Reference in New Issue
Block a user