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 card in self.gold_rush_equipment:
                    if isinstance(card, grc.Zaino):
                        card.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, card in enumerate(self.game.deck.shop_cards):
                    if card.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, card in enumerate(self.hand):
                if card.can_be_used_now and (
                    card.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, card in enumerate(self.equipment):
                if (
                    not self.game.check_event(ce.Lazo)
                    and card.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, card in enumerate(self.equipment):
                if i < len(self.equipment) and isinstance(card, 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, card in enumerate(self.equipment):
                if isinstance(card, 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, card in enumerate(self.equipment):
                if isinstance(card, 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, card in enumerate(self.hand):
            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
                    and card.number != 42
                )
            )
        )
        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 isinstance(p.role, r.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)
                    or self.game.check_event(cew.RegolamentoDiConti)
                ) 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
            gary_looter = None
            for p in self.game.players:
                if p != self and p.character.check(self.game, chw.GaryLooter):
                    gary_looter = p
                    break
            if gary_looter is not None:
                G.sio.emit(
                    "card_drawn",
                    room=self.game.name,
                    data={"player": gary_looter.name, "pile": self.name},
                )
                gary_looter.hand.append(card)
                gary_looter.notify_self()
            else:
                self.game.deck.scrap(card, player=self)
            G.sio.emit(
                "chat_message",
                room=self.game.name,
                data=f"_scrapped|{self.name}|{card.name}|{card.num_suit()}",
            )
            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()
