from __future__ import annotations from enum import IntEnum import json from random import random, randrange, sample, uniform, randint import socketio import bang.deck as deck import bang.roles as r import bang.cards as cs import bang.expansions.dodge_city.cards as csd import bang.characters as chars import bang.expansions.dodge_city.characters as chd import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.high_noon.card_events as ceh import bang.expansions.wild_west_show.card_events as cew import bang.expansions.wild_west_show.characters as chw import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.characters as grch import bang.expansions.the_valley_of_shadows.cards as tvosc import bang.expansions.the_valley_of_shadows.characters as tvosch from typing import List, TYPE_CHECKING from metrics import Metrics from globals import G import sys if TYPE_CHECKING: from bang.game import Game robot_pictures = [ "https://i.imgur.com/40rAFIb.jpg", "https://i.imgur.com/gG77VRR.jpg", "https://i.imgur.com/l2DTQeH.jpg", "https://i.imgur.com/aPM2gix.jpg", "https://i.imgur.com/ep5EB8c.jpg", "https://i.imgur.com/qsOWIsf.jpg", "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/apple/325/robot_1f916.png", "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/openmoji/338/robot_1f916.png", "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/microsoft/319/robot_1f916.png", "https://i.imgur.com/0plPBZR.png", "https://i.imgur.com/DScxoSX.png", "https://i.imgur.com/YzPQChj.png", "https://i.imgur.com/v3BvnX4.png", "https://i.imgur.com/1XHsE9f.png", "https://i.imgur.com/q0CrH2c.png", "https://i.imgur.com/Z5vXgd4.png", ] class PendingAction(IntEnum): PICK = 0 DRAW = 1 PLAY = 2 RESPOND = 3 WAIT = 4 CHOOSE = 5 class Player: def is_admin(self): return self.discord_id in {"244893980960096266", "539795574019457034"} def _get_avatar(self): import requests headers = { "Authorization": "Bearer " + self.discord_token, } r = requests.get("https://discordapp.com/api/users/@me", headers=headers) if r.status_code == 200: res = r.json() print(res) if res["avatar"] is None: self.avatar = robot_pictures[randrange(len(robot_pictures))] else: self.avatar = f'https://cdn.discordapp.com/avatars/{res["id"]}/{res["avatar"]}.png' if self.game: G.sio.emit( "chat_message", room=self.game.name, data=f'_change_username|{self.name}|{res["username"]}', ) self.name = res["username"] self.discord_id = res["id"] if self.is_admin(): if self.game: self.game.feature_flags() G.sio.emit( "chat_message", room=self.sid, data={"color": "green", "text": "(you are admin)"}, ) if self.game: self.game.notify_room() G.sio.emit("me", data=self.name, room=self.sid) else: print("error getting avatar", r.status_code, r.text) print(r) def __init__(self, name, sid, bot=False, discord_token=None): super().__init__() self.name = name self.sid = sid self.is_bot = bot self.discord_token = discord_token self.discord_id = None self.played_cards = 0 self.avatar = "" self.last_played_card: cs.Card = None if self.is_bot: self.avatar = robot_pictures[randrange(len(robot_pictures))] if self.discord_token: G.sio.start_background_task(self._get_avatar) self.game: Game = None self.reset() def reset(self): self.hand: List[cs.Card] = [] self.equipment: List[cs.Card] = [] self.role: r.Role = None self.character: chars.Character = None self.real_character: chars.Character = None self.is_using_checchino = False self.last_played_card = None self.lives = 0 self.max_lives = 0 self.is_my_turn = False self.is_waiting_for_action = True self.has_played_bang = False self.can_play_ranch = True self.is_playing_ranch = False self.pending_action: PendingAction = None self.available_characters: List[chars.Character] = [] self.was_shot = False self.on_pick_cb = None self.on_failed_response_cb = None self.event_type: str = None self.expected_response = [] self.attacking_card = None self.attacker: Player = None self.target_p: str = None self.is_drawing = False self.special_use_count = 0 self.rissa_targets = [] self.committed_suit_manette = None self.not_chosen_character = None try: # pylint: disable=no-member del self.win_status except: pass self.mancato_needed = 0 self.molly_discarded_cards = 0 self.is_dead = False self.is_ghost = False self.death_turn = 0 self.noStar = False self.can_play_vendetta = True self.can_play_again_don_bell = True self.is_giving_life = False self.choose_text = "choose_card_to_get" self.using_rimbalzo = 0 # 0 no, 1 scegli giocatore, 2 scegli carta self.bang_used = 0 self.gold_nuggets = 0 self.gold_rush_equipment: List[grc.ShopCard] = [] self.was_player = False self.setaccio_count = 0 self.choose_action = "" def join_game(self, game): self.game = game print(f"I {self.name} joined {self.game}") def disconnect(self): if ( self.is_admin() and self.game.debug and self.game.started and getattr(sys, "gettrace", None)() ): return False return self.game.handle_disconnect(self) def set_role(self, role: r.Role): self.role = role print(f'{self.name}: I am a {role.name}, my goal is "{role.goal}"') G.sio.emit( "role", room=self.sid, data=json.dumps(role, default=lambda o: o.__dict__) ) def set_character(self, character: str): print(self.available_characters, character) if self.character is None: try: self.character = next( x for x in self.available_characters if x.name == character ) except: # fix for wrong character encoding in the first part of some characters like Jose delgrado self.character = next( x for x in self.available_characters if x.name.split()[1] == character.split()[1] ) if "high_noon" in self.game.expansions: # questo viene utilizzato per la carta nuova identità self.not_chosen_character = next( x for x in self.available_characters if x.name != character ) else: self.not_chosen_character = None self.real_character = self.character self.available_characters = [] print(f"{self.name}: I chose character {self.character.name}") G.sio.emit( "chat_message", room=self.game.name, data=f"_did_choose_character|{self.name}", ) self.game.notify_character_selection() elif self.real_character and isinstance(self.real_character, chd.VeraCuster): self.character = next( x for x in self.available_characters if x.name == character ) self.available_characters = [] G.sio.emit( "chat_message", room=self.game.name, data=f"_did_choose_character|{self.name}", ) self.pending_action = PendingAction.DRAW self.notify_self() def prepare(self): self.max_lives = self.character.max_lives + self.role.health_mod self.lives = self.max_lives self.hand = [] self.equipment = [] self.pending_action = PendingAction.WAIT self.noStar = self.game.initial_players == 3 def set_available_character(self, available): self.available_characters = available print(f"{self.name}: I have to choose between {available}") if not self.is_bot: G.sio.emit( "characters", room=self.sid, data=json.dumps(available, default=lambda o: o.__dict__), ) else: char_name = available[randrange(0, len(available))].name self.game.rpc_log.append(f"{self.name};set_character;{char_name}") self.set_character(char_name) def notify_card(self, player, card, message=""): try: card = card.__dict__ except: pass mess = {"player": player.name, "card": card, "message": message} print("notifying card") G.sio.emit("notify_card", room=self.sid, data=mess) def notify_self(self): if any((True for c in self.equipment if isinstance(c, tvosc.Fantasma))): self.is_ghost = True elif ( self.is_ghost and self.is_my_turn and not self.game.check_event(ceh.CittaFantasma) ): self.end_turn(forced=True) elif self.is_ghost and not self.game.check_event(ceh.CittaFantasma): self.is_ghost = False for i in range(len(self.hand)): self.game.deck.scrap(self.hand.pop(), True) for i in range(len(self.equipment)): self.game.deck.scrap(self.equipment.pop(), True) if self.is_ghost: self.lives = 0 if self.pending_action == PendingAction.DRAW and self.game.check_event( ce.Peyote ): self.available_cards = [ {"icon": "🔴", "noDesc": True}, {"icon": "⚫", "noDesc": True}, ] self.is_drawing = True self.choose_text = "choose_guess" self.pending_action = PendingAction.CHOOSE elif ( self.can_play_ranch and self.pending_action == PendingAction.PLAY and self.game.check_event(ce.Ranch) ): self.can_play_ranch = False self.available_cards = [c for c in self.hand] self.discarded_cards = [] self.available_cards.append({"icon": "✅", "noDesc": True}) self.is_playing_ranch = True self.choose_text = "choose_ranch" self.pending_action = PendingAction.CHOOSE elif ( self.character and self.character.check(self.game, chars.SuzyLafayette) and self.lives > 0 and len(self.hand) == 0 and (not self.is_my_turn or self.pending_action == PendingAction.PLAY) ): self.game.deck.draw(True, player=self) if self.lives <= 0 and self.max_lives > 0 and not self.is_dead: print("dying, attacker", self.attacker) if self.gold_nuggets >= 2 and any( (isinstance(c, grc.Zaino) for c in self.gold_rush_equipment) ): for i in range(len(self.gold_rush_equipment)): if isinstance(self.gold_rush_equipment[i], grc.Zaino): self.gold_rush_equipment[i].play_card(self, None) return # play card will notify the player if self.character.check(self.game, chw.TerenKill): picked: cs.Card = self.game.deck.pick_and_scrap() G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if not picked.check_suit(self.game, [cs.Suit.SPADES]): self.lives = 1 self.game.deck.draw(True, player=self) if ( self.character.check(self.game, chars.SidKetchum) and len(self.hand) > 1 and self.lives == 0 ): if self.game.players[self.game.turn] != self: self.game.players[ self.game.turn ].pending_action = PendingAction.WAIT self.game.players[self.game.turn].notify_self() self.scrapped_cards = 0 self.previous_pending_action = self.pending_action self.pending_action = PendingAction.CHOOSE self.choose_text = "choose_sid_scrap" self.available_cards = self.hand self.lives += 1 ser = self.__dict__.copy() ser.pop("game") ser.pop("sid") ser.pop("on_pick_cb") ser.pop("discord_token") ser.pop("on_failed_response_cb") ser.pop("attacker") ser.pop("rissa_targets") if self.attacker: ser["attacker"] = self.attacker.name ser["sight"] = self.get_sight() ser["sight_extra"] = self.get_sight(False) - 1 ser["can_gold_rush_discard"] = any( ( p != self and any( (e.number <= self.gold_nuggets + 1 for e in p.gold_rush_equipment) ) for p in self.game.get_alive_players() ) ) if self.character: ser["gold_rush_discount"] = ( 1 if self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1 else 0 ) ser["lives"] = max(ser["lives"], 0) if self.lives <= 0 and self.max_lives > 0 and not self.is_dead: self.pending_action = PendingAction.WAIT ser["hand"] = [] ser["equipment"] = [] G.sio.emit( "self", room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__), ) self.game.player_death(self) if ( self.game and self.game.started ): # falso quando un bot viene eliminato dalla partita G.sio.emit( "self_vis", room=self.sid, data=json.dumps( self.game.get_visible_players(self), default=lambda o: o.__dict__ ), ) self.game.notify_all() G.sio.emit( "self", room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__) ) def bot_spin(self): while self.is_bot and self.game is not None and not self.game.shutting_down: G.sio.sleep( max(0.2, uniform(self.game.bot_speed / 2 - 0.1, self.game.bot_speed)) ) if self.lives > 0 or self.is_ghost: self.bot_logic() def bot_logic(self): if self.game is None or self.game.shutting_down: return if ( self.pending_action is not None and self.pending_action != PendingAction.WAIT ): # eventlet.sleep(uniform(self.game.bot_speed/2-0.1, self.game.bot_speed)) pass else: return if self.pending_action == PendingAction.PICK: self.game.rpc_log.append(f"{self.name};pick;") self.pick() elif self.pending_action == PendingAction.DRAW: self.game.rpc_log.append(f"{self.name};draw;") self.draw("") elif self.pending_action == PendingAction.PLAY: non_blocked_cards = [ card for card in self.hand if ( not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette ) and ( not self.game.check_event(ce.IlGiudice) or not card.usable_next_turn ) ] equippables = [ c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not c.need_target and not any( ( type(c) == type(x) and not (c.is_weapon and c.must_be_used) for x in self.equipment ) ) ] misc = [ c for c in non_blocked_cards if not c.need_target and ( isinstance(c, cs.WellsFargo) or isinstance(c, cs.Indiani) or isinstance(c, cs.Gatling) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or ( ( isinstance(c, cs.Birra) and self.lives < self.max_lives or c.must_be_used ) and not self.game.check_event(ceh.IlReverendo) ) or ( c.need_with and len(self.hand) > 1 and not ( isinstance(c, csd.Whisky) and self.lives == self.max_lives ) ) ) and not (not c.can_be_used_now and self.game.check_event(ce.IlGiudice)) and not c.is_equipment ] need_target = [ c for c in non_blocked_cards if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not ( type(c) == type(cs.Bang) and ( self.game.check_event(ceh.Sermone) or ( self.has_played_bang and ( not any( (isinstance(c, cs.Volcanic) for c in self.equipment) ) or self.game.check_event(ce.Lazo) ) ) ) ) and not ( isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice) ) or isinstance(c, cs.Duello) or isinstance(c, cs.CatBalou) or isinstance(c, csd.Pugno) ] green_cards = [ c for c in self.equipment if not self.game.check_event(ce.Lazo) and not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now ] if self.game.debug: print(f"hand: {self.hand}") print(f"non_blocked: {non_blocked_cards}") print(f"equippables: {equippables}") print(f"misc: {misc}") print(f"need_target: {need_target}") print(f"green_cards: {green_cards}") if self.gold_nuggets > 0 and any( (c.number <= self.gold_nuggets for c in self.game.deck.shop_cards) ): for i in range(len(self.game.deck.shop_cards)): if self.game.deck.shop_cards[i].number <= self.gold_nuggets: self.game.rpc_log.append(f"{self.name};buy_gold_rush_card;{i}") self.buy_gold_rush_card(i) return if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice): for card in equippables: if ( isinstance(card, tvosc.Fantasma) and len(self.game.get_dead_players(include_ghosts=False)) == 0 ): continue if self.play_card(self.hand.index(card)): return elif len(misc) > 0: for card in misc: if ( card.need_with and len(self.hand) > 1 and self.play_card( self.hand.index(card), _with=sample( [ j for j in range(len(self.hand)) if j != self.hand.index(card) ], 1, )[0], ) ): return elif self.play_card(self.hand.index(card)): return elif len(need_target) > 0: for card in need_target: _range = ( self.get_sight() if card.name == "Bang!" or card.name == "Pepperbox" else card.range ) others = [ p for p in self.game.get_visible_players(self) if _range >= p["dist"] and not ( isinstance(self.role, r.Vice) and p["is_sheriff"] and not card.must_be_used ) and p["lives"] > 0 and not ( ( isinstance(card, cs.CatBalou) or isinstance(card, cs.Panico) ) and p["cards"] == 0 ) and not (p["is_sheriff"] and isinstance(card, cs.Prigione)) ] if ( isinstance(card, cs.Panico) or isinstance(card, cs.Panico) ) and any( isinstance(c, tvosc.SerpenteASonagli) or isinstance(c, cs.Prigione) for c in self.equipment ) > 0: others.append( { "name": self.name, "is_sheriff": isinstance(self.role, r.Sheriff), } ) if len(others) == 0 or card not in self.hand: continue target = others[randrange(0, len(others))] if target["is_sheriff"] and isinstance(self.role, r.Renegade): target = others[randrange(0, len(others))] if not card.need_with: if self.play_card( self.hand.index(card), against=target["name"] ): return elif len(self.hand) > 1: if self.play_card( self.hand.index(card), against=target["name"], _with=sample( [ j for j in range(len(self.hand)) if j != self.hand.index(card) ], 1, )[0], ): return elif len(green_cards) > 0: for card in green_cards: if ( not isinstance(card, cs.Mancato) and card.usable_next_turn and card.can_be_used_now ): if not card.need_target: if self.play_card( len(self.hand) + self.equipment.index(card) ): return else: _range = ( self.get_sight() if card.name == "Pepperbox" else card.range ) others = [ p for p in self.game.get_visible_players(self) if _range >= p["dist"] and not ( isinstance(self.role, r.Vice) and p["is_sheriff"] ) ] if len(others) == 0: continue target = others[randrange(0, len(others))] if target["is_sheriff"] and isinstance( self.role, r.Renegade ): target = others[randrange(0, len(others))] if self.play_card( len(self.hand) + self.equipment.index(card), against=target["name"], ): return break maxcards = ( self.lives if not self.character.check(self.game, chd.SeanMallory) else 10 ) if maxcards == self.lives and any( (isinstance(c, grc.Cinturone) for c in self.gold_rush_equipment) ): maxcards = 8 if len(self.hand) > maxcards: self.game.rpc_log.append(f"{self.name};scrap;{0}") self.scrap(0) else: self.game.rpc_log.append(f"{self.name};end_turn") self.end_turn() elif self.pending_action == PendingAction.RESPOND: did_respond = False for i in range(len(self.hand)): if self.hand[i].can_be_used_now and ( self.hand[i].name in self.expected_response or self.character.check(self.game, chd.ElenaFuente) ): self.game.rpc_log.append(f"{self.name};respond;{i}") self.respond(i) did_respond = True break for i in range(len(self.equipment)): if ( not self.game.check_event(ce.Lazo) and self.equipment[i].name in self.expected_response ): self.game.rpc_log.append(f"{self.name};respond;{len(self.hand)+i}") self.respond(len(self.hand) + i) did_respond = True break if not did_respond: self.game.rpc_log.append(f"{self.name};respond;{-1}") self.respond(-1) elif self.pending_action == PendingAction.CHOOSE: if not self.target_p: card_index = randrange(0, len(self.available_cards)) self.game.rpc_log.append(f"{self.name};choose;{card_index}") self.choose(card_index) else: target = self.game.get_player_named(self.target_p) if len(target.hand) + len(target.equipment) == 0: self.pending_action = PendingAction.PLAY self.notify_self() else: try: card_index = randrange( 0, len(target.hand) + len(target.equipment) ) self.choose(card_index) self.game.rpc_log.append(f"{self.name};choose;{card_index}") except: self.choose(0) self.game.rpc_log.append(f"{self.name};choose;{0}") def play_turn(self, can_play_vendetta=True, again=False): if ((self.lives == 0 or self.is_dead) and not self.is_ghost) or ( self.is_ghost and any(isinstance(c, tvosc.Fantasma) for c in self.equipment) and self.game.check_event(ce.Lazo) ): return self.end_turn(forced=True) self.scrapped_cards = 0 self.setaccio_count = 0 self.played_cards = 0 self.can_play_ranch = True self.is_playing_ranch = False self.last_played_card = None self.can_play_vendetta = can_play_vendetta if not again: G.sio.emit("chat_message", room=self.game.name, data=f"_turn|{self.name}") print(f"{self.name}: I was notified that it is my turn") self.was_shot = False self.attacker = None self.is_my_turn = True self.is_waiting_for_action = True self.has_played_bang = False self.special_use_count = 0 self.bang_used = 0 if self.game.check_event(cew.DarlingValentine): hand = len(self.hand) for _ in range(hand): self.game.deck.scrap(self.hand.pop(0), True, player=self) self.game.deck.draw(True, player=self) if self.game.check_event(ceh.MezzogiornoDiFuoco): self.lives -= 1 if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)): self.gold_nuggets += 1 if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): self.game.deck.draw(True, player=self) if self.character.check(self.game, chars.BartCassidy) and self.lives > 0: self.game.deck.draw(True, player=self) G.sio.emit( "chat_message", room=self.game.name, data=f"_special_bart_cassidy|{self.name}", ) self.heal_if_needed() if self.lives <= 0: return self.notify_self() # non è un elif perchè vera custer deve fare questo poi cambiare personaggio if ( self.game.check_event(ce.FratelliDiSangue) and self.lives > 1 and not self.is_giving_life and sum( p != self and p.lives < p.max_lives for p in self.game.get_alive_players() ) ): self.available_cards = [ { "name": p.name, "icon": p.role.icon if (self.game.initial_players == 3) else "⭐️" if isinstance(p.role, r.Sheriff) else "🤠", "alt_text": "".join(["❤️"] * p.lives) + "".join(["💀"] * (p.max_lives - p.lives)), "avatar": p.avatar, "is_character": True, "is_player": True, } for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives ] self.available_cards.append({"icon": "❌", "noDesc": True}) self.choose_text = "choose_fratelli_di_sangue" self.pending_action = PendingAction.CHOOSE self.is_giving_life = True elif ( self.game.check_event(ceh.NuovaIdentita) and self.not_chosen_character is not None and not again ): self.available_cards = [self.character, self.not_chosen_character] self.choose_text = "choose_nuova_identita" self.pending_action = PendingAction.CHOOSE elif not self.game.check_event(ce.Lazo) and any( ( isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) or isinstance(c, tvosc.SerpenteASonagli) for c in self.equipment ) ): self.is_giving_life = False self.pending_action = PendingAction.PICK else: self.is_giving_life = False if isinstance(self.real_character, chd.VeraCuster): self.set_available_character( [p.character for p in self.game.get_alive_players() if p != self] ) else: self.pending_action = PendingAction.DRAW self.notify_self() def draw(self, pile): if ( self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == "event" and self.game.check_event(ce.Cecchino) and sum((c.name == cs.Bang(0, 0).name for c in self.hand)) >= 2 ): self.is_using_checchino = True self.available_cards = [ { "name": p["name"], "icon": p["role"].icon if (self.game.initial_players == 3) else "⭐️" if p["is_sheriff"] else "🤠", "alt_text": "".join(["❤️"] * p["lives"]) + "".join(["💀"] * (p["max_lives"] - p["lives"])), "is_character": True, "is_player": True, } for p in self.game.get_visible_players(self) if p["dist"] <= self.get_sight() ] self.available_cards.append({"icon": "❌", "noDesc": True}) self.choose_text = "choose_cecchino" self.pending_action = PendingAction.CHOOSE self.notify_self() if ( self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == "event" ): self.game.deck.event_cards[0].on_clicked(self.game, self) elif ( self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == "event_wildwestshow" ): self.game.deck.event_cards_wildwestshow[0].on_clicked(self.game, self) if self.pending_action != PendingAction.DRAW: return if ( pile == "event" and self.lives < self.max_lives and self.game.check_event(ce.LiquoreForte) ): self.lives += 1 self.pending_action = PendingAction.PLAY self.notify_self() elif self.character.check(self.game, chars.KitCarlson) and not self.is_ghost: self.is_drawing = True self.available_cards = [self.game.deck.draw() for i in range(3)] self.choose_text = "choose_card_to_get" self.pending_action = PendingAction.CHOOSE self.notify_self() elif self.character.check(self.game, grch.DutchWill) and not self.is_ghost: self.is_drawing = True self.available_cards = [self.game.deck.draw() for i in range(2)] self.choose_text = "choose_card_to_get" self.pending_action = PendingAction.CHOOSE self.notify_self() elif ( self.character.check(self.game, chd.PatBrennan) and type(pile) == str and pile != self.name and pile in self.game.players_map and len(self.game.get_player_named(pile).equipment) > 0 ): self.is_drawing = True self.available_cards = self.game.get_player_named(pile).equipment self.pat_target = pile self.choose_text = "choose_card_to_get" self.pending_action = PendingAction.CHOOSE self.notify_self() else: self.pending_action = PendingAction.PLAY if self.character.check(self.game, chw.YoulGrinner): hsize = len(self.hand) for p in self.game.get_alive_players(): if p != self and len(p.hand) > hsize: G.sio.emit( "card_drawn", room=self.game.name, data={"player": self.name, "pile": p.name}, ) self.hand.append(p.hand.pop(randint(0, len(p.hand) - 1))) num = ( 2 if not self.character.check(self.game, chd.BillNoface) else self.max_lives - self.lives + 1 ) if self.character.check(self.game, chd.PixiePete): num += 1 if self.character.check(self.game, tvosch.TucoFranziskaner) and not any( (True for c in self.equipment if not c.usable_next_turn) ): num += 2 if any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)): num += 1 if self.game.check_event(ceh.IlTreno) or ( self.is_ghost and self.game.check_event(ceh.CittaFantasma) ): num += 1 elif self.game.check_event(ceh.Sete): num -= 1 for i in range(num): if ( i == 0 and pile == "scrap" and self.character.check(self.game, chars.PedroRamirez) ): self.hand.append(self.game.deck.draw_from_scrap_pile()) G.sio.emit( "chat_message", room=self.game.name, data=f"_draw_from_scrap|{self.name}", ) elif ( i == 0 and type(pile) == str and pile != self.name and pile in self.game.players_map and self.character.check(self.game, chars.JesseJones) and len(self.game.get_player_named(pile).hand) > 0 ): self.hand.append( self.game.get_player_named(pile).hand.pop( randrange(0, len(self.game.get_player_named(pile).hand)) ) ) self.game.get_player_named(pile).notify_self() G.sio.emit( "chat_message", room=self.game.name, data=f"_draw_from_player|{self.name}|{pile}", ) elif i == 1: card: cs.Card = self.game.deck.draw() if self.character.check( self.game, chars.BlackJack ) or self.game.check_event(ce.LeggeDelWest): for p in self.game.get_alive_players(): if p != self: p.notify_card( self, card, "blackjack_special" if self.character.check(self.game, chars.BlackJack) else "foc.leggedelwest", ) if self.game.check_event(ce.LeggeDelWest): card.must_be_used = True if self.character.check( self.game, chars.BlackJack ) and card.check_suit( self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS] ): self.game.deck.draw(player=self) self.hand.append(card) G.sio.emit( "card_drawn", room=self.game.name, data={"player": self.name, "pile": pile}, ) else: self.game.deck.draw(player=self) self.manette() self.notify_self() def manette(self): if self.game.check_event(ceh.Manette): self.choose_text = "choose_manette" self.available_cards = [ {"name": "", "icon": "♦♣♥♠"[s], "alt_text": "", "noDesc": True} for s in [0, 1, 2, 3] ] self.pending_action = PendingAction.CHOOSE def pick(self): if self.pending_action != PendingAction.PICK: return pickable_cards = 1 + self.character.pick_mod if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)): pickable_cards += 1 if self.is_my_turn and self.attacker is None: for i in range(len(self.equipment)): if i < len(self.equipment) and isinstance( self.equipment[i], cs.Dinamite ): while pickable_cards > 0: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f"Did pick {picked}") G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if ( picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0 ): self.lives -= 3 if any( ( isinstance(c, grc.Talismano) for c in self.gold_rush_equipment ) ): self.gold_nuggets += 3 if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 3 if any( ( isinstance(c, grc.Stivali) for c in self.gold_rush_equipment ) ): self.game.deck.draw(player=self) self.game.deck.draw(player=self) self.game.deck.draw(player=self) self.attacker = None self.game.deck.scrap( self.equipment.pop(i), True, player=self ) G.sio.emit( "chat_message", room=self.game.name, data=f"_explode|{self.name}", ) self.heal_if_needed() if ( self.character.check(self.game, chars.BartCassidy) and self.lives > 0 ): for i in range(3): self.game.deck.draw(True, player=self) G.sio.emit( "chat_message", room=self.game.name, data=f"_special_bart_cassidy|{self.name}", ) print(f"{self.name} Boom, -3 hp") break else: self.game.next_player().equipment.append( self.equipment.pop(i) ) self.game.next_player().notify_self() break if any( ( isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) or isinstance(c, tvosc.SerpenteASonagli) for c in self.equipment ) ): self.notify_self() return for i in range(len(self.equipment)): if isinstance(self.equipment[i], cs.Prigione): while pickable_cards > 0: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f"Did pick {picked}") G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if ( not picked.check_suit(self.game, [cs.Suit.HEARTS]) and pickable_cards == 0 ): self.game.deck.scrap( self.equipment.pop(i), True, player=self ) G.sio.emit( "chat_message", room=self.game.name, data=f"_prison_turn|{self.name}", ) self.end_turn(forced=True) return elif pickable_cards == 0: self.game.deck.scrap( self.equipment.pop(i), True, player=self ) G.sio.emit( "chat_message", room=self.game.name, data=f"_prison_free|{self.name}", ) break break for i in range(len(self.equipment)): if isinstance(self.equipment[i], tvosc.SerpenteASonagli): while pickable_cards > 0: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f"Did pick {picked}") G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if not picked.check_suit(self.game, [cs.Suit.SPADES]): break elif pickable_cards == 0: self.lives -= 1 G.sio.emit( "chat_message", room=self.game.name, data=f"_snake_bit|{self.name}", ) if self.character.check(self.game, chars.BartCassidy): G.sio.emit( "chat_message", room=self.game.name, data=f"_special_bart_cassidy|{self.name}", ) self.game.deck.draw(True, player=self) if any( ( isinstance(c, grc.Talismano) for c in self.gold_rush_equipment ) ): self.gold_nuggets += 1 if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any( ( isinstance(c, grc.Stivali) for c in self.gold_rush_equipment ) ): self.game.deck.draw(True, player=self) break if any((isinstance(c, cs.Prigione) for c in self.equipment)): self.notify_self() # TODO perchè solo le prigioni? e multiple dinamiti come si comportano con veracuster? return if isinstance(self.real_character, chd.VeraCuster): self.set_available_character( [p.character for p in self.game.get_alive_players() if p != self] ) else: self.pending_action = PendingAction.DRAW self.notify_self() else: self.pending_action = PendingAction.WAIT self.on_pick_cb() def get_playable_cards(self): playable_cards = [] for i in range(len(self.hand)): card = self.hand[i] if ( isinstance(card, cs.Bang) and self.has_played_bang and not any((isinstance(c, cs.Volcanic) for c in self.equipment)) ): continue elif isinstance(card, cs.Birra) and self.lives >= self.max_lives: continue else: playable_cards.append(i) return playable_cards def play_card(self, hand_index: int, against=None, _with=None): if self.is_bot: data = {"index": hand_index, "against": against, "with": _with} self.game.rpc_log.append(f"{self.name};play_card;{json.dumps(data)}") print( self.name, "wants to play card ", hand_index, " against:", against, " with:", _with, ) if ( not self.is_my_turn or self.pending_action != PendingAction.PLAY or self.game.is_handling_death ): print("but cannot") return if not ( 0 <= hand_index < len(self.hand) + len(self.equipment) + len(self.gold_rush_equipment) ): print("but the card index is out of range") return elif len(self.hand) + len(self.equipment) <= hand_index < len(self.hand) + len( self.equipment ) + len(self.gold_rush_equipment) and len(self.gold_rush_equipment): print("which is a gold rush black card") card: grc.ShopCard = self.gold_rush_equipment[ hand_index - len(self.hand) - len(self.equipment) ] return card.play_card(self) from_hand = hand_index < len(self.hand) 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 is not 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 = False event_blocks_card = ( ( self.game.check_event(ce.IlGiudice) and ( card.is_equipment or (card.usable_next_turn and not card.can_be_used_now) ) ) or ( self.game.check_event(ce.Lazo) and card.usable_next_turn and card.can_be_used_now ) or ( ( self.game.check_event(ceh.Manette) and card.suit != self.committed_suit_manette ) ) ) if ( not ( against is not None and ( self.game.get_player_named(against).character.check( self.game, chd.ApacheKid ) or any( ( isinstance(c, grc.Calumet) for c in self.game.get_player_named( against ).gold_rush_equipment ) ) ) and card.check_suit(self.game, [cs.Suit.DIAMONDS]) ) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) and not event_blocks_card ): if ( against == self.name and not isinstance(card, csd.Tequila) and not isinstance(card, cs.Panico) and not isinstance(card, cs.CatBalou) ) or event_blocks_card: did_play_card = False else: did_play_card = card.play_card(self, against, withCard) if ( not card.is_equipment and not card.usable_next_turn and not ( isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK ) or ( event_blocks_card and not (card.usable_next_turn and card.can_be_used_now) ) ): if did_play_card: self.game.deck.scrap(card, True) self.last_played_card = card else: self.hand.insert(hand_index, card) if withCard: self.hand.insert(_with, withCard) G.sio.emit("cant_play_card", room=self.sid) elif ( (card.usable_next_turn and card.can_be_used_now) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) or event_blocks_card ): if did_play_card: self.game.deck.scrap(card, True) else: self.equipment.insert(hand_index - len(self.hand), card) elif card.is_equipment or (card.usable_next_turn and not card.can_be_used_now): if not did_play_card: if from_hand: self.hand.insert(hand_index, card) else: self.equipment.insert(hand_index - len(self.hand), card) else: did_play_card = True if not self.game.is_replay: Metrics.send_metric( "play_card", points=[1], tags=[ f"success:{did_play_card}", f"card:{card.name}", f"bot:{self.is_bot}", f'exp:{card.expansion if "expansion" in card.__dict__ else "vanilla"}', ], ) print("did play card:", did_play_card) if did_play_card: self.played_cards += 1 self.notify_self() if self.is_bot: return ( did_play_card or card.is_equipment or (card.usable_next_turn and not card.can_be_used_now) ) def choose(self, card_index): if self.pending_action != PendingAction.CHOOSE: return if self.target_p and self.target_p != "": # panico, cat balou, rissa target = self.game.get_player_named(self.target_p) card = None if target.name == self.name: card = self.equipment.pop( card_index if card_index < len(target.hand) else card_index - len(target.hand) ) elif card_index >= len(target.hand): card = target.equipment.pop(card_index - len(target.hand)) else: card = target.hand.pop(card_index) target.notify_self() if self.choose_action == "steal": G.sio.emit( "card_drawn", room=self.game.name, data={"player": self.name, "pile": target.name}, ) card.reset_card() if ( card.name != "Fantasma" or self.name != target.name ): # se si uccide facendo panico su fantasma la carta non gli viene messa in mano self.hand.append(card) else: self.game.deck.scrap(card, True, player=target) if self.event_type != "rissa" or len(self.rissa_targets) == 0: self.event_type = "" self.target_p = "" self.choose_action = "" self.pending_action = PendingAction.PLAY if target.character.check(self.game, tvosch.HenryBlock): self.game.attack(target, self.name, card_name="Henry Block") else: self.target_p = self.rissa_targets.pop(0).name print(f"rissa targets: {self.rissa_targets}") self.notify_self() elif self.choose_text == "choose_ricercato": player = self.game.get_player_named( self.available_cards[card_index]["name"] ) G.sio.emit( "chat_message", room=player.game.name, data=f"_play_card_against|{self.name}|Ricercato|{player.name}", ) if any((isinstance(c, grc.Ricercato) for c in player.gold_rush_equipment)): self.game.deck.shop_deck.append(grc.Ricercato()) else: player.gold_rush_equipment.append(grc.Ricercato()) player.notify_self() self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == "choose_sid_scrap": self.scrapped_cards += 1 self.game.deck.scrap(self.hand.pop(card_index), True, player=self) if self.scrapped_cards == 2: self.available_cards = [] self.pending_action = self.previous_pending_action if self.game.players[self.game.turn] != self: self.game.players[ self.game.turn ].pending_action = PendingAction.PLAY self.game.players[self.game.turn].notify_self() self.notify_self() elif self.choose_text == "choose_bicchierino": player = self.game.get_player_named( self.available_cards[card_index]["name"] ) G.sio.emit( "chat_message", room=self.game.name, data=f'_play_card_for|{self.name}|{"Bicchierino"}|{player.name}', ) player.lives = min(player.lives + 1, player.max_lives) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == "choose_birra_function": if card_index == 0: self.gold_nuggets += 1 G.sio.emit( "chat_message", room=self.game.name, data=f"_get_nugget|{self.name}" ) else: cs.Birra(1, 1).play_card(self, skipChecks=True) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == "choose_bottiglia": G.sio.emit( "chat_message", room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}', ) if isinstance(self.available_cards[card_index], cs.Birra): self.lives = min(self.lives + 1, self.max_lives) else: self.hand.append(self.available_cards[card_index]) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == "choose_complice": G.sio.emit( "chat_message", room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}', ) self.hand.append(self.available_cards[card_index]) self.pending_action = PendingAction.PLAY self.notify_self() elif self.choose_text == "gold_rush_discard": if card_index == len(self.available_cards) - 1: self.pending_action = PendingAction.PLAY else: player = self.game.get_player_named( self.available_cards[card_index]["name"] ) self.available_cards = [ c for c in player.gold_rush_equipment if c.number + 1 <= self.gold_nuggets ] self.available_cards.append({"icon": "❌", "noDesc": True}) self.choose_text = "gold_rush_discard_2|" + player.name self.notify_self() elif "gold_rush_discard_2" in self.choose_text: if card_index == len(self.available_cards) - 1: self.pending_action = PendingAction.PLAY else: self.gold_nuggets -= self.available_cards[card_index].number + 1 player = self.game.get_player_named(self.choose_text.split("|")[1]) player.gold_rush_equipment.remove(self.available_cards[card_index]) self.game.deck.shop_deck.append(self.available_cards[card_index]) G.sio.emit( "chat_message", room=self.game.name, data=f"_gold_rush_pay_discard|{self.name}|{player.name}|{self.available_cards[card_index].name}", ) player.notify_self() self.pending_action = PendingAction.PLAY self.notify_self() elif "choose_fantasma" in self.choose_text: if card_index <= len(self.available_cards): player = self.game.get_player_named( self.available_cards[card_index]["name"] ) player.equipment.append(self.game.deck.scrap_pile.pop(-1)) player.notify_self() self.game.notify_all() G.sio.emit( "chat_message", room=player.game.name, data=f"_play_card_against|{self.name}|Fantasma|{player.name}", ) self.pending_action = PendingAction.PLAY self.notify_self() elif "choose_sventagliata" in self.choose_text: if card_index <= len(self.available_cards): og = self.available_cards[card_index]["original_target"] player = self.game.get_player_named( self.available_cards[card_index]["name"] ) player.game.attack(self, og, card_name="Sventagliata") player.game.attack(self, player.name, card_name="Sventagliata") G.sio.emit( "chat_message", room=player.game.name, data=f"_play_card_against|{self.name}|Sventagliata|{og}", ) G.sio.emit( "chat_message", room=player.game.name, data=f"_play_card_against|{self.name}|Sventagliata|{player.name}", ) self.pending_action = PendingAction.PLAY self.notify_self() elif "choose_play_as_bang" in self.choose_text: if card_index <= len(self.available_cards): self.hand.remove(self.available_cards[card_index]) self.game.deck.scrap(self.available_cards[card_index], player=self) self.hand.append(cs.Bang(self.available_cards[card_index].suit, 42)) self.pending_action = PendingAction.PLAY self.notify_self() elif "choose_tornado" in self.choose_text: if card_index <= len(self.available_cards): self.game.deck.scrap(self.hand.pop(card_index), player=self) self.game.deck.draw(player=self) self.game.deck.draw(player=self) self.pending_action = PendingAction.WAIT self.game.responders_did_respond_resume_turn() self.notify_self() elif "choose_poker" in self.choose_text: if card_index <= len(self.available_cards): self.game.deck.scrap(self.hand.pop(card_index), player=self) self.pending_action = PendingAction.WAIT self.game.responders_did_respond_resume_turn() self.notify_self() elif "choose_from_poker" in self.choose_text: st_idx = len(self.game.deck.scrap_pile) - len(self.available_cards) self.available_cards.pop(card_index) self.hand.append(self.game.deck.scrap_pile.pop(st_idx + card_index)) self.game.notify_scrap_pile() if self.choose_text.split(";")[1] == "1": self.pending_action = PendingAction.PLAY else: self.choose_text = "choose_from_poker;1" self.notify_self() elif "choose_bandidos" in self.choose_text: if card_index < len(self.hand): self.available_cards.pop(card_index) self.game.deck.scrap(self.hand.pop(card_index), player=self) self.mancato_needed -= 1 else: self.lives -= 1 self.mancato_needed = 0 if self.mancato_needed <= 0: self.pending_action = PendingAction.WAIT self.game.responders_did_respond_resume_turn() self.notify_self() elif "choose_flint_special" == self.choose_text: if card_index < len(self.hand): self.available_cards = [ { "name": p.name, "icon": p.role.icon if (self.game.initial_players == 3) else "⭐️" if p["is_sheriff"] else "🤠", "avatar": p.avatar, "is_character": True, "is_player": True, } for p in self.game.get_alive_players() if p.name != self.name and len(p.hand) > 0 ] self.choose_text = f"choose_flint_special;{card_index}" self.notify_self() elif "choose_flint_special" in self.choose_text: if card_index < len(self.available_cards): my_card = self.hand.pop(int(self.choose_text.split(";")[1])) other_player = self.game.get_player_named( self.available_cards[card_index]["name"] ) for i in range(min(2, len(other_player.hand))): self.hand.append( other_player.hand.pop(randint(0, len(other_player.hand) - 1)) ) other_player.hand.append(my_card) other_player.notify_self() self.pending_action = PendingAction.PLAY self.notify_self() elif ( self.game.check_event(ceh.NuovaIdentita) and self.choose_text == "choose_nuova_identita" ): if card_index == 1: # the other character self.character = self.not_chosen_character self.real_character = self.character self.max_lives = self.character.max_lives + self.role.health_mod self.lives = 2 G.sio.emit( "chat_message", room=self.game.name, data=f"_choose_character|{self.name}|{self.character.name}", ) self.play_turn(again=True) elif ( self.game.check_event(ceh.Manette) and self.choose_text == "choose_manette" ): self.committed_suit_manette = cs.Suit(card_index) G.sio.emit( "chat_message", room=self.game.name, data=f'_choose_manette|{self.name}|{"♦♣♥♠"[card_index]}', ) self.pending_action = PendingAction.PLAY self.notify_self() elif self.is_giving_life and self.game.check_event(ce.FratelliDiSangue): try: player = self.game.get_player_named( self.available_cards[card_index]["name"] ) player.lives += 1 self.lives -= 1 player.notify_self() G.sio.emit( "chat_message", room=self.game.name, data=f"_fratelli_sangue|{self.name}|{player.name}", ) except: pass self.play_turn(again=True) elif self.is_using_checchino and self.game.check_event(ce.Cecchino): try: if self.available_cards[card_index]["name"] != "": for _ in range(2): card = next( c for c in self.hand if c.name == cs.Bang(0, 0).name ) self.hand.remove(card) self.game.deck.scrap(card, True) self.pending_action = PendingAction.PLAY self.game.attack( self, self.available_cards[card_index]["name"], double=True ) except: self.pending_action = PendingAction.PLAY self.is_using_checchino = False self.notify_self() elif self.using_rimbalzo > 0 and self.game.check_event(ce.Rimbalzo): if self.using_rimbalzo == 1 and "name" in self.available_cards[card_index]: self.rimbalzo_p = self.available_cards[card_index]["name"] self.available_cards = self.game.get_player_named( self.available_cards[card_index]["name"] ).equipment self.choose_text = "choose_rimbalzo_card" self.using_rimbalzo = 2 elif ( self.using_rimbalzo == 2 and "name" in self.available_cards[card_index].__dict__ ): card = next(c for c in self.hand if c.name == cs.Bang(0, 0).name) self.hand.remove(card) self.game.deck.scrap(card, True) self.using_rimbalzo = 0 self.available_cards = [] self.pending_action = PendingAction.PLAY self.game.rimbalzo(self, self.rimbalzo_p, card_index) else: self.using_rimbalzo = 0 self.rimbalzo_p = "" self.pending_action = PendingAction.PLAY self.notify_self() elif self.is_playing_ranch and self.game.check_event(ce.Ranch): if card_index == len(self.available_cards) - 1: self.hand = [c for c in self.hand if c not in self.discarded_cards] for i in range(len(self.discarded_cards)): self.game.deck.scrap(self.discarded_cards[i], True, player=self) self.game.deck.draw(player=self) self.discarded_cards = [] self.is_playing_ranch = False self.pending_action = PendingAction.PLAY else: self.discarded_cards.append(self.available_cards.pop(card_index)) self.notify_self() elif self.game.dalton_on and self.game.check_event(ceh.IDalton): card = next( c for c in self.equipment if c == self.available_cards[card_index] ) self.equipment.remove(card) self.game.deck.scrap(card, True, player=self) self.pending_action = PendingAction.WAIT self.notify_self() self.game.responders_did_respond_resume_turn() elif self.is_drawing and self.game.check_event(ce.Peyote): self.is_drawing = False card = self.game.deck.draw() G.sio.emit( "chat_message", room=self.game.name, data=f"_guess|{self.name}|{self.available_cards[card_index]['icon']}", ) self.available_cards = [] if card_index == card.suit % 2: self.hand.append(card) G.sio.emit( "chat_message", room=self.game.name, data=f"_guess_right|{self.name}", ) self.pending_action = PendingAction.DRAW else: self.game.deck.scrap(card) G.sio.emit( "chat_message", room=self.game.name, data=f"_guess_wrong|{self.name}", ) self.pending_action = PendingAction.PLAY self.notify_self() # specifico per personaggio elif self.is_drawing and self.character.check(self.game, chars.KitCarlson): card: cs.Card = self.available_cards.pop(card_index) if len(self.available_cards) == 1: # ho pescato la seconda carta if self.game.check_event(ce.LeggeDelWest): card.must_be_used = True self.hand.append(card) pickable_stop = 1 if self.game.check_event(ceh.Sete): pickable_stop += 1 if self.game.check_event(ceh.IlTreno) or any( (isinstance(c, grc.Piccone) for c in self.gold_rush_equipment) ): pickable_stop -= 1 if len(self.available_cards) == pickable_stop: if ( len(self.available_cards) > 0 ): # la carta non scelta la rimettiamo in cima al mazzo self.game.deck.put_on_top(self.available_cards.pop()) if len(self.available_cards) > 0: # se sono rimaste carte le scartiamo self.game.deck.scrap(self.available_cards.pop()) # se c'è sia treno che piccone pesco un'altra carta if self.game.check_event(ceh.IlTreno) and any( (isinstance(c, grc.Piccone) for c in self.gold_rush_equipment) ): self.game.deck.draw(player=self) self.is_drawing = False self.pending_action = PendingAction.PLAY self.manette() self.notify_self() # specifico per personaggio elif self.is_drawing and self.character.check(self.game, grch.DutchWill): if not self.game.check_event(ceh.Sete): self.hand.append( self.available_cards.pop(card_index) ) # prendo la carta scelta else: self.game.deck.scrap( self.available_cards.pop(0), True ) # non pesco carte self.game.deck.scrap(self.available_cards.pop(0), True) # scarto l'altra # legge del west non si applica perchè la seconda carta viene scartata if self.game.check_event(ceh.IlTreno): self.game.deck.draw(player=self) if any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)): self.game.deck.draw(player=self) self.gold_nuggets += 1 self.is_drawing = False self.pending_action = PendingAction.PLAY self.manette() self.notify_self() # specifico per personaggio elif self.is_drawing and self.character.check(self.game, chd.PatBrennan): # non pesca per niente dal mazzo self.is_drawing = False card = self.available_cards.pop(card_index) card.reset_card() self.hand.append(card) self.available_cards = [] G.sio.emit( "card_drawn", room=self.game.name, data={"player": self.name, "pile": self.pat_target}, ) self.game.get_player_named(self.pat_target).notify_self() self.pending_action = PendingAction.PLAY self.manette() self.notify_self() else: # emporio self.game.respond_emporio(self, card_index) def barrel_pick(self): pickable_cards = 1 + self.character.pick_mod if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)): pickable_cards += 1 if any( (isinstance(c, cs.Barile) for c in self.equipment) ) and self.character.check(self.game, chars.Jourdonnais): pickable_cards = 2 while pickable_cards > 0: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f"Did pick {picked}") G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.mancato_needed -= 1 self.notify_self() if self.mancato_needed <= 0: self.game.responders_did_respond_resume_turn(did_lose=False) return if ( not self.game.is_competitive and not any( ( isinstance(c, cs.Mancato) 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 ) ) ): self.take_damage_response() self.game.responders_did_respond_resume_turn(did_lose=True) else: self.pending_action = PendingAction.RESPOND self.expected_response = self.game.deck.mancato_cards.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.character.check(self.game, chw.BigSpencer): self.expected_response = [] self.on_failed_response_cb = self.take_damage_response self.notify_self() def barrel_pick_no_dmg(self): pickable_cards = 1 + self.character.pick_mod if any( (isinstance(c, cs.Barile) for c in self.equipment) ) and self.character.check(self.game, chars.Jourdonnais): pickable_cards = 2 if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)): pickable_cards += 1 while pickable_cards > 0: pickable_cards -= 1 picked: cs.Card = self.game.deck.pick_and_scrap() print(f"Did pick {picked}") G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.mancato_needed -= 1 self.notify_self() if self.mancato_needed <= 0: self.game.responders_did_respond_resume_turn(did_lose=False) return if ( not self.game.is_competitive and not any( ( isinstance(c, cs.Mancato) 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 ) ) ): self.take_no_damage_response() self.game.responders_did_respond_resume_turn(did_lose=True) else: self.pending_action = PendingAction.RESPOND self.expected_response = self.game.deck.mancato_cards.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.character.check(self.game, chw.BigSpencer): self.expected_response = [] self.on_failed_response_cb = self.take_no_damage_response self.notify_self() 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 self.attacking_card = card_name print(f"attacker -> {attacker}") if ( isinstance(attacker, Player) and attacker.character.check(self.game, tvosch.ColoradoBill) and card_name == "Bang!" ): picked: cs.Card = self.game.deck.pick_and_scrap() G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{attacker.name}|{picked.name}|{picked.num_suit()}", ) if picked.check_suit(self.game, [cs.Suit.SPADES]): self.take_damage_response() return False self.mancato_needed = 1 if not double else 2 if card_index is not None: self.dmg_card_index = card_index else: self.dmg_card_index = -1 for i in range(len(self.equipment)): if self.equipment[i].can_be_used_now: 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 self.can_escape(card_name) ) or card_name == "Mira": print("Cant defend") if not no_dmg: self.take_damage_response() else: self.take_no_damage_response() return False else: if ( ( not self.game.check_event(ce.Lazo) and any((isinstance(c, cs.Barile) for c in self.equipment)) ) and not ( self.game.players[self.game.turn].character.check( self.game, chd.BelleStar ) and isinstance(attacker, Player) ) ) or self.character.check( self.game, chars.Jourdonnais ): # se ho un barile e non c'è lazo e non mi sta attaccando Belle Star o se sono Jourdonnais print("has barrel") self.pending_action = PendingAction.PICK if not no_dmg: self.on_pick_cb = self.barrel_pick else: self.on_pick_cb = self.barrel_pick_no_dmg else: print("has mancato") self.pending_action = PendingAction.RESPOND self.expected_response = self.game.deck.mancato_cards.copy() if ( self.attacker and self.attacker in self.game.get_alive_players() and attacker.character.check(self.game, chd.BelleStar) or self.game.check_event(ce.Lazo) ): 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.character.check(self.game, chw.BigSpencer): self.expected_response = [] 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: self.on_failed_response_cb = self.take_no_damage_response return True def get_dalton(self): equipments = [c for c in self.equipment if not c.usable_next_turn] if len(equipments) == 0: return False else: self.choose_text = "choose_dalton" self.pending_action = PendingAction.CHOOSE self.available_cards = equipments return True def get_indians(self, attacker): 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 ) ) 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, 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 return True 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 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, 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 return True def heal_if_needed(self): while ( self.lives <= 0 and len(self.game.get_alive_players()) > 2 and any((isinstance(c, cs.Birra) for c in self.hand)) and not self.game.check_event(ceh.IlReverendo) ): for i in range(len(self.hand)): if isinstance(self.hand[i], cs.Birra): if ( self.character.check(self.game, chd.MollyStark) and not self.is_my_turn ): self.game.deck.draw(True, player=self) self.lives += ( 1 if not self.character.check(self.game, chd.TequilaJoe) else 2 ) self.lives = min(self.lives, self.max_lives) self.game.deck.scrap(self.hand.pop(i), True, player=self) G.sio.emit( "chat_message", room=self.game.name, data=f"_beer_save|{self.name}", ) break def take_damage_response(self): self.lives -= 1 G.sio.emit("hurt", room=self.sid, data=f"") if self.lives > 0: if self.character.check(self.game, chars.BartCassidy): G.sio.emit( "chat_message", room=self.game.name, data=f"_special_bart_cassidy|{self.name}", ) self.game.deck.draw(True, player=self) elif ( self.character.check(self.game, chars.ElGringo) and self.attacker and self.attacker in self.game.get_alive_players() and len(self.attacker.hand) > 0 ): self.hand.append( self.attacker.hand.pop(randrange(0, len(self.attacker.hand))) ) self.hand[-1].reset_card() G.sio.emit( "card_drawn", room=self.game.name, data={"player": self.name, "pile": self.attacker.name}, ) G.sio.emit( "chat_message", room=self.game.name, data=f"_special_el_gringo|{self.name}|{self.attacker.name}", ) self.attacker.notify_self() if isinstance(self.attacker, Player) and not self.game.check_event(ce.Lazo): if any((isinstance(c, tvosc.Taglia) for c in self.equipment)): self.game.deck.draw(True, player=self.attacker) G.sio.emit( "chat_message", room=self.game.name, data=f"_taglia_reward|{self.name}|{self.attacker.name}", ) self.attacker.notify_self() if len(self.hand) > 0 and any( (isinstance(cd, tvosc.Shotgun) for cd in self.attacker.equipment) ): c = self.hand.pop(randrange(0, len(self.hand))) self.game.deck.scrap(c, True, player=self) G.sio.emit( "chat_message", room=self.game.name, data=f"_shotgun_scrap|{self.name}|{c.name}", ) if self.attacker and "gold_rush" in self.game.expansions and not self.is_ghost: if isinstance(self.attacker, Player): self.attacker.gold_nuggets += 1 self.attacker.notify_self() if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)): self.gold_nuggets += 1 if self.character.check(self.game, grch.SimeonPicos): self.gold_nuggets += 1 if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)): self.game.deck.draw(True, player=self) self.heal_if_needed() self.mancato_needed = 0 self.expected_response = [] self.attacking_card = None self.event_type = "" self.notify_self() self.attacker = None def take_no_damage_response(self): if ( self.dmg_card_index is not None and self.dmg_card_index != -1 and self.game.check_event(ce.Rimbalzo) ): self.game.deck.scrap(self.equipment.pop(self.dmg_card_index), player=self) self.dmg_card_index = -1 self.mancato_needed = 0 self.expected_response = [] self.attacking_card = None self.event_type = "" self.notify_self() self.attacker = None def respond(self, hand_index): if self.pending_action != PendingAction.RESPOND: return self.pending_action = PendingAction.WAIT if ( hand_index != -1 and hand_index < (len(self.hand) + len(self.equipment)) and ( ( ( hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response ) or self.character.check(self.game, chd.ElenaFuente) ) or ( 0 <= hand_index - len(self.hand) < len(self.equipment) and 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)) ) # hand_index < len(self.hand) with the '<=' due to the hand.pop if ( self.character.check(self.game, chd.MollyStark) and 0 <= hand_index <= len(self.hand) and not self.is_my_turn and self.event_type != "duel" ): if ( hasattr(self.attacker, "character") and self.attacker.character.check(self.game, chars.SlabTheKiller) and isinstance(card, cs.Mancato) ): self.molly_discarded_cards += 1 else: self.game.deck.draw(True, player=self) card.use_card(self) print(f"{self.game.name}: {self.name} responded with {card.name}") G.sio.emit( "chat_message", room=self.game.name, data=f"_respond|{self.name}|{card.name}", ) self.game.deck.scrap(card, True, player=self) self.notify_self() self.mancato_needed -= 1 if isinstance(card, tvosc.RitornoDiFiamma): self.game.attack(self, self.attacker.name, card_name=card.name) if self.mancato_needed <= 0: if self.event_type == "duel": if isinstance(card, tvosc.Fuga) or ( isinstance(card, cs.Mancato) and self.character.check(self.game, tvosch.MickDefender) ): 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): self.game.deck.draw(True, player=self) self.molly_discarded_cards = 0 self.notify_self() self.game.responders_did_respond_resume_turn(did_lose=False) self.event_type = "" elif 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 ) ): self.on_failed_response_cb() if self.game: self.game.responders_did_respond_resume_turn(did_lose=True) else: self.pending_action = PendingAction.RESPOND self.notify_self() else: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn: for i in range(self.molly_discarded_cards): self.game.deck.draw(True, player=self) self.molly_discarded_cards = 0 self.notify_self() elif ( self.attacker and self.attacker in self.game.get_alive_players() and self.attacker.character.check(self.game, chd.MollyStark) and self.is_my_turn ): for i in range(self.attacker.molly_discarded_cards): self.attacker.game.deck.draw(True, player=self.attacker) self.attacker.molly_discarded_cards = 0 self.attacker.notify_self() self.on_failed_response_cb() if self.game: self.game.responders_did_respond_resume_turn(did_lose=True) if self.mancato_needed <= 0: self.attacker = None def get_sight(self, countWeapon=True): # come vedo io gli altri if not self.character: return 0 if self.game.check_event(ce.Lazo): return 1 + self.character.sight_mod aim = 0 range = 0 for card in self.equipment: if card.is_weapon and countWeapon: range += card.range else: aim += card.sight_mod return ( max(1, range) + aim + ( self.character.sight_mod if not self.game.check_event(ceh.Sbornia) else 0 ) ) def get_visibility(self): # come mi vedono gli altri if ( not self.character or not self.game or not self.game.players[self.game.turn].character ): return 0 covers = 0 ch_vis_mod = ( self.character.visibility_mod if not self.game.check_event(ceh.Sbornia) else 0 ) if self.game.check_event(ce.Lazo) or self.game.players[ self.game.turn ].character.check(self.game, chd.BelleStar): return ch_vis_mod for card in self.equipment: covers += card.vis_mod return ch_vis_mod + covers def scrap(self, card_index): if len(self.hand) == 0 or len(self.hand) <= card_index: return self.notify_self() if self.is_my_turn or self.character.check(self.game, chars.SidKetchum): self.scrapped_cards += 1 card = self.hand.pop(card_index) if ( self.character.check(self.game, chars.SidKetchum) and self.scrapped_cards == 2 ): self.scrapped_cards = 0 self.lives = min(self.lives + 1, self.max_lives) elif ( self.character.check(self.game, chd.JoseDelgado) and card.is_equipment and self.special_use_count < 2 ): self.game.deck.draw(True, player=self) self.game.deck.draw(True, player=self) self.special_use_count += 1 self.game.deck.scrap(card, player=self) self.notify_self() def special(self, data): self.character.special(self, data) def gold_rush_discard(self): self.available_cards = [ { "name": p.name, "icon": p.role.icon if (self.game.initial_players == 3) else "⭐️" if isinstance(p.role, r.Sheriff) else "🤠", "is_character": True, "avatar": p.avatar, "alt_text": "".join(["🎴️"] * len(p.gold_rush_equipment)), "is_player": True, } for p in self.game.get_alive_players() if p != self and any((e.number + 1 <= self.gold_nuggets for e in p.gold_rush_equipment)) ] self.available_cards.append({"icon": "❌", "noDesc": True}) self.choose_text = "gold_rush_discard" self.pending_action = PendingAction.CHOOSE self.notify_self() def buy_gold_rush_card(self, index): print( f"{self.name} wants to buy gr-card index {index} in room {self.game.name}" ) card: cs.Card = self.game.deck.shop_cards[index] discount = 0 if ( self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1 ): discount = 1 if ( self.pending_action == PendingAction.PLAY and self.gold_nuggets >= card.number - discount ): self.gold_nuggets -= card.number - discount if ( self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1 ): self.special_use_count += 1 if card.play_card(self): self.game.deck.shop_deck.append(card) self.game.deck.shop_cards[index] = None self.game.deck.fill_gold_rush_shop() self.notify_self() def check_can_end_turn(self): must_be_used_cards = [c for c in self.hand if c.must_be_used] if self.game.check_event(ce.LeggeDelWest) and len(must_be_used_cards) > 0: card = must_be_used_cards[0] print(f"Legge del west card: {card.name}") print( self.has_played_bang and not ( any((isinstance(c, cs.Volcanic) for c in self.equipment)) and type(card) == type(cs.Bang) ) ) if ( card.suit == cs.Suit.DIAMONDS and card.need_target and not any( ( ( not p.character.check(self.game, chd.ApacheKid) and not any( ( isinstance(c, grc.Calumet) for c in p.gold_rush_equipment ) ) ) for p in self.game.get_alive_players() ) ) ): if isinstance(card, cs.Bang): return True else: return len(self.equipment) == 0 # se non ho carte equipaggiamento elif ( ( isinstance(card, cs.Bang) or ( isinstance(card, cs.Mancato) and self.character.check(self.game, chars.CalamityJanet) ) ) and self.has_played_bang and not any((isinstance(c, cs.Volcanic) for c in self.equipment)) or not any( ( self.get_sight() >= p["dist"] for p in self.game.get_visible_players(self) ) ) ): return True elif isinstance(card, cs.Mancato) or ( card.need_with and len(self.hand) < 2 ): return True elif ( isinstance(card, cs.Panico) and not any( ( self.get_sight(False) >= p["dist"] for p in self.game.get_visible_players(self) ) ) and len(self.equipment) == 0 ): return True elif isinstance(card, csd.Pugno) and not any( ( self.get_sight(False) >= p["dist"] for p in self.game.get_visible_players(self) ) ): return True elif isinstance(card, cs.Prigione) and not any( (not p["is_sheriff"] for p in self.game.get_visible_players(self)) ): return True elif not card.is_weapon and any( (c.name == card.name for c in self.equipment) ): return True return False return True def end_turn(self, forced=False): print(f"{self.name} wants to end his turn") if not self.is_my_turn and not forced: return maxcards = ( self.lives if not self.character.check(self.game, chd.SeanMallory) else 10 ) if maxcards == self.lives and any( (isinstance(c, grc.Cinturone) for c in self.gold_rush_equipment) ): maxcards = 8 if len(self.hand) > maxcards and not forced: print( f"{self.name}: I have to many cards in my hand and I can't end the turn" ) elif not self.check_can_end_turn(): print(f"{self.name}: I must play the legge del west card") elif self.pending_action == PendingAction.PLAY or forced: 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 for i in range(len(self.hand)): if self.hand[i].must_be_used: self.hand[i].must_be_used = False self.has_played_bang = False ##Vendetta## if ( not forced and self.game.check_event(ce.Vendetta) and self.can_play_vendetta ): picked: cs.Card = self.game.deck.pick_and_scrap() G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) if picked.check_suit(self.game, [cs.Suit.HEARTS]): self.play_turn(can_play_vendetta=False) return ##Don Bell## if ( not forced and self.character.check(self.game, grch.DonBell) and self.can_play_again_don_bell ): picked: cs.Card = self.game.deck.pick_and_scrap() G.sio.emit( "chat_message", room=self.game.name, data=f"_flipped|{self.name}|{picked.name}|{picked.num_suit()}", ) self.can_play_again_don_bell = False if picked.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]): self.play_turn(can_play_vendetta=False) return ##Ghost## if ( self.is_dead and self.is_ghost and self.game.check_event(ceh.CittaFantasma) and not any( (True for c in self.equipment if isinstance(c, tvosc.Fantasma)) ) ) or ( self.is_dead and forced and self.is_ghost and not any( (True for c in self.equipment if isinstance(c, tvosc.Fantasma)) ) ): self.is_ghost = False for i in range(len(self.hand)): self.game.deck.scrap(self.hand.pop(), True) for i in range(len(self.equipment)): self.game.deck.scrap(self.equipment.pop(), True) self.is_my_turn = False if self.played_cards < 3 and self.game.check_event(cew.MissSusanna): self.lives -= 1 self.played_cards = 0 self.can_play_again_don_bell = True self.committed_suit_manette = None self.pending_action = PendingAction.WAIT self.notify_self() self.game.next_turn()