From c9c6101eef96bec7f414920c40f91241e24de590 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 29 Jan 2023 15:45:26 +0000 Subject: [PATCH] add fuga and MickDefender --- backend/bang/cards.py | 12 +-- backend/bang/deck.py | 3 + .../expansions/the_valley_of_shadows/cards.py | 7 +- .../the_valley_of_shadows/characters.py | 2 +- backend/bang/game.py | 21 ++++- backend/bang/players.py | 85 ++++++++++++++----- 6 files changed, 90 insertions(+), 40 deletions(-) diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 64ee2a7..bb4b0ff 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -289,11 +289,7 @@ class CatBalou(Card): 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): super().play_card(player, against=against) - from bang.players import PendingAction - player.pending_action = PendingAction.CHOOSE - player.choose_action = 'discard' - player.target_p = against - print('choose now') + player.game.steal_discard(player, against, self) return True return False @@ -405,11 +401,7 @@ class Panico(Card): 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): super().play_card(player, against=against) - from bang.players import PendingAction - player.pending_action = PendingAction.CHOOSE - player.choose_action = 'steal' - player.target_p = against - print('choose now') + player.game.steal_discard(player, against, self) return True return False diff --git a/backend/bang/deck.py b/backend/bang/deck.py index 6172177..5c49d6d 100644 --- a/backend/bang/deck.py +++ b/backend/bang/deck.py @@ -12,11 +12,14 @@ class Deck: self.cards: List[cs.Card] = cs.get_starting_deck(game.expansions) self.mancato_cards: List[str] = [] self.mancato_cards_not_green_or_blue: List[str] = [] + self.green_cards: Set[str] = set() for c in self.cards: if isinstance(c, cs.Mancato) and c.name not in self.mancato_cards: self.mancato_cards.append(c.name) if not (c.usable_next_turn or c.is_equipment): self.mancato_cards_not_green_or_blue.append(c.name) + elif c.usable_next_turn: + self.green_cards.add(c.name) self.all_cards_str: List[str] = [] for c in self.cards: if c.name not in self.all_cards_str: diff --git a/backend/bang/expansions/the_valley_of_shadows/cards.py b/backend/bang/expansions/the_valley_of_shadows/cards.py index 85e4009..1c0b53e 100644 --- a/backend/bang/expansions/the_valley_of_shadows/cards.py +++ b/backend/bang/expansions/the_valley_of_shadows/cards.py @@ -186,10 +186,7 @@ class Fuga(Card): #comprende indiani gatling etc, ma solo se carte marroni, le c self.alt_text = "❌" def play_card(self, player, against, _with=None): - #TODO - # super().play_card(player, against=against) - # player.game.attack(player, against) - return True + return False class Poker(Card): def __init__(self, suit, number): @@ -228,7 +225,7 @@ def get_starting_deck() -> List[Card]: Sventagliata(Suit.SPADES, 2), # Salvo(Suit.HEARTS, 5), Bandidos(Suit.DIAMONDS,'Q'), # gli altri giocatori scelgono se scartare 2 carte o perdere 1 punto vita - # Fuga(Suit.HEARTS, 3), # evita l'effetto di carte marroni (tipo panico cat balou) di cui sei bersaglio + Fuga(Suit.HEARTS, 3), # evita l'effetto di carte marroni (tipo panico cat balou) di cui sei bersaglio # Mira(Suit.CLUBS, 6), Poker(Suit.HEARTS, 'J'), # tutti gli altri scartano 1 carta a scelta, se non ci sono assi allora pesca 2 RitornoDiFiamma(Suit.CLUBS, 'Q'), # un mancato che fa bang diff --git a/backend/bang/expansions/the_valley_of_shadows/characters.py b/backend/bang/expansions/the_valley_of_shadows/characters.py index 882566d..56d4347 100644 --- a/backend/bang/expansions/the_valley_of_shadows/characters.py +++ b/backend/bang/expansions/the_valley_of_shadows/characters.py @@ -80,7 +80,7 @@ def all_characters() -> List[Character]: # EvelynShebang(), # HenryBlock(), # LemonadeJim(), - # MickDefender(), + MickDefender(), TucoFranziskaner(), ] for c in cards: diff --git a/backend/bang/game.py b/backend/bang/game.py index 187cfec..da1430f 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -403,6 +403,23 @@ class Game: elif not attacker.is_my_turn: self.players[self.turn].pending_action = pl.PendingAction.PLAY + def steal_discard(self, attacker: pl.Player, target_username:str, card:cs.Card): + p = self.get_player_named(target_username) + if p != attacker and p.get_discarded(attacker, card_name=card.name, action='steal' if isinstance(card, cs.Panico) else 'discard'): + self.ready_count = 0 + self.waiting_for = 1 + attacker.pending_action = pl.PendingAction.WAIT + attacker.notify_self() + self.get_player_named(target_username).notify_self() + else: + attacker.pending_action = pl.PendingAction.CHOOSE + attacker.target_p = target_username + if isinstance(card, cs.CatBalou): + attacker.choose_action = 'discard' + elif isinstance(card, cs.Panico): + attacker.choose_action = 'steal' + attacker.notify_self() + def rimbalzo(self, attacker: pl.Player, target_username:str, card_index:int): if self.get_player_named(target_username).get_banged(attacker=attacker, no_dmg=True, card_index=card_index): self.ready_count = 0 @@ -456,7 +473,7 @@ class Game: {'name':nextPlayer.name,'cards': self.available_cards}, default=lambda o: o.__dict__)) nextPlayer.notify_self() - def get_player_named(self, name:str, next=False): + def get_player_named(self, name:str, next=False) -> pl.Player: if next: return self.players[(self.players_map[name]+1) % len(self.players)] return self.players[self.players_map[name]] @@ -520,6 +537,8 @@ class Game: print('attack completed, next attack') atk = self.attack_queue.pop(0) self.attack(atk[0], atk[1], atk[2], atk[3], skip_queue=True) + elif self.players[self.turn].pending_action == pl.PendingAction.CHOOSE: + self.players[self.turn].notify_self() else: self.players[self.turn].pending_action = pl.PendingAction.PLAY self.poker_on = False diff --git a/backend/bang/players.py b/backend/bang/players.py index c91e66e..a30e842 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -47,7 +47,6 @@ class PendingAction(IntEnum): CHOOSE = 5 class Player: - def is_admin(self): return self.discord_id in {'244893980960096266', '539795574019457034'} @@ -1116,18 +1115,53 @@ class Player: self.on_failed_response_cb = self.take_no_damage_response self.notify_self() - def get_discarded(self, attacker=None, card_name=None): - self.pending_action = PendingAction.CHOOSE - self.available_cards = self.hand.copy() - if card_name == 'Tornado': - self.choose_text = 'choose_tornado' - if card_name == 'Poker': - self.choose_text = 'choose_poker' - if card_name == 'Bandidos': - self.choose_text = 'choose_bandidos' - self.mancato_needed = min(2, len(self.hand)) - self.available_cards.append({'name': '-1hp', 'icon': '💔', 'noDesc': True}) - return True + def get_discarded(self, attacker=None, card_name=None, action=None): + if card_name in {'Tornado', 'Poker', 'Bandidos'}: + self.pending_action = PendingAction.CHOOSE + self.available_cards = self.hand.copy() + if card_name == 'Tornado': + self.choose_text = 'choose_tornado' + if card_name == 'Poker': + self.choose_text = 'choose_poker' + if card_name == 'Bandidos': + self.choose_text = 'choose_bandidos' + self.mancato_needed = min(2, len(self.hand)) + self.available_cards.append({'name': '-1hp', 'icon': '💔', 'noDesc': True}) + return True + else: + if self.can_escape(card_name) or self.character.check(self.game, tvosch.MickDefender): + self.pending_action = PendingAction.RESPOND + self.mancato_needed = 1 + self.attacker = attacker + self.attacking_card = card_name + self.expected_response = ['Fuga'] + if self.can_escape(with_mancato=True): + self.expected_response.append(cs.Mancato(0, 0).name) + if action == 'steal': + self.on_failed_response_cb = self.take_steal_response + else: + self.on_failed_response_cb = self.take_discard_response + self.notify_self() + return True + + def take_steal_response(self): + self.attacker.pending_action = PendingAction.CHOOSE + self.attacker.target_p = self.name + self.attacker.choose_text = 'steal' + self.take_no_damage_response() + + def take_discard_response(self): + self.attacker.pending_action = PendingAction.CHOOSE + self.attacker.target_p = self.name + self.attacker.choose_text = 'discard' + self.take_no_damage_response() + + def can_escape(self, card_name:str=None, with_mancato:bool=False): + if card_name == 'Bang!' or card_name in self.game.deck.green_cards: + return False + if any((isinstance(c, tvosc.Fuga) for c in self.hand)) and not with_mancato: + return True + return with_mancato and self.character.check(self.game, tvosch.MickDefender) def get_banged(self, attacker, double:bool=False, no_dmg:bool=False, card_index:int|None=None, card_name:str|None=None): self.attacker = attacker @@ -1149,7 +1183,7 @@ class Player: print('usable', self.equipment[i]) if not self.game.is_competitive and not any((isinstance(c, cs.Barile) for c in self.equipment)) and not self.character.check(self.game, chars.Jourdonnais)\ and not any(((isinstance(c, cs.Mancato) and c.can_be_used_now) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente) for c in self.hand))\ - and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)): + and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)) and not self.can_escape(card_name): print('Cant defend') if not no_dmg: self.take_damage_response() @@ -1174,6 +1208,8 @@ class Player: self.expected_response = self.game.deck.mancato_cards_not_green_or_blue.copy() if self.character.check(self.game, chars.CalamityJanet) and cs.Bang(0, 0).name not in self.expected_response: self.expected_response.append(cs.Bang(0, 0).name) + if self.can_escape(card_name, with_mancato=False): + self.expected_response.append(tvosc.Fuga(0, 0).name) if not no_dmg: self.on_failed_response_cb = self.take_damage_response else: @@ -1194,15 +1230,15 @@ class Player: self.attacker = attacker self.attacking_card = "Indiani!" if self.character.check(self.game, chd.ApacheKid) or any((isinstance(c, grc.Calumet) for c in self.gold_rush_equipment)): return False - if not self.game.is_competitive and not any((isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand)): + if not self.game.is_competitive and not any((isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand)) and not self.can_escape(): print('Cant defend') self.take_damage_response() return False else: print('has bang') self.pending_action = PendingAction.RESPOND - self.expected_response = [cs.Bang(0, 0).name] - if self.character.check(self.game, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response: + self.expected_response = [cs.Bang(0, 0).name, tvosc.Fuga(0, 0).name] + if (self.character.check(self.game, chars.CalamityJanet) or self.can_escape(with_mancato=True)) and cs.Mancato(0, 0).name not in self.expected_response: self.expected_response.append(cs.Mancato(0, 0).name) self.event_type = 'indians' self.on_failed_response_cb = self.take_damage_response @@ -1211,15 +1247,15 @@ class Player: def get_dueled(self, attacker): self.attacker = attacker self.attacking_card = "Duello" - if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and not any((isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand))): + if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and not any((isinstance(c, cs.Bang) or isinstance(c, tvosc.Fuga) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand))): print('Cant defend') self.take_damage_response() self.game.responders_did_respond_resume_turn(did_lose=True) return False else: self.pending_action = PendingAction.RESPOND - self.expected_response = [cs.Bang(0, 0).name] - if self.character.check(self.game, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response: + self.expected_response = [cs.Bang(0, 0).name, tvosc.Fuga(0, 0).name] + if (self.character.check(self.game, chars.CalamityJanet) or self.can_escape(with_mancato=True)) and cs.Mancato(0, 0).name not in self.expected_response: self.expected_response.append(cs.Mancato(0, 0).name) self.event_type = 'duel' self.on_failed_response_cb = self.take_damage_response @@ -1315,9 +1351,12 @@ class Player: self.game.attack(self, self.attacker.name, card_name=card.name) if self.mancato_needed <= 0: if self.event_type == 'duel': - self.game.duel(self, self.attacker.name) - if self.character.check(self.game, chd.MollyStark) and hand_index < len(self.hand) and not self.is_my_turn: - self.molly_discarded_cards += 1 + if isinstance(card, tvosc.Fuga) or (isinstance(card, cs.Mancato) and self.can_escape(with_mancato=True)): + self.game.responders_did_respond_resume_turn(did_lose=False) + else: + self.game.duel(self, self.attacker.name) + if self.character.check(self.game, chd.MollyStark) and hand_index < len(self.hand) and not self.is_my_turn: + self.molly_discarded_cards += 1 else: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn: for i in range(self.molly_discarded_cards):