diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 7cce19e..ce84677 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -36,6 +36,8 @@ class Card(ABC): self.desc = desc self.need_target = False self.can_target_self = False + self.can_be_used_now = True + self.usable_next_turn = False self.need_with = False def __str__(self): @@ -284,7 +286,7 @@ class Mancato(Card): def __init__(self, suit, number): super().__init__(suit, 'Mancato!', number) self.icon = '๐ ' - self.desc = "Usa questa carta per annullare un bang" + self.desc = "Usa questa carta per annullare un Bang!" def play_card(self, player, against, _with=None): import bang.characters as chars diff --git a/backend/bang/deck.py b/backend/bang/deck.py index 015a841..e8aaf33 100644 --- a/backend/bang/deck.py +++ b/backend/bang/deck.py @@ -6,6 +6,10 @@ class Deck: def __init__(self, game): super().__init__() self.cards: List[cs.Card] = cs.get_starting_deck(game.expansions) + self.mancato_cards: List[str] = [] + for c in self.cards: + if isinstance(c, cs.Mancato) and c.name not in self.mancato_cards: + self.mancato_cards.append(c.name) self.game = game random.shuffle(self.cards) self.scrap_pile: List[cs.Card] = [] @@ -46,5 +50,7 @@ class Deck: return self.draw() def scrap(self, card: cs.Card): + if card.usable_next_turn: + card.can_be_used_now = False self.scrap_pile.append(card) self.game.notify_scrap_pile() diff --git a/backend/bang/expansions/dodge_city/cards.py b/backend/bang/expansions/dodge_city/cards.py index b5f57bc..a821ce5 100644 --- a/backend/bang/expansions/dodge_city/cards.py +++ b/backend/bang/expansions/dodge_city/cards.py @@ -31,7 +31,7 @@ class Schivata(Mancato): super().__init__(suit, number) self.name = 'Schivata' self.icon = '๐ โโ๏ธ' - self.desc += " e poi pesca una carta" + self.desc = "Usa questa carta per annullare un Bang! e poi pesca una carta" self.alt_text = 'โ๏ธ๐' def play_card(self, player, against, _with=None): @@ -69,9 +69,12 @@ class Rissa(CatBalou): def play_card(self, player, against, _with): if _with != None: + players_with_cards = [p.name for p in player.game.players if p != player and (len(p.hand)+len(p.equipment)) > 0] + if len(players_with_cards) == 0: + return False player.game.deck.scrap(_with) player.event_type = 'rissa' - super().play_card(player, against=[p.name for p in player.game.players if p != player and (len(p.hand)+len(p.equipment)) > 0][0]) + super().play_card(player, against=players_with_cards[0]) player.sio.emit('chat_message', room=player.game.name, data=f'{player.name} ha giocato {self.name}') return True return False @@ -130,6 +133,198 @@ class Whisky(Card): return True return False +class Bibbia(Schivata): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Bibbia' + self.icon = '๐' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + pass + return False + else: + player.equipment.append(self) + return True + + def use_card(self, player): + player.hand.append(player.game.deck.draw()) + player.notify_self() + +class Cappello(Mancato): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Cappello' + self.icon = '๐งข' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + pass + return False + else: + player.equipment.append(self) + return True + +class PlaccaDiFerro(Cappello): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Placca Di Ferro' + self.icon = '๐ก' + +class Sombrero(Cappello): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Sombrero' + self.icon = '๐' + +class Pugnale(Pugno): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Pugnale' + self.icon = '๐ก' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against=against) + else: + player.equipment.append(self) + return True + +class Derringer(Pugnale): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Derringer' + self.icon = '๐ฌ' + self.alt_text += ' โ๏ธ๐' + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + player.hand.append(player.game.deck.draw()) + return super().play_card(player, against=against) + else: + player.equipment.append(self) + return True + +class Borraccia(Birra): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Borraccia' + self.icon = '๐ผ' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against) + else: + player.equipment.append(self) + return True + +class PonyExpress(WellsFargo): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Pony Express' + self.icon = '๐ฆ' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against) + else: + player.equipment.append(self) + return True + +class Howitzer(Gatling): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Howitzer' + self.icon = '๐ก' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against) + else: + player.equipment.append(self) + return True + +class CanCan(CatBalou): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Can Can' + self.icon = '๐ฏโโ๏ธ' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against) + else: + player.equipment.append(self) + return True + +class Conestoga(Panico): + def __init__(self, suit, number): + Card.__init__(self, suit, 'Conestoga', number) + self.icon = '๐' + self.desc = "Ruba 1 carta dalla mano di un giocatore a prescindere dalla distanza" + self.need_target = True + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + return super().play_card(player, against) + else: + player.equipment.append(self) + return True + +class Pepperbox(Bang): + def __init__(self, suit, number): + super().__init__(suit, number) + self.name = 'Pepperbox' + self.icon = '๐ถ' + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + if against != None: + Card.play_card(self, player, against=against) + player.game.attack(player, against) + return True + return False + else: + player.equipment.append(self) + return True + +class FucileDaCaccia(Card): + def __init__(self, suit, number): + super().__init__(suit, 'Fucile Da Caccia', number) + self.icon = '๐' + self.desc = "Spara a un giocatore a prescindere dalla distanza" + self.need_target = True + self.usable_next_turn = True + self.can_be_used_now = False + + def play_card(self, player, against, _with=None): + if self.can_be_used_now: + if against != None: + super().play_card(player, against=against) + player.game.attack(player, against) + return True + return False + else: + player.equipment.append(self) + return True def get_starting_deck() -> List[Card]: return [ @@ -160,4 +355,18 @@ def get_starting_deck() -> List[Card]: SpringField(Suit.SPADES, 'K'), Tequila(Suit.CLUBS, 9), Whisky(Suit.HEARTS, 'Q'), + Bibbia(Suit.HEARTS, 10), + Cappello(Suit.DIAMONDS, 'J'), + PlaccaDiFerro(Suit.DIAMONDS, 'A'), + PlaccaDiFerro(Suit.SPADES, 'Q'), + Sombrero(Suit.CLUBS, 7), + Pugnale(Suit.HEARTS, 8), + Derringer(Suit.SPADES, 7), + Borraccia(Suit.HEARTS, 7), + CanCan(Suit.CLUBS, 'J'), + Conestoga(Suit.DIAMONDS, 9), + FucileDaCaccia(Suit.CLUBS, 'Q'), + PonyExpress(Suit.DIAMONDS, 'Q'), + Pepperbox(Suit.HEARTS, 'A'), + Howitzer(Suit.SPADES, 9), ] diff --git a/backend/bang/game.py b/backend/bang/game.py index 7c69cbe..b0679bb 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -23,13 +23,14 @@ class Game: self.expansions = [] def notify_room(self): - self.sio.emit('room', room=self.name, data={ - 'name': self.name, - 'started': self.started, - 'players': [{'name':p.name, 'ready': False} for p in self.players], - 'password': self.password, - 'expansions': self.expansions, - }) + if len([p for p in self.players if p.character == None]) != 0: + self.sio.emit('room', room=self.name, data={ + 'name': self.name, + 'started': self.started, + 'players': [{'name':p.name, 'ready': p.character != None} for p in self.players], + 'password': self.password, + 'expansions': self.expansions, + }) def toggle_expansion(self, expansion_name): if not self.started: diff --git a/backend/bang/players.py b/backend/bang/players.py index fd9162d..6b316e5 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -16,7 +16,6 @@ class PendingAction(IntEnum): WAIT = 4 CHOOSE = 5 - class Player: def __init__(self, name, sid, sio): @@ -250,22 +249,28 @@ class Player: return s def play_card(self, hand_index: int, against=None, _with=None): - if not (0 <= hand_index < len(self.hand)): - print('illegal') + if not self.is_my_turn or self.pending_action != PendingAction.PLAY: return - card: cs.Card = self.hand.pop(hand_index) + if not (0 <= hand_index < len(self.hand) + len(self.equipment)): + return + card: cs.Card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand)) withCard: cs.Card = None if _with != None: withCard = self.hand.pop(_with) if hand_index > _with else self.hand.pop(_with - 1) print(self.name, 'is playing ', card, ' against:', against, ' with:', _with) did_play_card = card.play_card(self, against, withCard) - if not card.is_equipment: + if not card.is_equipment and not card.usable_next_turn: if did_play_card: self.game.deck.scrap(card) else: self.hand.insert(hand_index, card) if withCard: self.hand.insert(_with, withCard) + elif card.usable_next_turn and card.can_be_used_now: + if did_play_card: + self.game.deck.scrap(card) + else: + self.equipment.insert(hand_index-len(self.hand), card) self.notify_self() def choose(self, card_index): @@ -280,6 +285,8 @@ class Player: card = target.hand.pop(card_index) target.notify_self() if self.choose_action == 'steal': + if card.usable_next_turn: + card.can_be_used_now = False self.hand.append(card) else: self.game.deck.scrap(card) @@ -289,10 +296,9 @@ class Player: self.choose_action = '' self.pending_action = PendingAction.PLAY else: - while len(self.game.players[self.game.players_map[self.target_p]+1].hand) + len(self.game.players[self.game.players_map[self.target_p]+1].equipment) == 0: + self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name + while self.target_p == self.name or len(self.game.players[self.game.players_map[self.target_p]].hand) + len(self.game.players[self.game.players_map[self.target_p]].equipment) == 0: self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name - if self.target_p == self.name: - self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name self.notify_self() # specifico per personaggio elif self.is_drawing and isinstance(self.character, chars.KitCarlson): @@ -321,19 +327,25 @@ class Player: if self.mancato_needed <= 0: self.game.responders_did_respond_resume_turn() return - if len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang))]) == 0: + if len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang))]) == 0\ + and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0: self.take_damage_response() self.game.responders_did_respond_resume_turn() else: self.pending_action = PendingAction.RESPOND - self.expected_response = [cs.Mancato(0, 0).name, csd.Schivata(0,0).name] + self.expected_response = self.game.deck.mancato_cards self.on_failed_response_cb = self.take_damage_response self.notify_self() def get_banged(self, attacker, double=False): self.attacker = attacker self.mancato_needed = 1 if not double else 2 - if len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang))]) == 0 and len([c for c in self.equipment if isinstance(c, cs.Barile)]) == 0 and not isinstance(self.character, chars.Jourdonnais): + for i in range(len(self.equipment)): + if self.equipment[i].can_be_used_now: + print('usable', self.equipment[i]) + if len([c for c in self.equipment if isinstance(c, cs.Barile)]) == 0 and not isinstance(self.character, chars.Jourdonnais)\ + and len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang))]) == 0\ + and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0: print('Cant defend') self.take_damage_response() return False @@ -345,7 +357,7 @@ class Player: else: print('has mancato') self.pending_action = PendingAction.RESPOND - self.expected_response = [cs.Mancato(0, 0).name, csd.Schivata(0,0).name] + self.expected_response = self.game.deck.mancato_cards self.on_failed_response_cb = self.take_damage_response self.notify_self() return True @@ -402,13 +414,16 @@ class Player: data=f'{self.name} ha usato una birra per recuperare una vita.') break self.mancato_needed = 0 + self.event_type = '' self.notify_self() self.attacker = None def respond(self, hand_index): self.pending_action = PendingAction.WAIT - if hand_index != -1 and self.hand[hand_index].name in self.expected_response: - card = self.hand.pop(hand_index) + if hand_index != -1 and ( + ((hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response)) or + self.equipment[hand_index-len(self.hand)].name in self.expected_response): + card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand)) card.use_card(self) self.game.deck.scrap(card) self.notify_self() @@ -465,6 +480,9 @@ class Player: f"I {self.name} have to many cards in my hand and I can't end the turn") else: self.is_my_turn = False + for i in range(len(self.equipment)): + if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now: + self.equipment[i].can_be_used_now = True self.pending_action = PendingAction.WAIT self.notify_self() self.game.next_turn() diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 9052697..f4e6f60 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -16,7 +16,7 @@
{{$t("online_players")}}{{onlinePlayers}}
-