Merge pull request #17 from albertoxamin/dev

add high noon
This commit is contained in:
Alberto Xamin 2020-12-27 22:09:36 +01:00 committed by GitHub
commit 2165579939
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 651 additions and 253 deletions

View File

@ -15,7 +15,7 @@ sio = socketio.Server(cors_allowed_origins="*")
static_files={ static_files={
'/': {'content_type': 'text/html', 'filename': 'index.html'}, '/': {'content_type': 'text/html', 'filename': 'index.html'},
'/game': {'content_type': 'text/html', 'filename': 'index.html'}, '/game': {'content_type': 'text/html', 'filename': 'index.html'},
'/robots.txt': {'content_type': 'text/html', 'filename': 'robots.txt'}, # '/robots.txt': {'content_type': 'text/html', 'filename': 'robots.txt'},
'/favicon.ico': {'filename': 'favicon.ico'}, '/favicon.ico': {'filename': 'favicon.ico'},
'/img/icons': './img/icons', '/img/icons': './img/icons',
'/manifest.json': {'filename': 'manifest.json'}, '/manifest.json': {'filename': 'manifest.json'},
@ -42,18 +42,21 @@ def connect(sid, environ):
@sio.event @sio.event
def set_username(sid, username): def set_username(sid, username):
if not isinstance(sio.get_session(sid), Player): ses = sio.get_session(sid)
if not isinstance(ses, Player):
sio.save_session(sid, Player(username, sid, sio)) sio.save_session(sid, Player(username, sid, sio))
print(f'{sid} is now {username}') print(f'{sid} is now {username}')
advertise_lobbies() advertise_lobbies()
elif sio.get_session(sid).game == None or not sio.get_session(sid).game.started: elif ses.game == None or not ses.game.started:
print(f'{sid} changed username to {username}') print(f'{sid} changed username to {username}')
if len([p for p in sio.get_session(sid).game.players if p.name == username]) > 0: prev = ses.name
sio.get_session(sid).name = f'{username}_{random.randint(0,100)}' if len([p for p in ses.game.players if p.name == username]) > 0:
ses.name = f"{username}_{random.randint(0,100)}"
else: else:
sio.get_session(sid).name = username ses.name = username
sio.emit('me', data=sio.get_session(sid).name, room=sid) sio.emit('chat_message', room=ses.game.name, data=f'_change_username|{prev}|{ses.name}')
sio.get_session(sid).game.notify_room() sio.emit('me', data=ses.name, room=sid)
ses.game.notify_room()
@sio.event @sio.event
def get_me(sid, room): def get_me(sid, room):
@ -80,8 +83,10 @@ def get_me(sid, room):
de_games[0].notify_all() de_games[0].notify_all()
sio.emit('role', room=sid, data=json.dumps(bot.role, default=lambda o: o.__dict__)) sio.emit('role', room=sid, data=json.dumps(bot.role, default=lambda o: o.__dict__))
bot.notify_self() bot.notify_self()
if len(bot.available_characters) > 0:
bot.set_available_character(bot.available_characters)
else: #spectate else: #spectate
de_games[0].dead_players.append(sio.get_session(sid)) de_games[0].spectators.append(sio.get_session(sid))
sio.get_session(sid).game = de_games[0] sio.get_session(sid).game = de_games[0]
sio.enter_room(sid, de_games[0].name) sio.enter_room(sid, de_games[0].name)
de_games[0].notify_room(sid) de_games[0].notify_room(sid)
@ -96,6 +101,7 @@ def get_me(sid, room):
if room['username'] == None or any([p.name == room['username'] for p in sio.get_session(sid).game.players]): if room['username'] == None or any([p.name == room['username'] for p in sio.get_session(sid).game.players]):
sio.emit('change_username', room=sid) sio.emit('change_username', room=sid)
else: else:
sio.emit('chat_message', room=sio.get_session(sid).game.name, data=f"_change_username|{sio.get_session(sid).name}|{room['username']}")
sio.get_session(sid).name = room['username'] sio.get_session(sid).name = room['username']
sio.emit('me', data=sio.get_session(sid).name, room=sid) sio.emit('me', data=sio.get_session(sid).name, room=sid)
if not sio.get_session(sid).game.started: if not sio.get_session(sid).game.started:
@ -166,10 +172,13 @@ def chat_message(sid, msg):
if msg[0] == '/': if msg[0] == '/':
if '/addbot' in msg and not ses.game.started: if '/addbot' in msg and not ses.game.started:
if len(msg.split()) > 1: if len(msg.split()) > 1:
for _ in range(int(msg.split()[1])): # for _ in range(int(msg.split()[1])):
ses.game.add_player(Player(f'AI_{random.randint(0,100)}', 'bot', sio, bot=True)) # ses.game.add_player(Player(f'AI_{random.randint(0,1000)}', 'bot', sio, bot=True))
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'Only 1 bot at the time'})
else: else:
ses.game.add_player(Player(f'AI_{random.randint(0,100)}', 'bot', sio, bot=True)) bot = Player(f'AI_{random.randint(0,1000)}', 'bot', sio, bot=True)
ses.game.add_player(bot)
bot.bot_spin()
elif '/removebot' in msg and not ses.game.started: elif '/removebot' in msg and not ses.game.started:
if any([p.is_bot for p in ses.game.players]): if any([p.is_bot for p in ses.game.players]):
[p for p in ses.game.players if p.is_bot][-1].disconnect() [p for p in ses.game.players if p.is_bot][-1].disconnect()
@ -228,14 +237,21 @@ def chat_message(sid, msg):
ses.character = [c for c in chs if c.name == ' '.join(cmd[1:])][0] ses.character = [c for c in chs if c.name == ' '.join(cmd[1:])][0]
ses.real_character = ses.character ses.real_character = ses.character
ses.notify_self() ses.notify_self()
elif '/setevent' in msg and ses.game and ses.game.deck:
cmd = msg.split()
if len(cmd) >= 3:
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and changed event'})
chs = ses.game.deck.event_cards
ses.game.deck.event_cards.insert(int(cmd[1]), [c for c in chs if c!=None and c.name == ' '.join(cmd[2:])][0])
ses.game.notify_event_card()
elif '/removecard' in msg: elif '/removecard' in msg:
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and removed a card'}) sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and removed a card'})
cmd = msg.split() cmd = msg.split()
if len(cmd) == 2: if len(cmd) == 2:
if len(ses.hand) > int(cmd[1]): if int(cmd[1]) < len(ses.hand):
ses.hand.pop(int(cmd[1])) ses.hand.pop(int(cmd[1]))
else: else:
ses.hand.pop(int(cmd[1])-len(ses.hand)) ses.equipment.pop(int(cmd[1])-len(ses.hand))
ses.notify_self() ses.notify_self()
elif '/getcard' in msg: elif '/getcard' in msg:
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'}) sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'})
@ -243,15 +259,17 @@ def chat_message(sid, msg):
cmd = msg.split() cmd = msg.split()
if len(cmd) >= 2: if len(cmd) >= 2:
cards = cs.get_starting_deck(ses.game.expansions) cards = cs.get_starting_deck(ses.game.expansions)
ses.hand.append([c for c in cards if c.name == ' '.join(cmd[1:])][0]) card_names = ' '.join(cmd[1:]).split(',')
ses.notify_self() for cn in card_names:
ses.hand.append([c for c in cards if c.name == cn][0])
ses.notify_self()
elif '/gameinfo' in msg: elif '/gameinfo' in msg:
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.game.__dict__}'}) sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.game.__dict__}'})
elif '/meinfo' in msg: elif '/meinfo' in msg:
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'}) sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'})
elif '/mebot' in msg: elif '/mebot' in msg:
ses.is_bot = not ses.is_bot ses.is_bot = not ses.is_bot
ses.notify_self() ses.bot_spin()
else: else:
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'}) sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
else: else:

View File

@ -75,11 +75,21 @@ class Card(ABC):
def is_duplicate_card(self, player): def is_duplicate_card(self, player):
return self.name in [c.name for c in player.equipment] return self.name in [c.name for c in player.equipment]
def check_suit(self, game, accepted):
import bang.expansions.high_noon.card_events as ceh
if game.check_event(ceh.Benedizione):
return Suit.HEARTS in accepted
elif game.check_event(ceh.Maledizione):
return Suit.SPADES in accepted
return self.suit in accepted
class Barile(Card): class Barile(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Barile', number, is_equipment=True) super().__init__(suit, 'Barile', number, is_equipment=True)
self.icon = '🛢' self.icon = '🛢'
self.alt_text = "♥️=😅"
self.desc = "Quando sei bersagliato da un Bang puoi estrarre la prima carta dalla cima del mazzo, se la carta estratta è del seme Cuori allora vale come un Mancato" self.desc = "Quando sei bersagliato da un Bang puoi estrarre la prima carta dalla cima del mazzo, se la carta estratta è del seme Cuori allora vale come un Mancato"
self.desc_eng = "When someone plays a Bang against you. You can flip the first card from the deck, if the suit is Hearts then it counts as a Missed card" self.desc_eng = "When someone plays a Bang against you. You can flip the first card from the deck, if the suit is Hearts then it counts as a Missed card"
@ -88,6 +98,7 @@ class Dinamite(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Dinamite', number, is_equipment=True) super().__init__(suit, 'Dinamite', number, is_equipment=True)
self.icon = '🧨' self.icon = '🧨'
self.alt_text = "2-9♠ = 🤯"
self.desc = "Giocando la Dinamite, posizionala davanti a te, resterà innocua per un intero giro. All'inizio del prossimo turno prima di pescare e prima di una eventuale estrazione (es. Prigione), estrai una carta dalla cima del mazzo. Se esce una carta tra il 2 il 9 di picche (compresi) allora la dinamite esplode: perdi 3 vite e scarta la carta, altrimenti passa la dinamite al giocatore successivo, il quale estrarà a sua volta dopo che tu avrai passato il tuo turno" self.desc = "Giocando la Dinamite, posizionala davanti a te, resterà innocua per un intero giro. All'inizio del prossimo turno prima di pescare e prima di una eventuale estrazione (es. Prigione), estrai una carta dalla cima del mazzo. Se esce una carta tra il 2 il 9 di picche (compresi) allora la dinamite esplode: perdi 3 vite e scarta la carta, altrimenti passa la dinamite al giocatore successivo, il quale estrarà a sua volta dopo che tu avrai passato il tuo turno"
self.desc_eng = "When playing Dynamite, place it in front of you, it will remain harmless for a whole round. At the beginning of the next turn before drawing and before any card flip (eg Prison), flip a card from the top of the deck. If a card is between 2 and 9 of spades (inclusive) then the dynamite explodes: you lose 3 lives and discard the card, otherwise pass the dynamite to the next player, who will draw in turn after you have ended your turn" self.desc_eng = "When playing Dynamite, place it in front of you, it will remain harmless for a whole round. At the beginning of the next turn before drawing and before any card flip (eg Prison), flip a card from the top of the deck. If a card is between 2 and 9 of spades (inclusive) then the dynamite explodes: you lose 3 lives and discard the card, otherwise pass the dynamite to the next player, who will draw in turn after you have ended your turn"
@ -96,6 +107,7 @@ class Mirino(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Mirino', number, is_equipment=True, sight_mod=1) super().__init__(suit, 'Mirino', number, is_equipment=True, sight_mod=1)
self.icon = '🔎' self.icon = '🔎'
self.alt_text = "-1"
self.desc = "Tu vedi gli altri giocatori a distanza -1" self.desc = "Tu vedi gli altri giocatori a distanza -1"
self.desc_eng = "You see the other players at distance -1" self.desc_eng = "You see the other players at distance -1"
@ -104,6 +116,7 @@ class Mustang(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Mustang', number, is_equipment=True, vis_mod=1) super().__init__(suit, 'Mustang', number, is_equipment=True, vis_mod=1)
self.icon = '🐎' self.icon = '🐎'
self.alt_text = "+1"
self.desc = "Gli altri giocatori ti vedono a distanza +1" self.desc = "Gli altri giocatori ti vedono a distanza +1"
self.desc_eng = "The other players see you at distance +1" self.desc_eng = "The other players see you at distance +1"
@ -115,6 +128,7 @@ class Prigione(Card):
self.desc = "Equipaggia questa carta a un altro giocatore, tranne lo Sceriffo. Il giocatore scelto all'inizio del suo turno, prima di pescare dovrà estrarre: se esce Cuori scarta questa carta e gioca normalmente il turno, altrimenti scarta questa carta e salta il turno" self.desc = "Equipaggia questa carta a un altro giocatore, tranne lo Sceriffo. Il giocatore scelto all'inizio del suo turno, prima di pescare dovrà estrarre: se esce Cuori scarta questa carta e gioca normalmente il turno, altrimenti scarta questa carta e salta il turno"
self.desc_eng = "Equip this card to another player, except the Sheriff. The player chosen at the beginning of his turn, must flip a card before drawing: if it's Hearts, discard this card and play the turn normally, otherwise discard this card and skip the turn" self.desc_eng = "Equip this card to another player, except the Sheriff. The player chosen at the beginning of his turn, must flip a card before drawing: if it's Hearts, discard this card and play the turn normally, otherwise discard this card and skip the turn"
self.need_target = True self.need_target = True
self.alt_text = "♥️= 🆓"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if against != None and not isinstance(player.game.get_player_named(against).role, r.Sheriff): if against != None and not isinstance(player.game.get_player_named(against).role, r.Sheriff):
@ -180,14 +194,19 @@ class Bang(Card):
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.fistful_of_cards.card_events as ce
import bang.expansions.high_noon.card_events as ceh
if player.game.check_event(ceh.Sermone):
return False
if player.has_played_bang and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)) and against != None: if player.has_played_bang and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)) and against != None:
return False return False
elif against != None: elif against != None:
import bang.characters as chars import bang.characters as chars
super().play_card(player, against=against) super().play_card(player, against=against)
player.has_played_bang = not isinstance( player.bang_used += 1
player.character, chars.WillyTheKid) player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1
player.game.attack(player, against, double=isinstance(player.character, chars.SlabTheKiller)) if player.character.check(player.game, chars.WillyTheKid):
player.has_played_bang = False
player.game.attack(player, against, double=player.character.check(player.game, chars.SlabTheKiller))
return True return True
return False return False
@ -200,14 +219,17 @@ class Birra(Card):
self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players" self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if len(player.game.players) != 2: import bang.expansions.high_noon.card_events as ceh
if player.game.check_event(ceh.IlReverendo):
return False
if len(player.game.get_alive_players()) != 2:
super().play_card(player, against=against) super().play_card(player, against=against)
player.lives = min(player.lives+1, player.max_lives) player.lives = min(player.lives+1, player.max_lives)
import bang.expansions.dodge_city.characters as chd import bang.expansions.dodge_city.characters as chd
if isinstance(player.character, chd.TequilaJoe): if player.character.check(player.game, chd.TequilaJoe):
player.lives = min(player.lives+1, player.max_lives) player.lives = min(player.lives+1, player.max_lives)
return True return True
elif len(player.game.players) == 2: elif len(player.game.get_alive_players()) == 2:
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
data=f'_spilled_beer|{player.name}|{self.name}') data=f'_spilled_beer|{player.name}|{self.name}')
return True return True
@ -239,6 +261,7 @@ class Diligenza(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Diligenza', number) super().__init__(suit, 'Diligenza', number)
self.icon = '🚡' self.icon = '🚡'
self.alt_text = "🎴🎴"
self.desc = "Pesca 2 carte dalla cima del mazzo" self.desc = "Pesca 2 carte dalla cima del mazzo"
self.desc_eng = "Draw 2 cards from the deck." self.desc_eng = "Draw 2 cards from the deck."
@ -285,6 +308,7 @@ class Gatling(Card):
self.icon = '🛰' self.icon = '🛰'
self.desc = "Spara a tutti gli altri giocatori" self.desc = "Spara a tutti gli altri giocatori"
self.desc_eng = "Shoot all the other players" self.desc_eng = "Shoot all the other players"
self.alt_text = "👥💥"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
super().play_card(player, against=against) super().play_card(player, against=against)
@ -314,10 +338,17 @@ class Mancato(Card):
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
import bang.characters as chars import bang.characters as chars
if (not player.has_played_bang and against != None and isinstance(player.character, chars.CalamityJanet)): if against != None and player.character.check(player.game, chars.CalamityJanet):
import bang.expansions.fistful_of_cards.card_events as ce
if player.has_played_bang and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)):
return False
import bang.expansions.high_noon.card_events as ceh
if player.game.check_event(ceh.Sermone):
return False
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
data=f'_special_calamity|{player.name}|{self.name}|{against}') data=f'_special_calamity|{player.name}|{self.name}|{against}')
player.has_played_bang = True player.bang_used += 1
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1
player.game.attack(player, against) player.game.attack(player, against)
return True return True
return False return False
@ -349,11 +380,12 @@ class Saloon(Card):
self.desc = "Tutti i giocatori recuperano un punto vita compreso chi gioca la carta" self.desc = "Tutti i giocatori recuperano un punto vita compreso chi gioca la carta"
self.desc_eng = "Everyone heals 1 Health point" self.desc_eng = "Everyone heals 1 Health point"
self.icon = '🍻' self.icon = '🍻'
self.alt_text = "👥🍺"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
data=f'_saloon|{player.name}|{self.name}') data=f'_saloon|{player.name}|{self.name}')
for p in player.game.players: for p in player.game.get_alive_players():
p.lives = min(p.lives+1, p.max_lives) p.lives = min(p.lives+1, p.max_lives)
p.notify_self() p.notify_self()
return True return True
@ -365,6 +397,7 @@ class WellsFargo(Card):
self.desc = "Pesca 3 carte dalla cima del mazzo" self.desc = "Pesca 3 carte dalla cima del mazzo"
self.desc_eng = "Draw 3 cards from the deck" self.desc_eng = "Draw 3 cards from the deck"
self.icon = '💸' self.icon = '💸'
self.alt_text = "🎴🎴🎴"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,

View File

@ -13,21 +13,11 @@ class Character(ABC):
self.icon = '🤷‍♂️' self.icon = '🤷‍♂️'
self.number = ''.join(['❤️']*self.max_lives) self.number = ''.join(['❤️']*self.max_lives)
# @abstractmethod def check(self, game, character):
# def on_hurt(self, dmg: int): import bang.expansions.high_noon.card_events as ceh
# pass if game.check_event(ceh.Sbornia):
return False
# @abstractmethod return isinstance(self, character)
# def on_pick(self, card): # tipo dinamite e prigione
# pass
# @abstractmethod
# def on_empty_hand(self):
# pass
# @abstractmethod
# def on_empty_hand(self):
# pass
class BartCassidy(Character): class BartCassidy(Character):
def __init__(self): def __init__(self):

View File

@ -2,6 +2,7 @@ from typing import List, Set, Dict, Tuple, Optional
import random import random
import bang.cards as cs import bang.cards as cs
import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.fistful_of_cards.card_events as ce
import bang.expansions.high_noon.card_events as ceh
class Deck: class Deck:
def __init__(self, game): def __init__(self, game):
@ -20,16 +21,25 @@ class Deck:
self.all_cards_str.append(c.name) self.all_cards_str.append(c.name)
self.game = game self.game = game
self.event_cards: List[ce.CardEvent] = [] self.event_cards: List[ce.CardEvent] = []
endgame_cards = []
if 'fistful_of_cards' in game.expansions: if 'fistful_of_cards' in game.expansions:
self.event_cards.extend(ce.get_all_events()) self.event_cards.extend(ce.get_all_events())
endgame_cards.append(ce.get_endgame_card())
if 'high_noon' in game.expansions:
self.event_cards.extend(ceh.get_all_events())
endgame_cards.append(ceh.get_endgame_card())
if len(self.event_cards) > 0:
random.shuffle(self.event_cards)
self.event_cards = self.event_cards[:12]
self.event_cards.insert(0, None) self.event_cards.insert(0, None)
self.event_cards.insert(0, None) # 2 perchè iniziale, e primo flip dallo sceriffo self.event_cards.insert(0, None) # 2 perchè iniziale, e primo flip dallo sceriffo
self.event_cards.append(random.choice(endgame_cards))
random.shuffle(self.cards) random.shuffle(self.cards)
self.scrap_pile: List[cs.Card] = [] self.scrap_pile: List[cs.Card] = []
print(f'Deck initialized with {len(self.cards)} cards') print(f'Deck initialized with {len(self.cards)} cards')
def flip_event(self): def flip_event(self):
if len(self.event_cards) > 0 and not isinstance(self.event_cards[0], ce.PerUnPugnoDiCarte): if len(self.event_cards) > 0 and not (isinstance(self.event_cards[0], ce.PerUnPugnoDiCarte) or isinstance(self.event_cards[0], ceh.MezzogiornoDiFuoco)):
self.event_cards.append(self.event_cards.pop(0)) self.event_cards.append(self.event_cards.pop(0))
self.game.notify_event_card() self.game.notify_event_card()

View File

@ -16,6 +16,7 @@ class Pugno(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Pugno!', number, range=1) super().__init__(suit, 'Pugno!', number, range=1)
self.icon = '👊' self.icon = '👊'
self.alt_text = "1🔎 💥"
self.desc = "Spara a un giocatore a distanza 1" self.desc = "Spara a un giocatore a distanza 1"
self.desc_eng = "Shoot a player at distance 1" self.desc_eng = "Shoot a player at distance 1"
self.need_target = True self.need_target = True
@ -34,7 +35,7 @@ class Schivata(Mancato):
self.icon = '🙅‍♂️' self.icon = '🙅‍♂️'
self.desc += " e poi pesca una carta" self.desc += " e poi pesca una carta"
self.desc_eng += " and then draw a card." self.desc_eng += " and then draw a card."
self.alt_text = '☝️🆓' self.alt_text = "😅 | 🎴"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
return False return False
@ -51,7 +52,7 @@ class RagTime(Panico):
self.desc_eng = "Steal a card from another player at any distance" self.desc_eng = "Steal a card from another player at any distance"
self.need_target = True self.need_target = True
self.need_with = True self.need_with = True
self.alt_text = '2🃏' self.alt_text = '2🃏 | 👤😱'
def play_card(self, player, against, _with): def play_card(self, player, against, _with):
if against != None and _with != None: if against != None and _with != None:
@ -69,7 +70,7 @@ class Rissa(CatBalou):
self.desc_eng = "Choose a card to discard from the hand/equipment of all the other players" self.desc_eng = "Choose a card to discard from the hand/equipment of all the other players"
self.need_with = True self.need_with = True
self.need_target = False self.need_target = False
self.alt_text = '2🃏' self.alt_text = '2🃏 | 👤💃'
def play_card(self, player, against, _with): def play_card(self, player, against, _with):
if _with != None: if _with != None:
@ -91,7 +92,7 @@ class SpringField(Card):
self.desc_eng = "Shoot a player at any distance" self.desc_eng = "Shoot a player at any distance"
self.need_target = True self.need_target = True
self.need_with = True self.need_with = True
self.alt_text = '2🃏' self.alt_text = '2🃏 | 👤💥'
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if against != None and _with != None: if against != None and _with != None:
@ -110,7 +111,7 @@ class Tequila(Card):
self.need_target = True self.need_target = True
self.can_target_self = True self.can_target_self = True
self.need_with = True self.need_with = True
self.alt_text = '2🃏' self.alt_text = "2🃏 | 👤🍺"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if against != None and _with != None: if against != None and _with != None:
@ -128,7 +129,7 @@ class Whisky(Card):
self.desc = "Gioca questa carta per recuperare fino a 2 punti vita" self.desc = "Gioca questa carta per recuperare fino a 2 punti vita"
self.desc_eng = "Heal 2 HP" self.desc_eng = "Heal 2 HP"
self.need_with = True self.need_with = True
self.alt_text = '2🃏' self.alt_text = '2🃏 | 🍺🍺'
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if _with != None: if _with != None:
@ -165,6 +166,7 @@ class Cappello(Mancato):
self.icon = '🧢' self.icon = '🧢'
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
self.alt_text = "😅"
def play_card(self, player, against, _with=None): def play_card(self, player, against, _with=None):
if self.can_be_used_now: if self.can_be_used_now:
@ -212,7 +214,7 @@ class Derringer(Pugnale):
super().__init__(suit, number) super().__init__(suit, number)
self.name = 'Derringer' self.name = 'Derringer'
self.icon = '🚬' self.icon = '🚬'
self.alt_text += ' ☝️🆓' self.alt_text += ' 🎴'
self.desc += ' e poi pesca una carta' self.desc += ' e poi pesca una carta'
self.desc_eng += ' and then draw a card.' self.desc_eng += ' and then draw a card.'
@ -237,6 +239,7 @@ class Borraccia(Card):
self.icon = '🍼' self.icon = '🍼'
self.desc = 'Recupera 1 vita' self.desc = 'Recupera 1 vita'
self.desc_eng = 'Regain 1 HP' self.desc_eng = 'Regain 1 HP'
self.alt_text = "🍺"
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -258,6 +261,7 @@ class PonyExpress(WellsFargo):
super().__init__(suit, number) super().__init__(suit, number)
self.name = 'Pony Express' self.name = 'Pony Express'
self.icon = '🦄' self.icon = '🦄'
self.alt_text = "🎴🎴🎴"
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -276,6 +280,7 @@ class Howitzer(Gatling):
super().__init__(suit, number) super().__init__(suit, number)
self.name = 'Howitzer' self.name = 'Howitzer'
self.icon = '📡' self.icon = '📡'
self.alt_text = "👥💥"
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -292,8 +297,9 @@ class Howitzer(Gatling):
class CanCan(CatBalou): class CanCan(CatBalou):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, number) super().__init__(suit, number)
self.name = 'Can Can' self.name = "Can Can"
self.icon = '👯‍♀️' self.icon = "👯‍♀️"
self.alt_text = "👤💃"
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -310,9 +316,10 @@ class CanCan(CatBalou):
class Conestoga(Panico): class Conestoga(Panico):
def __init__(self, suit, number): def __init__(self, suit, number):
Card.__init__(self, suit, 'Conestoga', number) Card.__init__(self, suit, 'Conestoga', number)
self.icon = '🏕' self.icon = "🏕"
self.desc = "Ruba 1 carta da un giocatore a prescindere dalla distanza" self.desc = "Ruba 1 carta da un giocatore a prescindere dalla distanza"
self.desc_eng = "Steal a card from another player at any distance" self.desc_eng = "Steal a card from another player at any distance"
self.alt_text = "👤😱"
self.need_target = True self.need_target = True
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -332,6 +339,7 @@ class Pepperbox(Bang):
super().__init__(suit, number) super().__init__(suit, number)
self.name = 'Pepperbox' self.name = 'Pepperbox'
self.icon = '🌶' self.icon = '🌶'
self.alt_text = "💥"
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False
@ -354,6 +362,7 @@ class FucileDaCaccia(Card):
super().__init__(suit, 'Fucile Da Caccia', number) super().__init__(suit, 'Fucile Da Caccia', number)
self.icon = '🌂' self.icon = '🌂'
self.desc = "Spara a un giocatore a prescindere dalla distanza" self.desc = "Spara a un giocatore a prescindere dalla distanza"
self.alt_text = "👤💥"
self.need_target = True self.need_target = True
self.usable_next_turn = True self.usable_next_turn = True
self.can_be_used_now = False self.can_be_used_now = False

View File

@ -24,7 +24,7 @@ class GregDigger(Character):
class HerbHunter(Character): class HerbHunter(Character):
def __init__(self): def __init__(self):
super().__init__("HerbHunter", max_lives=4) super().__init__("Herb Hunter", max_lives=4)
self.desc = "Quando un giocatore muore, pesca 2 carte" self.desc = "Quando un giocatore muore, pesca 2 carte"
self.desc_eng = "Whenever a player dies, he draws 2 cards" self.desc_eng = "Whenever a player dies, he draws 2 cards"
self.icon = '⚰️' self.icon = '⚰️'
@ -66,7 +66,7 @@ class SeanMallory(Character):
class BelleStar(Character): class BelleStar(Character):
def __init__(self): def __init__(self):
super().__init__("Belle Star", max_lives=3) super().__init__("Belle Star", max_lives=4)
self.desc = "Nel suo turno le carte verdi degli altri giocatori non hanno effetto." self.desc = "Nel suo turno le carte verdi degli altri giocatori non hanno effetto."
self.desc_eng = "During her turn the green cards of the other players do not work." self.desc_eng = "During her turn the green cards of the other players do not work."
self.icon = '' self.icon = ''

View File

@ -22,7 +22,7 @@ class DeadMan(CardEvent):
def __init__(self): def __init__(self):
super().__init__("Dead Man", "⚰️") super().__init__("Dead Man", "⚰️")
self.desc = "Al proprio turno il giocatore che è morto per primo torna in vita con 2 vite e 2 carte" self.desc = "Al proprio turno il giocatore che è morto per primo torna in vita con 2 vite e 2 carte"
self.desc_eng = "The first player that died return back to life with 2 hp and 2 cards" self.desc_eng = "The first player that died returns back to life with 2 hp and 2 cards"
class FratelliDiSangue(CardEvent): class FratelliDiSangue(CardEvent):
def __init__(self): def __init__(self):
@ -97,6 +97,11 @@ class Vendetta(CardEvent):
self.desc = "Alla fine del proprio turno il giocatore estrae dal mazzo, se esce ♥️ gioca un altro turno (ma non estrae di nuovo)" self.desc = "Alla fine del proprio turno il giocatore estrae dal mazzo, se esce ♥️ gioca un altro turno (ma non estrae di nuovo)"
self.desc_eng = "When ending the turn, the player flips a card from the deck, if it's ♥️ he plays another turn (but he does not flip another card)" self.desc_eng = "When ending the turn, the player flips a card from the deck, if it's ♥️ he plays another turn (but he does not flip another card)"
def get_endgame_card():
end_game = PerUnPugnoDiCarte()
end_game.expansion = 'fistful-of-cards'
return end_game
def get_all_events(): def get_all_events():
cards = [ cards = [
Agguato(), Agguato(),
@ -115,7 +120,6 @@ def get_all_events():
Vendetta(), Vendetta(),
] ]
random.shuffle(cards) random.shuffle(cards)
cards.append(PerUnPugnoDiCarte())
for c in cards: for c in cards:
c.expansion = 'fistful-of-cards' c.expansion = 'fistful-of-cards'
return cards return cards

View File

@ -0,0 +1,119 @@
import random
from bang.expansions.fistful_of_cards.card_events import CardEvent
class Benedizione(CardEvent):
def __init__(self):
super().__init__("Benedizione", "🙏")
self.desc = "Tutte le carte sono considerate di cuori ♥️"
self.desc_eng = "All cards are of hearts ♥️"
class Maledizione(CardEvent):
def __init__(self):
super().__init__("Maledizione", "🤬")
self.desc = "Tutte le carte sono considerate di picche ♠"
self.desc_eng = "All cards are of spades ♠"
class Sbornia(CardEvent):
def __init__(self):
super().__init__("Sbornia", "🥴")
self.desc = "I personaggi perdono le loro abilità speciali"
self.desc_eng = "The characters lose their special abilities"
class Sete(CardEvent):
def __init__(self):
super().__init__("Sete", "🥵")
self.desc = "I giocatori pescano 1 carta in meno nella loro fase 1"
self.desc_eng = "Players only draw 1 card at the start of their turn"
class IlTreno(CardEvent):
def __init__(self):
super().__init__("Il Treno", "🚂")
self.desc = "I giocatori pescano 1 carta extra nella loro fase 1"
self.desc_eng = "Players draw 1 extra card"
class IlReverendo(CardEvent):
def __init__(self):
super().__init__("Il Reverendo", "⛪️")
self.desc = "Non si possono giocare le carte Birra"
self.desc_eng = "Beers can't be played"
class IlDottore(CardEvent):
def __init__(self):
super().__init__("Il Dottore", "👨‍⚕️")
self.desc = "Il/i giocatore/i con meno vite ne recupera/no una"
self.desc_eng = "The player with the least amount of HP gets healed 1"
class Sermone(CardEvent):
def __init__(self):
super().__init__("Sermone", "✝️")
self.desc = "I giocatori non possono giocare Bang! durante il loro turno"
self.desc_eng = "Players can't play Bang! during their turn"
class Sparatoria(CardEvent):
def __init__(self):
super().__init__("Sparatoria", "🔫🔫")
self.desc = "Il limite di Bang! per turno è 2 invece che 1"
self.desc_eng = "The turn Bang! limit is 2"
class CorsaAllOro(CardEvent):
def __init__(self):
super().__init__("Corsa All'Oro", "🌟")
self.desc = "Si gioca per un intero giro in senso antiorario, tuttavia gli effetti delle carte rimangono invariati"
self.desc_eng = "Turns are played counter clockwise"
class IDalton(CardEvent):
def __init__(self):
super().__init__("I Dalton", "🙇‍♂️")
self.desc = "Chi ha carte blu in gioco ne scarta 1 a sua scelta"
self.desc_eng = "Players that have blue cards equipped, discard 1 of those card of their choice"
class Manette(CardEvent):
def __init__(self):
super().__init__("Manette", "🔗")
self.desc = "Dopo aver pescato in fase 1, il giocatore di turno dichiara un seme: potrà usare solamente carte di quel seme nel suo turno"
self.desc_eng = ""
class NuovaIdentita(CardEvent):
def __init__(self):
super().__init__("Nuova Identità", "🕶")
self.desc = "All'inizio del proprio turno, ogni giocatore potrà decidere se sostituire il suo personaggio attuale con quello era stato proposto ad inizio partita, se lo fa riparte con 2 punti vita"
self.desc_eng = ""
class CittaFantasma(CardEvent):
def __init__(self):
super().__init__("Città Fantasma", "👻")
self.desc = "Tutti i giocatori morti tornano in vita al proprio turno, non possono morire e pescano 3 carte invece che 2. Quando terminano il turno tornano morti."
self.desc_eng = "All dead players come back to life in their turn, they can't die and draw 3 cards instead of 2. When they end their turn the die."
class MezzogiornoDiFuoco(CardEvent):
def __init__(self):
super().__init__("Mezzogiorno di Fuoco", "🔥")
self.desc = "Ogni giocatore perde 1 punto vita all'inizio del turno"
self.desc_eng = "Every player loses 1 HP when their turn starts"
def get_endgame_card():
end_game = MezzogiornoDiFuoco()
end_game.expansion = 'high-noon'
return end_game
def get_all_events():
cards = [
Benedizione(),
Maledizione(),
CittaFantasma(),
CorsaAllOro(),
IDalton(),
IlDottore(),
IlReverendo(),
IlTreno(),
Sbornia(),
Sermone(),
Sete(),
Sparatoria(),
# Manette(),
# NuovaIdentita(),
]
random.shuffle(cards)
for c in cards:
c.expansion = 'high-noon'
return cards

View File

@ -2,12 +2,14 @@
from typing import List, Set, Dict, Tuple, Optional from typing import List, Set, Dict, Tuple, Optional
import random import random
import socketio import socketio
import eventlet
import bang.players as pl import bang.players as pl
import bang.characters as characters import bang.characters as characters
from bang.deck import Deck from bang.deck import Deck
import bang.roles as roles import bang.roles as roles
import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.fistful_of_cards.card_events as ce
import eventlet import bang.expansions.high_noon.card_events as ceh
class Game: class Game:
def __init__(self, name, sio:socketio): def __init__(self, name, sio:socketio):
@ -15,7 +17,7 @@ class Game:
self.sio = sio self.sio = sio
self.name = name self.name = name
self.players: List[pl.Player] = [] self.players: List[pl.Player] = []
self.dead_players: List[pl.Player] = [] self.spectators: List[pl.Player] = []
self.deck: Deck = None self.deck: Deck = None
self.started = False self.started = False
self.turn = 0 self.turn = 0
@ -24,25 +26,29 @@ class Game:
self.initial_players = 0 self.initial_players = 0
self.password = '' self.password = ''
self.expansions = [] self.expansions = []
self.available_expansions = ['dodge_city', 'fistful_of_cards'] self.available_expansions = ['dodge_city', 'fistful_of_cards', 'high_noon']
self.shutting_down = False self.shutting_down = False
self.is_competitive = False self.is_competitive = False
self.disconnect_bot = True self.disconnect_bot = True
self.player_bangs = 0 self.player_bangs = 0
self.is_russian_roulette_on = False self.is_russian_roulette_on = False
self.dalton_on = False
self.bot_speed = 1.5 self.bot_speed = 1.5
self.incremental_turn = 0
self.did_resuscitate_deadman = False
self.is_handling_death = False
def notify_room(self, sid=None): def notify_room(self, sid=None):
if len([p for p in self.players if p.character == None]) != 0 or sid: if len([p for p in self.players if p.character == None]) != 0 or sid:
self.sio.emit('room', room=self.name if not sid else sid, data={ self.sio.emit('room', room=self.name if not sid else sid, data={
'name': self.name, 'name': self.name,
'started': self.started, 'started': self.started,
'players': [{'name':p.name, 'ready': p.character != None} for p in self.players], 'players': [{'name':p.name, 'ready': p.character != None, 'is_bot': p.is_bot} for p in self.players],
'password': self.password, 'password': self.password,
'is_competitive': self.is_competitive, 'is_competitive': self.is_competitive,
'disconnect_bot': self.disconnect_bot, 'disconnect_bot': self.disconnect_bot,
'expansions': self.expansions, 'expansions': self.expansions,
'available_expansions': self.available_expansions 'available_expansions': self.available_expansions,
}) })
def toggle_expansion(self, expansion_name): def toggle_expansion(self, expansion_name):
@ -148,7 +154,7 @@ class Game:
attacker.notify_self() attacker.notify_self()
self.waiting_for = 0 self.waiting_for = 0
self.readyCount = 0 self.readyCount = 0
for p in self.players: for p in self.get_alive_players():
if p != attacker: if p != attacker:
if p.get_banged(attacker=attacker): if p.get_banged(attacker=attacker):
self.waiting_for += 1 self.waiting_for += 1
@ -162,7 +168,7 @@ class Game:
attacker.notify_self() attacker.notify_self()
self.waiting_for = 0 self.waiting_for = 0
self.readyCount = 0 self.readyCount = 0
for p in self.players: for p in self.get_alive_players():
if p != attacker: if p != attacker:
if p.get_indians(attacker=attacker): if p.get_indians(attacker=attacker):
self.waiting_for += 1 self.waiting_for += 1
@ -196,7 +202,8 @@ class Game:
self.get_player_named(target_username).notify_self() self.get_player_named(target_username).notify_self()
def emporio(self): def emporio(self):
self.available_cards = [self.deck.draw(True) for i in range(len([p for p in self.players if p.lives > 0]))] pls = self.get_alive_players()
self.available_cards = [self.deck.draw(True) for i in range(len(pls))]
self.players[self.turn].pending_action = pl.PendingAction.CHOOSE self.players[self.turn].pending_action = pl.PendingAction.CHOOSE
self.players[self.turn].choose_text = 'choose_card_to_get' self.players[self.turn].choose_text = 'choose_card_to_get'
self.players[self.turn].available_cards = self.available_cards self.players[self.turn].available_cards = self.available_cards
@ -207,13 +214,14 @@ class Game:
player.available_cards = [] player.available_cards = []
player.pending_action = pl.PendingAction.WAIT player.pending_action = pl.PendingAction.WAIT
player.notify_self() player.notify_self()
nextPlayer = self.players[(self.turn + (len(self.players)-len(self.available_cards))) % len(self.players)] pls = self.get_alive_players()
nextPlayer = pls[(pls.index(self.players[self.turn])+(len(pls)-len(self.available_cards))) % len(pls)]
if nextPlayer == self.players[self.turn]: if nextPlayer == self.players[self.turn]:
self.players[self.turn].pending_action = pl.PendingAction.PLAY self.players[self.turn].pending_action = pl.PendingAction.PLAY
self.players[self.turn].notify_self() self.players[self.turn].notify_self()
else: else:
nextPlayer.pending_action = pl.PendingAction.CHOOSE nextPlayer.pending_action = pl.PendingAction.CHOOSE
self.players[self.turn].choose_text = 'choose_card_to_get' nextPlayer.choose_text = 'choose_card_to_get'
nextPlayer.available_cards = self.available_cards nextPlayer.available_cards = self.available_cards
nextPlayer.notify_self() nextPlayer.notify_self()
@ -235,17 +243,20 @@ class Game:
self.player_bangs = 0 self.player_bangs = 0
self.players[self.turn].play_turn() self.players[self.turn].play_turn()
elif self.is_russian_roulette_on and self.check_event(ce.RouletteRussa): elif self.is_russian_roulette_on and self.check_event(ce.RouletteRussa):
pls = self.get_alive_players()
if did_lose: if did_lose:
target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
print('stop roulette') print('stop roulette')
self.players[(self.turn+self.player_bangs) % len(self.players)].lives -= 1 target_pl.lives -= 1
self.players[(self.turn+self.player_bangs) % len(self.players)].notify_self() target_pl.notify_self()
self.is_russian_roulette_on = False self.is_russian_roulette_on = False
self.players[self.turn].play_turn() self.players[self.turn].play_turn()
else: else:
self.player_bangs += 1 self.player_bangs += 1
print(f'next in line {self.players[(self.turn+self.player_bangs) % len(self.players)].name}') target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
if self.players[(self.turn+self.player_bangs) % len(self.players)].get_banged(self.deck.event_cards[0]): print(f'next in line {target_pl.name}')
self.players[(self.turn+self.player_bangs) % len(self.players)].notify_self() if target_pl.get_banged(self.deck.event_cards[0]):
target_pl.notify_self()
else: else:
self.responders_did_respond_resume_turn(did_lose=True) self.responders_did_respond_resume_turn(did_lose=True)
else: else:
@ -253,23 +264,44 @@ class Game:
if self.readyCount == self.waiting_for: if self.readyCount == self.waiting_for:
self.waiting_for = 0 self.waiting_for = 0
self.readyCount = 0 self.readyCount = 0
self.players[self.turn].pending_action = pl.PendingAction.PLAY if self.dalton_on:
self.dalton_on = False
print(f'notifying {self.players[self.turn].name} about his turn')
self.players[self.turn].play_turn()
else:
self.players[self.turn].pending_action = pl.PendingAction.PLAY
self.players[self.turn].notify_self() self.players[self.turn].notify_self()
def next_player(self): def next_player(self):
return self.players[(self.turn + 1) % len(self.players)] pls = self.get_alive_players()
return pls[(pls.index(self.players[self.turn]) + 1) % len(pls)]
def play_turn(self): def play_turn(self):
self.incremental_turn += 1
if self.players[self.turn].is_dead:
pl = sorted(self.get_dead_players(), key=lambda x:x.death_turn)[0]
if self.check_event(ce.DeadMan) and not self.did_resuscitate_deadman and pl == self.players[self.turn]:
print(f'{self.players[self.turn]} is dead, revive')
self.did_resuscitate_deadman = True
pl.is_dead = False
pl.is_ghost = False
pl.lives = 2
pl.hand.append(self.deck.draw())
pl.hand.append(self.deck.draw())
pl.notify_self()
elif self.check_event(ceh.CittaFantasma):
print(f'{self.players[self.turn]} is dead, event ghost')
self.players[self.turn].is_ghost = True
else:
print(f'{self.players[self.turn]} is dead, next turn')
return self.next_turn()
self.player_bangs = 0 self.player_bangs = 0
if isinstance(self.players[self.turn].role, roles.Sheriff): if isinstance(self.players[self.turn].role, roles.Sheriff):
self.deck.flip_event() self.deck.flip_event()
if self.check_event(ce.DeadMan) and len(self.dead_players) > 0: if len(self.deck.event_cards) > 0 and self.deck.event_cards[0] != None:
self.players.append(self.dead_players.pop(0)) print(f'flip new event {self.deck.event_cards[0].name}')
self.players[-1].lives = 2 if self.check_event(ce.DeadMan):
self.players[-1].hand.append(self.deck.draw()) self.did_resuscitate_deadman = False
self.players[-1].hand.append(self.deck.draw())
self.players_map = {c.name: i for i, c in enumerate(self.players)}
self.players[-1].notify_self()
elif self.check_event(ce.RouletteRussa): elif self.check_event(ce.RouletteRussa):
self.is_russian_roulette_on = True self.is_russian_roulette_on = True
if self.players[self.turn].get_banged(self.deck.event_cards[0]): if self.players[self.turn].get_banged(self.deck.event_cards[0]):
@ -277,6 +309,26 @@ class Game:
else: else:
self.responders_did_respond_resume_turn(did_lose=True) self.responders_did_respond_resume_turn(did_lose=True)
return return
elif self.check_event(ceh.IlDottore):
most_hurt = [p.lives for p in self.players if p.lives > 0 and p.max_lives > p.lives]
if len(most_hurt) > 0:
hurt_players = [p for p in self.players if p.lives == min(most_hurt)]
for p in hurt_players:
p.lives += 1
self.sio.emit('chat_message', room=self.name, data=f'_doctor_heal|{p.name}')
p.notify_self()
elif self.check_event(ceh.IDalton):
self.waiting_for = 0
self.readyCount = 0
self.dalton_on = True
for p in self.players:
if p.get_dalton():
self.waiting_for += 1
p.notify_self()
if self.waiting_for != 0:
return
self.dalton_on = False
if self.check_event(ce.PerUnPugnoDiCarte) and len(self.players[self.turn].hand) > 0: if self.check_event(ce.PerUnPugnoDiCarte) and len(self.players[self.turn].hand) > 0:
self.player_bangs = len(self.players[self.turn].hand) self.player_bangs = len(self.players[self.turn].hand)
if self.players[self.turn].get_banged(self.deck.event_cards[0]): if self.players[self.turn].get_banged(self.deck.event_cards[0]):
@ -284,12 +336,18 @@ class Game:
else: else:
self.responders_did_respond_resume_turn() self.responders_did_respond_resume_turn()
else: else:
print(f'notifying {self.players[self.turn].name} about his turn')
self.players[self.turn].play_turn() self.players[self.turn].play_turn()
def next_turn(self): def next_turn(self):
if self.shutting_down: return if self.shutting_down: return
if len(self.players) > 0: print(f'{self.players[self.turn].name} invoked next turn')
self.turn = (self.turn + 1) % len(self.players) pls = self.get_alive_players()
if len(pls) > 0:
if self.check_event(ceh.CorsaAllOro):
self.turn = (self.turn - 1) % len(self.players)
else:
self.turn = (self.turn + 1) % len(self.players)
self.play_turn() self.play_turn()
def notify_event_card(self): def notify_event_card(self):
@ -308,26 +366,35 @@ class Game:
def handle_disconnect(self, player: pl.Player): def handle_disconnect(self, player: pl.Player):
print(f'player {player.name} left the game {self.name}') print(f'player {player.name} left the game {self.name}')
if player in self.players: if player in self.spectators:
if self.disconnect_bot and self.started: self.spectators.remove(player)
player.is_bot = True return False
eventlet.sleep(15) # he may reconnect if player.is_bot and not self.started:
player.notify_self() player.game = None
else: if self.disconnect_bot and self.started:
self.player_death(player=player, disconnected=True) player.is_bot = True
eventlet.sleep(15) # he may reconnect
if player.is_bot:
if len(player.available_characters) > 0:
player.set_available_character(player.available_characters)
player.bot_spin()
else: else:
self.dead_players.remove(player) self.player_death(player=player, disconnected=True)
if len([p for p in self.players if not p.is_bot])+len([p for p in self.dead_players if not p.is_bot]) == 0: # else:
print(f'no players left in game {self.name}') # player.lives = 0
# self.players.remove(player)
if len([p for p in self.players if not p.is_bot]) == 0:
print(f'no players left in game {self.name}, shutting down')
self.shutting_down = True self.shutting_down = True
self.players = [] self.players = []
self.dead_players = [] self.spectators = []
self.deck = None self.deck = None
return True return True
else: return False else: return False
def player_death(self, player: pl.Player, disconnected=False): def player_death(self, player: pl.Player, disconnected=False):
if not player in self.players: return if not player in self.players or player.is_ghost: return
self.is_handling_death = True
import bang.expansions.dodge_city.characters as chd import bang.expansions.dodge_city.characters as chd
print(player.attacker) print(player.attacker)
if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice): if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice):
@ -344,15 +411,20 @@ class Game:
if (self.waiting_for > 0): if (self.waiting_for > 0):
self.responders_did_respond_resume_turn() self.responders_did_respond_resume_turn()
if not player in self.players: return if player.is_dead: return
index = self.players.index(player) if not self.started:
died_in_his_turn = self.started and index == self.turn self.players.remove(player)
if self.started and index <= self.turn: elif disconnected:
self.turn -= 1 self.players.remove(player)
self.players_map = {c.name: i for i, c in enumerate(self.players)}
player.lives = 0
player.is_dead = True
player.death_turn = self.incremental_turn
corpse = self.players.pop(index) # corpse = self.players.pop(index)
if not disconnected: corpse = player
self.dead_players.append(corpse) # if not disconnected:
# self.dead_players.append(corpse)
self.notify_room() self.notify_room()
self.sio.emit('chat_message', room=self.name, data=f'_died|{player.name}') self.sio.emit('chat_message', room=self.name, data=f'_died|{player.name}')
if self.started: if self.started:
@ -360,23 +432,25 @@ class Game:
for p in self.players: for p in self.players:
if not p.is_bot: if not p.is_bot:
p.notify_self() p.notify_self()
self.players_map = {c.name: i for i, c in enumerate(self.players)} # self.players_map = {c.name: i for i, c in enumerate(self.players)}
if self.started: if self.started:
print('Check win status') print('Check win status')
attacker_role = None attacker_role = None
if player.attacker and player.attacker in self.players: if player.attacker and player.attacker in self.players:
attacker_role = player.attacker.role attacker_role = player.attacker.role
winners = [p for p in self.players if p.role != None and p.role.on_player_death(self.players, initial_players=self.initial_players, dead_role=player.role, attacker_role=attacker_role)] winners = [p for p in self.players if p.role != None and p.role.on_player_death(self.get_alive_players(), initial_players=self.initial_players, dead_role=player.role, attacker_role=attacker_role)]
if len(winners) > 0: if len(winners) > 0:
print('WE HAVE A WINNER') print('WE HAVE A WINNER')
for p in self.players: for p in self.get_alive_players():
p.win_status = p in winners p.win_status = p in winners
self.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}') self.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}')
p.notify_self() p.notify_self()
eventlet.sleep(5.0) for i in range(5):
self.sio.emit('chat_message', room=self.name, data=f'_lobby_reset|{5-i}')
eventlet.sleep(1)
return self.reset() return self.reset()
vulture = [p for p in self.players if isinstance(p.character, characters.VultureSam)] vulture = [p for p in self.get_alive_players() if p.character.check(self, characters.VultureSam)]
if len(vulture) == 0: if len(vulture) == 0:
for i in range(len(player.hand)): for i in range(len(player.hand)):
self.deck.scrap(player.hand.pop(), True) self.deck.scrap(player.hand.pop(), True)
@ -397,31 +471,35 @@ class Game:
vulture[0].notify_self() vulture[0].notify_self()
#se Vulture Sam è uno sceriffo e ha appena ucciso il suo Vice, deve scartare le carte che ha pescato con la sua abilità #se Vulture Sam è uno sceriffo e ha appena ucciso il suo Vice, deve scartare le carte che ha pescato con la sua abilità
if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice): if player.attacker and player.attacker in self.get_alive_players() and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice):
for i in range(len(player.attacker.hand)): for i in range(len(player.attacker.hand)):
self.deck.scrap(player.attacker.hand.pop(), True) self.deck.scrap(player.attacker.hand.pop(), True)
player.attacker.notify_self() player.attacker.notify_self()
greg = [p for p in self.players if isinstance(p.character, chd.GregDigger)] greg = [p for p in self.get_alive_players() if p.character.check(self, chd.GregDigger)]
if len(greg) > 0: if len(greg) > 0:
greg[0].lives = min(greg[0].lives+2, greg[0].max_lives) greg[0].lives = min(greg[0].lives+2, greg[0].max_lives)
herb = [p for p in self.players if isinstance(p.character, chd.HerbHunter)] herb = [p for p in self.get_alive_players() if p.character.check(self, chd.HerbHunter)]
if len(herb) > 0: if len(herb) > 0:
herb[0].hand.append(self.deck.draw(True)) herb[0].hand.append(self.deck.draw(True))
herb[0].hand.append(self.deck.draw(True)) herb[0].hand.append(self.deck.draw(True))
herb[0].notify_self() herb[0].notify_self()
self.is_handling_death = False
if died_in_his_turn: if corpse.is_my_turn:
self.next_turn() self.next_turn()
def reset(self): def reset(self):
print('resetting lobby') print('resetting lobby')
self.players.extend(self.dead_players) self.players.extend(self.spectators)
self.dead_players = [] self.spectators = []
for bot in [p for p in self.players if p.is_bot]:
bot.game = None
self.players = [p for p in self.players if not p.is_bot] self.players = [p for p in self.players if not p.is_bot]
print(self.players) print(self.players)
self.started = False self.started = False
self.is_handling_death = False
self.waiting_for = 0 self.waiting_for = 0
self.incremental_turn = 0
for p in self.players: for p in self.players:
p.reset() p.reset()
p.notify_self() p.notify_self()
@ -429,21 +507,31 @@ class Game:
self.notify_room() self.notify_room()
def check_event(self, ev): def check_event(self, ev):
if len(self.deck.event_cards) == 0: return False if self.deck == None or len(self.deck.event_cards) == 0: return False
return isinstance(self.deck.event_cards[0], ev) return isinstance(self.deck.event_cards[0], ev)
def get_visible_players(self, player: pl.Player): def get_visible_players(self, player: pl.Player):
i = self.players.index(player) pls = self.get_alive_players()
if len(pls) == 0 or player not in pls: return []
i = pls.index(player)
sight = player.get_sight() sight = player.get_sight()
mindist = 99 if not self.check_event(ce.Agguato) else 1 mindist = 99 if not self.check_event(ce.Agguato) else 1
return [{ return [{
'name': self.players[j].name, 'name': pls[j].name,
'dist': min([abs(i - j), (i+ abs(j-len(self.players))), (j+ abs(i-len(self.players))), mindist]) + self.players[j].get_visibility() - (player.get_sight(countWeapon=False)-1), 'dist': min([abs(i - j), (i+ abs(j-len(pls))), (j+ abs(i-len(pls))), mindist]) + pls[j].get_visibility() - (player.get_sight(countWeapon=False)-1),
'lives': self.players[j].lives, 'lives': pls[j].lives,
'max_lives': self.players[j].max_lives, 'max_lives': pls[j].max_lives,
'is_sheriff': isinstance(self.players[j].role, roles.Sheriff), 'is_sheriff': isinstance(pls[j].role, roles.Sheriff),
'cards': len(self.players[j].hand)+len(self.players[j].equipment) 'cards': len(pls[j].hand)+len(pls[j].equipment),
} for j in range(len(self.players)) if i != j] 'is_ghost': pls[j].is_ghost,
'is_bot': pls[j].is_bot,
} for j in range(len(pls)) if i != j]
def get_alive_players(self):
return [p for p in self.players if not p.is_dead or p.is_ghost]
def get_dead_players(self):
return [p for p in self.players if p.is_dead]
def notify_all(self): def notify_all(self):
if self.started: if self.started:
@ -458,6 +546,8 @@ class Game:
'pending_action': p.pending_action, 'pending_action': p.pending_action,
'character': p.character.__dict__ if p.character else None, 'character': p.character.__dict__ if p.character else None,
'real_character': p.real_character.__dict__ if p.real_character else None, 'real_character': p.real_character.__dict__ if p.real_character else None,
'icon': p.role.icon if self.initial_players == 3 and p.role else '🤠' 'icon': p.role.icon if self.initial_players == 3 and p.role else '🤠',
} for p in self.players] 'is_ghost': p.is_ghost,
'is_bot': p.is_bot,
} for p in self.get_alive_players()]
self.sio.emit('players_update', room=self.name, data=data) self.sio.emit('players_update', room=self.name, data=data)

View File

@ -9,6 +9,7 @@ import bang.expansions.dodge_city.cards as csd
import bang.characters as chars import bang.characters as chars
import bang.expansions.dodge_city.characters as chd import bang.expansions.dodge_city.characters as chd
import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.fistful_of_cards.card_events as ce
import bang.expansions.high_noon.card_events as ceh
import eventlet import eventlet
class PendingAction(IntEnum): class PendingAction(IntEnum):
@ -58,7 +59,11 @@ class Player:
self.mancato_needed = 0 self.mancato_needed = 0
self.molly_discarded_cards = 0 self.molly_discarded_cards = 0
self.is_bot = bot self.is_bot = bot
self.bang_used = 0
self.special_use_count = 0 self.special_use_count = 0
self.is_dead = False
self.death_turn = 0
self.is_ghost = False
def reset(self): def reset(self):
self.hand: cs.Card = [] self.hand: cs.Card = []
@ -91,6 +96,9 @@ class Player:
pass pass
self.mancato_needed = 0 self.mancato_needed = 0
self.molly_discarded_cards = 0 self.molly_discarded_cards = 0
self.is_dead = False
self.is_ghost = False
self.death_turn = 0
def join_game(self, game): def join_game(self, game):
self.game = game self.game = game
@ -156,6 +164,7 @@ class Player:
self.sio.emit('notify_card', room=self.sid, data=mess) self.sio.emit('notify_card', room=self.sid, data=mess)
def notify_self(self): def notify_self(self):
if self.is_ghost: self.lives = 0
if self.pending_action == PendingAction.DRAW and self.game.check_event(ce.Peyote): if self.pending_action == PendingAction.DRAW and self.game.check_event(ce.Peyote):
self.available_cards = [{ self.available_cards = [{
'icon': '🔴' 'icon': '🔴'
@ -173,11 +182,11 @@ class Player:
self.is_playing_ranch = True self.is_playing_ranch = True
self.choose_text = 'choose_ranch' self.choose_text = 'choose_ranch'
self.pending_action = PendingAction.CHOOSE self.pending_action = PendingAction.CHOOSE
elif isinstance(self.character, chars.SuzyLafayette) and len(self.hand) == 0 and ( not self.is_my_turn or self.pending_action == PendingAction.PLAY): 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.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
if self.lives <= 0 and self.max_lives > 0: if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
print('dying, attacker', self.attacker) print('dying, attacker', self.attacker)
if isinstance(self.character, chars.SidKetchum) and len(self.hand) > 1: if self.character.check(self.game, chars.SidKetchum) and len(self.hand) > 1:
self.lives += 1 self.lives += 1
#TODO Sid dovrebbe poter decidere cosa scartare #TODO Sid dovrebbe poter decidere cosa scartare
self.game.deck.scrap(self.hand.pop( self.game.deck.scrap(self.hand.pop(
@ -190,36 +199,36 @@ class Player:
ser.pop('sid') ser.pop('sid')
ser.pop('on_pick_cb') ser.pop('on_pick_cb')
ser.pop('on_failed_response_cb') ser.pop('on_failed_response_cb')
# ser.pop('expected_response')
ser.pop('attacker') ser.pop('attacker')
if self.attacker: if self.attacker:
ser['attacker'] = self.attacker.name ser['attacker'] = self.attacker.name
ser['sight'] = self.get_sight() ser['sight'] = self.get_sight()
ser['lives'] = max(ser['lives'], 0) ser['lives'] = max(ser['lives'], 0)
if self.lives <= 0 and self.max_lives > 0: if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
self.pending_action = PendingAction.WAIT self.pending_action = PendingAction.WAIT
ser['hand'] = [] ser['hand'] = []
ser['equipment'] = [] ser['equipment'] = []
self.sio.emit('self', room=self.sid, data=json.dumps( self.sio.emit('self', room=self.sid, data=json.dumps(
ser, default=lambda o: o.__dict__)) ser, default=lambda o: o.__dict__))
self.game.player_death(self) self.game.player_death(self)
elif not self.is_bot: if self.game: # falso quando un bot viene eliminato dalla partita
self.sio.emit('self_vis', room=self.sid, data=json.dumps( self.sio.emit('self_vis', room=self.sid, data=json.dumps(self.game.get_visible_players(self), default=lambda o: o.__dict__))
self.game.get_visible_players(self), default=lambda o: o.__dict__))
if not self.is_bot:
self.sio.emit('self', room=self.sid, data=json.dumps(
ser, default=lambda o: o.__dict__))
self.game.notify_all()
else:
self.game.notify_all()
self.bot_logic()
self.game.notify_all() self.game.notify_all()
self.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 != None and not self.game.shutting_down:
eventlet.sleep(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): def bot_logic(self):
if self.game.shutting_down: return if self.game == None or self.game.shutting_down: return
if self.pending_action != None and self.pending_action != PendingAction.WAIT: if self.pending_action != None and self.pending_action != PendingAction.WAIT:
eventlet.sleep(uniform(self.game.bot_speed/2-0.1, self.game.bot_speed)) # eventlet.sleep(uniform(self.game.bot_speed/2-0.1, self.game.bot_speed))
pass
else: else:
return return
if self.pending_action == PendingAction.PICK: if self.pending_action == PendingAction.PICK:
@ -228,8 +237,10 @@ class Player:
self.draw('') self.draw('')
elif self.pending_action == PendingAction.PLAY: elif self.pending_action == PendingAction.PLAY:
equippables = [c for c in self.hand if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not any([type(c) == type(x) for x in self.equipment])] equippables = [c for c in self.hand if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not any([type(c) == type(x) for x in self.equipment])]
misc = [c for c in self.hand if (isinstance(c, cs.WellsFargo) and not c.usable_next_turn) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or (isinstance(c, cs.Birra) and self.lives < self.max_lives)] misc = [c for c in self.hand if (isinstance(c, cs.WellsFargo) and not c.usable_next_turn) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or (isinstance(c, cs.Birra) and self.lives < self.max_lives and not self.game.check_event(ceh.IlReverendo))]
need_target = [c for c in self.hand if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (self.has_played_bang and not (any([isinstance(c, cs.Volcanic) for c in self.equipment]) and not self.game.check_event(ce.Lazo))) and not ( isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice))] need_target = [c for c in self.hand if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (
(self.game.check_event(ceh.Sermone) or self.has_played_bang and not (any([isinstance(c, cs.Volcanic) for c in self.equipment]) and type(c) == type(cs.Bang)
) and not self.game.check_event(ce.Lazo))) and not ( isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice))]
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] 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 len(equippables) > 0 and not self.game.check_event(ce.IlGiudice): if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice):
for c in equippables: for c in equippables:
@ -243,7 +254,7 @@ class Player:
for c in need_target: for c in need_target:
_range = self.get_sight() if c.name == 'Bang!' or c.name == "Pepperbox" else c.range _range = self.get_sight() if c.name == 'Bang!' or c.name == "Pepperbox" else c.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 p['lives'] > 0 and not ((isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico)) and p['cards'] == 0) and not (p['is_sheriff'] and isinstance(c, cs.Prigione))] 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 p['lives'] > 0 and not ((isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico)) and p['cards'] == 0) and not (p['is_sheriff'] and isinstance(c, cs.Prigione))]
if len(others) == 0: if len(others) == 0 or c not in self.hand:
continue continue
target = others[randrange(0, len(others))] target = others[randrange(0, len(others))]
if target['is_sheriff'] and isinstance(self.role, r.Renegade): if target['is_sheriff'] and isinstance(self.role, r.Renegade):
@ -271,7 +282,7 @@ class Player:
if self.play_card(len(self.hand)+self.equipment.index(c), against=target['name']): if self.play_card(len(self.hand)+self.equipment.index(c), against=target['name']):
return return
break break
maxcards = self.lives if not isinstance(self.character, chd.SeanMallory) else 10 maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
if len(self.hand) > maxcards: if len(self.hand) > maxcards:
self.scrap(0) self.scrap(0)
else: else:
@ -302,7 +313,7 @@ class Player:
self.choose(randrange(0, len(target.hand)+len(target.equipment))) self.choose(randrange(0, len(target.hand)+len(target.equipment)))
def play_turn(self, can_play_vendetta = True): def play_turn(self, can_play_vendetta = True):
if self.lives == 0: if (self.lives == 0 or self.is_dead) and not self.is_ghost:
return self.end_turn(forced=True) return self.end_turn(forced=True)
self.scrapped_cards = 0 self.scrapped_cards = 0
self.can_play_ranch = True self.can_play_ranch = True
@ -316,12 +327,23 @@ class Player:
self.is_waiting_for_action = True self.is_waiting_for_action = True
self.has_played_bang = False self.has_played_bang = False
self.special_use_count = 0 self.special_use_count = 0
if self.game.check_event(ce.FratelliDiSangue) and self.lives > 1 and not self.is_giving_life and len([p for p in self.game.players if p != self and p.lives < p.max_lives]): self.bang_used = 0
if self.game.check_event(ceh.MezzogiornoDiFuoco):
self.lives -= 1
if self.character.check(self.game, chars.BartCassidy) and self.lives > 0:
self.hand.append(self.game.deck.draw(True))
self.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 len([p for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]):
self.available_cards = [{ self.available_cards = [{
'name': p.name, 'name': p.name,
'icon': '⭐️' if isinstance(p.role, r.Sheriff) else '🤠', 'icon': '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)) 'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives))
} for p in self.game.players if p != self and p.lives < p.max_lives] } for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]
self.available_cards.append({'icon': ''}) self.available_cards.append({'icon': ''})
self.choose_text = 'choose_fratelli_di_sangue' self.choose_text = 'choose_fratelli_di_sangue'
self.pending_action = PendingAction.CHOOSE self.pending_action = PendingAction.CHOOSE
@ -332,7 +354,7 @@ class Player:
else: else:
self.is_giving_life = False self.is_giving_life = False
if isinstance(self.real_character, chd.VeraCuster): if isinstance(self.real_character, chd.VeraCuster):
self.set_available_character([p.character for p in self.game.players if p != self]) self.set_available_character([p.character for p in self.game.get_alive_players() if p != self])
else: else:
self.pending_action = PendingAction.DRAW self.pending_action = PendingAction.DRAW
self.notify_self() self.notify_self()
@ -353,7 +375,7 @@ class Player:
self.available_cards = [{ self.available_cards = [{
'name': p.name, 'name': p.name,
'icon': '⭐️' if isinstance(p.role, r.Sheriff) else '🤠' 'icon': '⭐️' if isinstance(p.role, r.Sheriff) else '🤠'
} for p in self.game.players if len(p.equipment) > 0 and p != self] } for p in self.game.get_alive_players() if len(p.equipment) > 0 and p != self]
self.available_cards.append({'icon': ''}) self.available_cards.append({'icon': ''})
self.choose_text = 'choose_rimbalzo_player' self.choose_text = 'choose_rimbalzo_player'
self.pending_action = PendingAction.CHOOSE self.pending_action = PendingAction.CHOOSE
@ -365,48 +387,62 @@ class Player:
self.lives += 1 self.lives += 1
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
self.notify_self() self.notify_self()
elif isinstance(self.character, chars.KitCarlson): elif self.character.check(self.game, chars.KitCarlson):
self.is_drawing = True self.is_drawing = True
self.available_cards = [self.game.deck.draw() for i in range(3)] self.available_cards = [self.game.deck.draw() for i in range(3)]
self.choose_text = 'choose_card_to_get' self.choose_text = 'choose_card_to_get'
self.pending_action = PendingAction.CHOOSE self.pending_action = PendingAction.CHOOSE
self.notify_self() self.notify_self()
elif isinstance(self.character, 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: 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.is_drawing = True
self.available_cards = self.game.get_player_named(pile).equipment self.available_cards = self.game.get_player_named(pile).equipment
self.pat_target = pile
self.choose_text = 'choose_card_to_get' self.choose_text = 'choose_card_to_get'
self.pending_action = PendingAction.CHOOSE self.pending_action = PendingAction.CHOOSE
self.notify_self() self.notify_self()
else: else:
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
if pile == 'scrap' and isinstance(self.character, chars.PedroRamirez): if pile == 'scrap' and self.character.check(self.game, chars.PedroRamirez):
self.hand.append(self.game.deck.draw_from_scrap_pile()) self.hand.append(self.game.deck.draw_from_scrap_pile())
self.hand.append(self.game.deck.draw()) if not self.game.check_event(ceh.Sete):
self.hand.append(self.game.deck.draw())
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.ceck_event(ceh.CittaFantasma)):
self.hand.append(self.game.deck.draw())
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_draw_from_scrap|{self.name}') data=f'_draw_from_scrap|{self.name}')
elif type(pile) == str and pile != self.name and pile in self.game.players_map and isinstance(self.character, chars.JesseJones) and len(self.game.get_player_named(pile).hand) > 0: elif 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( self.hand.append(self.game.get_player_named(pile).hand.pop(
randrange(0, len(self.game.get_player_named(pile).hand)))) randrange(0, len(self.game.get_player_named(pile).hand))))
self.game.get_player_named(pile).notify_self() self.game.get_player_named(pile).notify_self()
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_draw_from_player|{self.name}|{pile}') data=f'_draw_from_player|{self.name}|{pile}')
if not self.game.check_event(ceh.Sete):
self.hand.append(self.game.deck.draw())
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.ceck_event(ceh.CittaFantasma)):
self.hand.append(self.game.deck.draw())
elif self.character.check(self.game, chd.BillNoface):
self.hand.append(self.game.deck.draw()) self.hand.append(self.game.deck.draw())
elif isinstance(self.character, chd.BillNoface): if not self.game.check_event(ceh.Sete):
self.hand.append(self.game.deck.draw()) for i in range(self.max_lives-self.lives):
for i in range(self.max_lives-self.lives): self.hand.append(self.game.deck.draw())
if self.game.check_event(ceh.IlTreno):
self.hand.append(self.game.deck.draw()) self.hand.append(self.game.deck.draw())
else: else:
for i in range(2): for i in range(2):
card: cs.Card = self.game.deck.draw() card: cs.Card = self.game.deck.draw()
self.hand.append(card) self.hand.append(card)
if i == 1 and isinstance(self.character, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest): if i == 1 and self.character.check(self.game, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest):
for p in self.game.players: for p in self.game.get_alive_players():
if p != self: if p != self:
p.notify_card(self, card, 'blackjack_special' if isinstance(self.character, chars.BlackJack) else 'foc.leggedelwest') p.notify_card(self, card, 'blackjack_special' if self.character.check(self.game, chars.BlackJack) else 'foc.leggedelwest')
if card.suit == cs.Suit.HEARTS or card.suit == cs.Suit.DIAMONDS and isinstance(self.character, chars.BlackJack): if card.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]) and self.character.check(self.game, chars.BlackJack):
self.hand.append(self.game.deck.draw()) self.hand.append(self.game.deck.draw())
if isinstance(self.character, chd.PixiePete): if self.game.check_event(ceh.Sete):
return self.notify_self()
if self.character.check(self.game, chd.PixiePete):
self.hand.append(self.game.deck.draw()) self.hand.append(self.game.deck.draw())
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.ceck_event(ceh.CittaFantasma)):
self.hand.append(self.game.deck.draw())
self.notify_self() self.notify_self()
def pick(self): def pick(self):
@ -422,12 +458,12 @@ class Player:
print(f'Did pick {picked}') print(f'Did pick {picked}')
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_flipped|{self.name}|{picked}') data=f'_flipped|{self.name}|{picked}')
if picked.suit == cs.Suit.SPADES and 2 <= picked.number <= 9 and pickable_cards == 0: if picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0:
self.lives -= 3 self.lives -= 3
self.game.deck.scrap(self.equipment.pop(i), True) self.game.deck.scrap(self.equipment.pop(i), True)
self.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}') self.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}')
self.heal_if_needed() self.heal_if_needed()
if isinstance(self.character, chars.BartCassidy) and self.lives > 0: if self.character.check(self.game, chars.BartCassidy) and self.lives > 0:
for i in range(3): for i in range(3):
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}') self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}')
@ -448,7 +484,7 @@ class Player:
print(f'Did pick {picked}') print(f'Did pick {picked}')
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_flipped|{self.name}|{picked}') data=f'_flipped|{self.name}|{picked}')
if picked.suit != cs.Suit.HEARTS and pickable_cards == 0: if not picked.check_suit(self.game, [cs.Suit.HEARTS]) and pickable_cards == 0:
self.game.deck.scrap(self.equipment.pop(i), True) self.game.deck.scrap(self.equipment.pop(i), True)
self.end_turn(forced=True) self.end_turn(forced=True)
return return
@ -460,7 +496,7 @@ class Player:
self.notify_self() self.notify_self()
return return
if isinstance(self.real_character, chd.VeraCuster): if isinstance(self.real_character, chd.VeraCuster):
self.set_available_character([p.character for p in self.game.players if p != self]) self.set_available_character([p.character for p in self.game.get_alive_players() if p != self])
else: else:
self.pending_action = PendingAction.DRAW self.pending_action = PendingAction.DRAW
self.notify_self() self.notify_self()
@ -486,7 +522,7 @@ class Player:
return s return s
def play_card(self, hand_index: int, against=None, _with=None): def play_card(self, hand_index: int, against=None, _with=None):
if not self.is_my_turn or self.pending_action != PendingAction.PLAY: if not self.is_my_turn or self.pending_action != PendingAction.PLAY or self.game.is_handling_death:
return return
if not (0 <= hand_index < len(self.hand) + len(self.equipment)): if not (0 <= hand_index < len(self.hand) + len(self.equipment)):
return return
@ -497,7 +533,7 @@ class Player:
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with) print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
did_play_card = False 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) 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)
if not(against != None and isinstance(self.game.get_player_named(against).character, chd.ApacheKid) and card.suit == cs.Suit.DIAMONDS) and not event_blocks_card: if not(against != None and isinstance(self.game.get_player_named(against).character, chd.ApacheKid) and card.check_suit(self.game, [cs.Suit.DIAMONDS])) and not event_blocks_card:
if against == self.name and not isinstance(card, csd.Tequila): if against == self.name and not isinstance(card, csd.Tequila):
did_play_card = False did_play_card = False
else: else:
@ -541,15 +577,15 @@ class Player:
self.hand.append(card) self.hand.append(card)
else: else:
self.game.deck.scrap(card, True) self.game.deck.scrap(card, True)
if self.event_type != 'rissa' or (self.event_type == 'rissa' and self.target_p == [p.name for p in self.game.players if p != self and (len(p.hand)+len(p.equipment)) > 0][-1]): if self.event_type != 'rissa' or (self.event_type == 'rissa' and (len([p.name for p in self.game.get_alive_players() if p != self and (len(p.hand)+len(p.equipment)) > 0]) == 0 or self.target_p == [p.name for p in self.game.get_alive_players() if p != self and (len(p.hand)+len(p.equipment)) > 0][-1])):
self.event_type = '' self.event_type = ''
self.target_p = '' self.target_p = ''
self.choose_action = '' self.choose_action = ''
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
else: else:
self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name self.target_p = self.game.players[(self.game.players_map[self.target_p]+1)%len(self.game.players)].name
while self.target_p == self.name or len(self.game.players[self.game.players_map[self.target_p]].hand) + len(self.game.players[self.game.players_map[self.target_p]].equipment) == 0: while self.target_p == self.name or len(self.game.players[self.game.players_map[self.target_p]].hand) + len(self.game.players[self.game.players_map[self.target_p]].equipment) == 0:
self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name self.target_p = self.game.players[(self.game.players_map[self.target_p]+1)%len(self.game.players)].name
self.notify_self() self.notify_self()
elif self.is_giving_life and self.game.check_event(ce.FratelliDiSangue): elif self.is_giving_life and self.game.check_event(ce.FratelliDiSangue):
try: try:
@ -604,6 +640,13 @@ class Player:
else: else:
self.discarded_cards.append(self.available_cards.pop(card_index)) self.discarded_cards.append(self.available_cards.pop(card_index))
self.notify_self() 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)
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): elif self.is_drawing and self.game.check_event(ce.Peyote):
self.is_drawing = False self.is_drawing = False
card = self.game.deck.draw() card = self.game.deck.draw()
@ -619,19 +662,25 @@ class Player:
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
self.notify_self() self.notify_self()
# specifico per personaggio # specifico per personaggio
elif self.is_drawing and isinstance(self.character, chars.KitCarlson): elif self.is_drawing and self.character.check(self.game, chars.KitCarlson):
self.hand.append(self.available_cards.pop(card_index)) self.hand.append(self.available_cards.pop(card_index))
if len(self.available_cards) == 1: pickable_stop = 1
self.game.deck.put_on_top(self.available_cards.pop()) if self.game.check_event(ceh.Sete): pickable_stop = 2
if self.game.check_event(ceh.IlTreno): pickable_stop = 0
if len(self.available_cards) == pickable_stop:
if len(self.available_cards) > 0:
self.game.deck.put_on_top(self.available_cards.pop())
self.is_drawing = False self.is_drawing = False
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
self.notify_self() self.notify_self()
elif self.is_drawing and isinstance(self.character, chd.PatBrennan): elif self.is_drawing and self.character.check(self.game, chd.PatBrennan):
self.is_drawing = False
card = self.available_cards.pop(card_index) card = self.available_cards.pop(card_index)
if card.usable_next_turn: if card.usable_next_turn:
card.can_be_used_now = False card.can_be_used_now = False
self.hand.append(card) self.hand.append(card)
self.available_cards = [] self.available_cards = []
self.game.get_player_named(self.pat_target).notify_self()
self.pending_action = PendingAction.PLAY self.pending_action = PendingAction.PLAY
self.notify_self() self.notify_self()
else: # emporio else: # emporio
@ -639,7 +688,7 @@ class Player:
def barrel_pick(self): def barrel_pick(self):
pickable_cards = 1 + self.character.pick_mod pickable_cards = 1 + self.character.pick_mod
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and isinstance(self.character, chars.Jourdonnais): if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
pickable_cards = 2 pickable_cards = 2
while pickable_cards > 0: while pickable_cards > 0:
pickable_cards -= 1 pickable_cards -= 1
@ -647,29 +696,29 @@ class Player:
print(f'Did pick {picked}') print(f'Did pick {picked}')
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_flipped|{self.name}|{picked}') data=f'_flipped|{self.name}|{picked}')
if picked.suit == cs.Suit.HEARTS: if picked.check_suit(self.game, [cs.Suit.HEARTS]):
self.mancato_needed -= 1 self.mancato_needed -= 1
self.notify_self() self.notify_self()
if self.mancato_needed <= 0: if self.mancato_needed <= 0:
self.game.responders_did_respond_resume_turn(did_lose=False) self.game.responders_did_respond_resume_turn(did_lose=False)
return return
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang)) or isinstance(self.character, chd.ElenaFuente)]) == 0\ if not self.game.is_competitive and len([c for c in self.hand if 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)]) == 0\
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0: and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
self.take_damage_response() self.take_damage_response()
self.game.responders_did_respond_resume_turn(did_lose=True) self.game.responders_did_respond_resume_turn(did_lose=True)
else: else:
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.expected_response = self.game.deck.mancato_cards.copy() self.expected_response = self.game.deck.mancato_cards.copy()
if isinstance(self.character, chars.CalamityJanet) and cs.Bang(0, 0).name not in self.expected_response: 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) self.expected_response.append(cs.Bang(0, 0).name)
elif isinstance(self.character, chd.ElenaFuente): elif self.character.check(self.game, chd.ElenaFuente):
self.expected_response = self.game.deck.all_cards_str self.expected_response = self.game.deck.all_cards_str.copy()
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
self.notify_self() self.notify_self()
def barrel_pick_no_dmg(self): def barrel_pick_no_dmg(self):
pickable_cards = 1 + self.character.pick_mod pickable_cards = 1 + self.character.pick_mod
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and isinstance(self.character, chars.Jourdonnais): if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
pickable_cards = 2 pickable_cards = 2
while pickable_cards > 0: while pickable_cards > 0:
pickable_cards -= 1 pickable_cards -= 1
@ -677,23 +726,23 @@ class Player:
print(f'Did pick {picked}') print(f'Did pick {picked}')
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_flipped|{self.name}|{picked}') data=f'_flipped|{self.name}|{picked}')
if picked.suit == cs.Suit.HEARTS: if picked.check_suit(self.game, [cs.Suit.HEARTS]):
self.mancato_needed -= 1 self.mancato_needed -= 1
self.notify_self() self.notify_self()
if self.mancato_needed <= 0: if self.mancato_needed <= 0:
self.game.responders_did_respond_resume_turn(did_lose=False) self.game.responders_did_respond_resume_turn(did_lose=False)
return return
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang)) or isinstance(self.character, chd.ElenaFuente)]) == 0\ if not self.game.is_competitive and len([c for c in self.hand if 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)]) == 0\
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0: and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
self.take_no_damage_response() self.take_no_damage_response()
self.game.responders_did_respond_resume_turn(did_lose=True) self.game.responders_did_respond_resume_turn(did_lose=True)
else: else:
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.expected_response = self.game.deck.mancato_cards.copy() self.expected_response = self.game.deck.mancato_cards.copy()
if isinstance(self.character, chars.CalamityJanet) and cs.Bang(0, 0).name not in self.expected_response: 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) self.expected_response.append(cs.Bang(0, 0).name)
elif isinstance(self.character, chd.ElenaFuente): elif self.character.check(self.game, chd.ElenaFuente):
self.expected_response = self.game.deck.all_cards_str self.expected_response = self.game.deck.all_cards_str.copy()
self.on_failed_response_cb = self.take_no_damage_response self.on_failed_response_cb = self.take_no_damage_response
self.notify_self() self.notify_self()
@ -707,8 +756,8 @@ class Player:
for i in range(len(self.equipment)): for i in range(len(self.equipment)):
if self.equipment[i].can_be_used_now: if self.equipment[i].can_be_used_now:
print('usable', self.equipment[i]) print('usable', self.equipment[i])
if not self.game.is_competitive and len([c for c in self.equipment if isinstance(c, cs.Barile)]) == 0 and not isinstance(self.character, chars.Jourdonnais)\ if not self.game.is_competitive and len([c for c in self.equipment if isinstance(c, cs.Barile)]) == 0 and not self.character.check(self.game, chars.Jourdonnais)\
and len([c for c in self.hand if isinstance(c, cs.Mancato) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Bang)) or isinstance(self.character, chd.ElenaFuente)]) == 0\ and len([c for c in self.hand if (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)]) == 0\
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0: and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
print('Cant defend') print('Cant defend')
if not no_dmg: if not no_dmg:
@ -717,7 +766,7 @@ class Player:
self.take_no_damage_response() self.take_no_damage_response()
return False return False
else: else:
if (not self.game.check_event(ce.Lazo) and len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0) or isinstance(self.character, chars.Jourdonnais): if (not self.game.check_event(ce.Lazo) and len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0) or self.character.check(self.game, chars.Jourdonnais):
print('has barrel') print('has barrel')
self.pending_action = PendingAction.PICK self.pending_action = PendingAction.PICK
if not no_dmg: if not no_dmg:
@ -728,22 +777,32 @@ class Player:
print('has mancato') print('has mancato')
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.expected_response = self.game.deck.mancato_cards.copy() self.expected_response = self.game.deck.mancato_cards.copy()
if self.attacker and self.attacker in self.game.players and isinstance(self.attacker.character, chd.BelleStar) or self.game.check_event(ce.Lazo): if self.attacker and self.attacker in self.game.get_alive_players() and isinstance(self.attacker.character, chd.BelleStar) or self.game.check_event(ce.Lazo):
self.expected_response = self.game.deck.mancato_cards_not_green self.expected_response = self.game.deck.mancato_cards_not_green.copy()
elif isinstance(self.character, chars.CalamityJanet) and cs.Bang(0, 0).name not in self.expected_response: elif 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) self.expected_response.append(cs.Bang(0, 0).name)
elif isinstance(self.character, chd.ElenaFuente): elif self.character.check(self.game, chd.ElenaFuente):
self.expected_response = self.game.deck.all_cards_str self.expected_response = self.game.deck.all_cards_str.copy()
if not no_dmg: if not no_dmg:
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
else: else:
self.on_failed_response_cb = self.take_no_damage_response self.on_failed_response_cb = self.take_no_damage_response
return True 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): def get_indians(self, attacker):
self.attacker = attacker self.attacker = attacker
if isinstance(self.character, chd.ApacheKid): return False if self.character.check(self.game, chd.ApacheKid): return False
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0: if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0:
print('Cant defend') print('Cant defend')
self.take_damage_response() self.take_damage_response()
return False return False
@ -751,7 +810,7 @@ class Player:
print('has bang') print('has bang')
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.expected_response = [cs.Bang(0, 0).name] self.expected_response = [cs.Bang(0, 0).name]
if isinstance(self.character, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response: if self.character.check(self.game, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response:
self.expected_response.append(cs.Mancato(0, 0).name) self.expected_response.append(cs.Mancato(0, 0).name)
self.event_type = 'indians' self.event_type = 'indians'
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
@ -759,7 +818,7 @@ class Player:
def get_dueled(self, attacker): def get_dueled(self, attacker):
self.attacker = attacker self.attacker = attacker
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (isinstance(self.character, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0: if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0):
print('Cant defend') print('Cant defend')
self.take_damage_response() self.take_damage_response()
self.game.responders_did_respond_resume_turn(did_lose=True) self.game.responders_did_respond_resume_turn(did_lose=True)
@ -767,19 +826,19 @@ class Player:
else: else:
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.expected_response = [cs.Bang(0, 0).name] self.expected_response = [cs.Bang(0, 0).name]
if isinstance(self.character, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response: if self.character.check(self.game, chars.CalamityJanet) and cs.Mancato(0, 0).name not in self.expected_response:
self.expected_response.append(cs.Mancato(0, 0).name) self.expected_response.append(cs.Mancato(0, 0).name)
self.event_type = 'duel' self.event_type = 'duel'
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
return True return True
def heal_if_needed(self): def heal_if_needed(self):
while self.lives <= 0 and len(self.game.players) > 2 and len([c for c in self.hand if isinstance(c, cs.Birra)]) > 0: while self.lives <= 0 and len(self.game.get_alive_players()) > 2 and len([c for c in self.hand if isinstance(c, cs.Birra)]) > 0 and not self.game.check_event(ceh.IlReverendo):
for i in range(len(self.hand)): for i in range(len(self.hand)):
if isinstance(self.hand[i], cs.Birra): if isinstance(self.hand[i], cs.Birra):
if isinstance(self.character, chd.MollyStark) and not self.is_my_turn: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn:
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.lives += 1 if not isinstance(self.character, chd.TequilaJoe) else 2 self.lives += 1 if not self.character.check(self.game, chd.TequilaJoe) else 2
self.game.deck.scrap(self.hand.pop(i), True) self.game.deck.scrap(self.hand.pop(i), True)
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_beer_save|{self.name}') data=f'_beer_save|{self.name}')
@ -788,11 +847,11 @@ class Player:
def take_damage_response(self): def take_damage_response(self):
self.lives -= 1 self.lives -= 1
if self.lives > 0: if self.lives > 0:
if isinstance(self.character, chars.BartCassidy): if self.character.check(self.game, chars.BartCassidy):
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_special_bart_cassidy|{self.name}') data=f'_special_bart_cassidy|{self.name}')
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
elif isinstance(self.character, chars.ElGringo) and self.attacker and self.attacker in self.game.players and len(self.attacker.hand) > 0: 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( self.hand.append(self.attacker.hand.pop(
randrange(0, len(self.attacker.hand)))) randrange(0, len(self.attacker.hand))))
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
@ -822,31 +881,31 @@ class Player:
((hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response)) or ((hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response)) or
(hand_index-len(self.hand) < len(self.equipment) and self.equipment[hand_index-len(self.hand)].name in self.expected_response)): (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)) card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand))
if isinstance(self.character, chd.MollyStark) and hand_index < len(self.hand)+1 and not self.is_my_turn and self.event_type != 'duel': if self.character.check(self.game, chd.MollyStark) and hand_index < len(self.hand)+1 and not self.is_my_turn and self.event_type != 'duel':
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
card.use_card(self) card.use_card(self)
self.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card}')
self.game.deck.scrap(card, True) self.game.deck.scrap(card, True)
self.notify_self() self.notify_self()
self.mancato_needed -= 1 self.mancato_needed -= 1
if self.mancato_needed <= 0: if self.mancato_needed <= 0:
if self.event_type == 'duel': if self.event_type == 'duel':
self.game.duel(self, self.attacker.name) self.game.duel(self, self.attacker.name)
if isinstance(self.character, chd.MollyStark) and hand_index < len(self.hand)+1 and not self.is_my_turn: if self.character.check(self.game, chd.MollyStark) and hand_index < len(self.hand)+1 and not self.is_my_turn:
self.molly_discarded_cards += 1 self.molly_discarded_cards += 1
else: else:
self.game.responders_did_respond_resume_turn(did_lose=False) self.game.responders_did_respond_resume_turn(did_lose=False)
self.event_type = '' self.event_type = ''
self.expected_response = []
else: else:
self.pending_action = PendingAction.RESPOND self.pending_action = PendingAction.RESPOND
self.notify_self() self.notify_self()
else: else:
if isinstance(self.character, chd.MollyStark) and not self.is_my_turn: if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn:
for i in range(self.molly_discarded_cards): for i in range(self.molly_discarded_cards):
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.molly_discarded_cards = 0 self.molly_discarded_cards = 0
self.notify_self() self.notify_self()
elif self.attacker and self.attacker in self.game.players and isinstance(self.attacker.character, chd.MollyStark) and self.is_my_turn: elif self.attacker and self.attacker in self.game.get_alive_players() and isinstance(self.attacker.character, chd.MollyStark) and self.is_my_turn:
for i in range(self.attacker.molly_discarded_cards): for i in range(self.attacker.molly_discarded_cards):
self.attacker.hand.append(self.attacker.game.deck.draw(True)) self.attacker.hand.append(self.attacker.game.deck.draw(True))
self.attacker.molly_discarded_cards = 0 self.attacker.molly_discarded_cards = 0
@ -881,13 +940,13 @@ class Player:
return self.character.visibility_mod + covers return self.character.visibility_mod + covers
def scrap(self, card_index): def scrap(self, card_index):
if self.is_my_turn or isinstance(self.character, chars.SidKetchum): if self.is_my_turn or self.character.check(self.game, chars.SidKetchum):
self.scrapped_cards += 1 self.scrapped_cards += 1
card = self.hand.pop(card_index) card = self.hand.pop(card_index)
if isinstance(self.character, chars.SidKetchum) and self.scrapped_cards == 2: if self.character.check(self.game, chars.SidKetchum) and self.scrapped_cards == 2:
self.scrapped_cards = 0 self.scrapped_cards = 0
self.lives = min(self.lives+1, self.max_lives) self.lives = min(self.lives+1, self.max_lives)
elif isinstance(self.character, chd.JoseDelgrado) and card.is_equipment and self.special_use_count < 2: elif self.character.check(self.game, chd.JoseDelgrado) and card.is_equipment and self.special_use_count < 2:
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.special_use_count += 1 self.special_use_count += 1
@ -895,7 +954,7 @@ class Player:
self.notify_self() self.notify_self()
def holyday_special(self, data): def holyday_special(self, data):
if isinstance(self.character, chd.DocHolyday) and self.special_use_count < 1: if self.character.check(self.game, chd.DocHolyday) and self.special_use_count < 1:
self.special_use_count += 1 self.special_use_count += 1
cards = sorted(data['cards'], reverse=True) cards = sorted(data['cards'], reverse=True)
for c in cards: for c in cards:
@ -904,16 +963,17 @@ class Player:
self.game.attack(self, data['against']) self.game.attack(self, data['against'])
def chuck_lose_hp_draw(self): def chuck_lose_hp_draw(self):
if isinstance(self.character, chd.ChuckWengam) and self.lives > 1 and self.is_my_turn: if self.character.check(self.game, chd.ChuckWengam) and self.lives > 1 and self.is_my_turn:
self.lives -= 1 self.lives -= 1
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.hand.append(self.game.deck.draw(True)) self.hand.append(self.game.deck.draw(True))
self.notify_self() self.notify_self()
def end_turn(self, forced=False): def end_turn(self, forced=False):
print(f"{self.name} wants to end his turn")
if not self.is_my_turn: if not self.is_my_turn:
return return
maxcards = self.lives if not isinstance(self.character, chd.SeanMallory) else 10 maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
if len(self.hand) > maxcards and not forced: if len(self.hand) > maxcards and not forced:
print( print(
f"I {self.name} have to many cards in my hand and I can't end the turn") f"I {self.name} have to many cards in my hand and I can't end the turn")
@ -921,13 +981,20 @@ class Player:
if not forced and self.game.check_event(ce.Vendetta) and self.can_play_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() picked: cs.Card = self.game.deck.pick_and_scrap()
self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked}') self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked}')
if picked.suit == cs.Suit.HEARTS: if picked.check_suit(self.game, [cs.Suit.HEARTS]):
self.play_turn(can_play_vendetta=False) self.play_turn(can_play_vendetta=False)
return return
self.is_my_turn = False self.is_my_turn = False
self.has_played_bang = False
for i in range(len(self.equipment)): for i in range(len(self.equipment)):
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now: if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now:
self.equipment[i].can_be_used_now = True self.equipment[i].can_be_used_now = True
if self.is_dead and self.is_ghost and 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)
self.pending_action = PendingAction.WAIT self.pending_action = PendingAction.WAIT
self.notify_self() self.notify_self()
self.game.next_turn() self.game.next_turn()

View File

@ -36,6 +36,7 @@ export default {
data: () => ({ data: () => ({
isConnected: false, isConnected: false,
c: false, c: false,
showUpdateUI: false,
}), }),
computed: { computed: {
}, },

View File

@ -103,7 +103,8 @@ export default {
text-align: center; text-align: center;
width: 100%; width: 100%;
top: -10pt; top: -10pt;
} font-size: 11pt;
}
.card .emoji { .card .emoji {
position: absolute; position: absolute;
text-align: center; text-align: center;
@ -133,6 +134,9 @@ export default {
bottom: 20pt; bottom: 20pt;
left: 3pt; left: 3pt;
} }
.cant-play {
filter: brightness(0.5);
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root, #app { :root, #app {
background-color: #181a1b; background-color: #181a1b;

View File

@ -2,7 +2,7 @@
<div class="chat"> <div class="chat">
<h3>{{$t("chat.chat")}}</h3> <h3>{{$t("chat.chat")}}</h3>
<div id="chatbox"> <div id="chatbox">
<p style="margin:1pt;" class="chat-message" v-for="msg in messages" v-bind:key="msg" :style="`color:${msg.color}`">{{msg.text}}</p> <p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color}`">{{msg.text}}</p>
<p class="end">.</p> <p class="end">.</p>
</div> </div>
<form @submit="sendChatMessage" id="msg-form"> <form @submit="sendChatMessage" id="msg-form">
@ -35,8 +35,15 @@ export default {
}, },
methods: { methods: {
sendChatMessage(e) { sendChatMessage(e) {
if (this.text.trim().length > 0){ let msg = this.text.trim()
this.$socket.emit('chat_message', this.text.trim()) if (msg.length > 0){
if (msg.indexOf('/addbot') !== -1 && msg.split(' ').length > 1){
for (let i = 0; i < parseInt(msg.split(' ')[1]); i++) {
this.$socket.emit('chat_message', msg.split(' ')[0])
}
}else{
this.$socket.emit('chat_message', msg)
}
this.text = '' this.text = ''
} }
e.preventDefault(); e.preventDefault();

View File

@ -2,7 +2,7 @@
<div id="overlay" class="center-stuff"> <div id="overlay" class="center-stuff">
<h1>{{text}}</h1> <h1>{{text}}</h1>
<div> <div>
<Card v-for="c in cards" v-bind:key="c" :card="c" @click.native="select(c)" @pointerenter.native="showDesc(c)" @pointerleave.native="desc=''"/> <Card v-for="(c, i) in cards" v-bind:key="i" :card="c" @click.native="select(c)" @pointerenter.native="showDesc(c)" @pointerleave.native="desc=''"/>
</div> </div>
<p v-if="hintText">{{hintText}}</p> <p v-if="hintText">{{hintText}}</p>
<div style="margin-top:6pt;" class="button center-stuff" v-if="showCancelBtn" @click="cancel"><span>{{realCancelText}}</span></div> <div style="margin-top:6pt;" class="button center-stuff" v-if="showCancelBtn" @click="cancel"><span>{{realCancelText}}</span></div>

View File

@ -5,7 +5,7 @@
<div v-if="eventCard" style="position:relative"> <div v-if="eventCard" style="position:relative">
<div class="card fistful-of-cards" style="position:relative; bottom:-3pt;right:-3pt;"/> <div class="card fistful-of-cards" style="position:relative; bottom:-3pt;right:-3pt;"/>
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/> <div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="eventCard" :key="eventCard" :class="eventClasses" @click.native="event"/> <card :card="eventCard" :key="eventCard.name" :class="eventClasses" @click.native="event"/>
</div> </div>
<div style="position:relative"> <div style="position:relative">
<div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/> <div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/>
@ -15,12 +15,14 @@
<div style="position:relative;"> <div style="position:relative;">
<card v-if="previousScrap" :card="previousScrap" style="top: 1.5pt;right: -1.5pt;"/> <card v-if="previousScrap" :card="previousScrap" style="top: 1.5pt;right: -1.5pt;"/>
<card v-else :card="card" class="back" style="opacity:0"/> <card v-else :card="card" class="back" style="opacity:0"/>
<card v-if="lastScrap" :card="lastScrap" :key="lastScrap" class="last-scrap" @click.native="action('scrap')" <card v-if="lastScrap" :card="lastScrap" :key="lastScrap.name+lastScrap.number" class="last-scrap" @click.native="action('scrap')"
@pointerenter.native="desc=($i18n.locale=='it'?lastScrap.desc:lastScrap.desc_eng)" @pointerleave.native="desc=''" /> @pointerenter.native="desc=($i18n.locale=='it'?lastScrap.desc:lastScrap.desc_eng)" @pointerleave.native="desc=''" />
</div> </div>
</div> </div>
<transition name="list"> <transition name="list">
<p v-if="eventCard" class="center-stuff"><i>{{($i18n.locale=='it'?eventCard.desc:eventCard.desc_eng)}}</i></p> <p v-if="eventCard" class="center-stuff"><i>{{($i18n.locale=='it'?eventCard.desc:eventCard.desc_eng)}}</i></p>
</transition>
<transition name="list">
<p v-if="desc" class="center-stuff"><i>{{desc}}</i></p> <p v-if="desc" class="center-stuff"><i>{{desc}}</i></p>
</transition> </transition>
</div> </div>
@ -52,7 +54,7 @@ export default {
sockets: { sockets: {
self(self){ self(self){
self = JSON.parse(self) self = JSON.parse(self)
this.isPlaying = self.lives > 0 this.isPlaying = self.lives > 0 || self.is_ghost
this.pending_action = self.pending_action this.pending_action = self.pending_action
}, },
scrap(card) { scrap(card) {

View File

@ -16,10 +16,13 @@
<Card v-if="startGameCard" :card="startGameCard" @click.native="startGame"/> <Card v-if="startGameCard" :card="startGameCard" @click.native="startGame"/>
<!-- <div style="position: relative;width:260pt;height:400pt;"> --> <!-- <div style="position: relative;width:260pt;height:400pt;"> -->
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;"> <div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
<transition-group v-if="p.max_lives" name="list" tag="div" class="tiny-health"> <transition-group v-if="p.max_lives && !p.is_ghost" name="list" tag="div" class="tiny-health">
<span v-for="(n, i) in p.lives" v-bind:key="n" :alt="i"></span> <span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i"></span>
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="n" :alt="i">💀</span> <span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
</transition-group> </transition-group>
<div v-else-if="p.is_ghost" class="tiny-health">
<span>👻</span>
</div>
<Card :card="p.card" :class="{is_my_turn:p.is_my_turn}"/> <Card :card="p.card" :class="{is_my_turn:p.is_my_turn}"/>
<Card v-if="p.character" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/> <Card v-if="p.character" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
<Card v-if="p.character && p.character.name !== p.real_character.name" style="transform:scale(0.5) translate(-90px, -50px);" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/> <Card v-if="p.character && p.character.name !== p.real_character.name" style="transform:scale(0.5) translate(-90px, -50px);" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
@ -28,6 +31,9 @@
<div class="tiny-equipment"> <div class="tiny-equipment">
<Card v-for="card in p.equipment" v-bind:key="card.name+card.number" :card="card" @click.native="selectedInfo = p.equipment"/> <Card v-for="card in p.equipment" v-bind:key="card.name+card.number" :card="card" @click.native="selectedInfo = p.equipment"/>
</div> </div>
<div v-if="p.is_bot" style="position:absolute;top:32%;left:50%;right:50%" class="tiny-health">
<span>🤖</span>
</div>
</div> </div>
<!-- :style="p.style"/> --> <!-- :style="p.style"/> -->
<!-- </div> --> <!-- </div> -->
@ -40,12 +46,12 @@
</div> </div>
<h3>{{$t('mods')}}</h3> <h3>{{$t('mods')}}</h3>
<PrettyCheck @click.native="toggleCompetitive" :disabled="!isRoomOwner" v-model="is_competitive" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('mod_comp')}}</PrettyCheck> <PrettyCheck @click.native="toggleCompetitive" :disabled="!isRoomOwner" v-model="is_competitive" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('mod_comp')}}</PrettyCheck>
<br> <!-- <br> -->
<PrettyCheck @click.native="toggleReplaceWithBot" :disabled="!isRoomOwner" v-model="disconnect_bot" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('disconnect_bot')}}</PrettyCheck> <!-- <PrettyCheck @click.native="toggleReplaceWithBot" :disabled="!isRoomOwner" v-model="disconnect_bot" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('disconnect_bot')}}</PrettyCheck> -->
</div> </div>
<div v-if="started"> <div v-if="started">
<deck :endTurnAction="()=>{wantsToEndTurn = true}"/> <deck :endTurnAction="()=>{wantsToEndTurn = true}"/>
<player :isEndingTurn="wantsToEndTurn" :cancelEndingTurn="()=>{wantsToEndTurn = false}" :chooseCardFromPlayer="choose"/> <player :isEndingTurn="wantsToEndTurn" :cancelEndingTurn="()=>{wantsToEndTurn = false}" :chooseCardFromPlayer="choose" :cancelChooseCardFromPlayer="()=>{hasToChoose=false}"/>
</div> </div>
</div> </div>
<chat/> <chat/>
@ -113,6 +119,7 @@ export default {
return { return {
name: x.name, name: x.name,
ready: x.ready, ready: x.ready,
is_bot: x.is_bot,
ncards: 0, ncards: 0,
} }
}) })

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<p v-if="instruction && lives > 0" class="center-stuff">{{instruction}}</p> <p v-if="instruction && (lives > 0 || is_ghost)" class="center-stuff">{{instruction}}</p>
<!-- <button v-if="canEndTurn" @click="end_turn">Termina Turno</button> --> <!-- <button v-if="canEndTurn" @click="end_turn">Termina Turno</button> -->
<div class="equipment-slot"> <div class="equipment-slot">
<Card v-if="my_role" :card="my_role" class="back" <Card v-if="my_role" :card="my_role" class="back"
@ -8,13 +8,13 @@
<Card v-if="character" :card="character" style="margin-left: -30pt;margin-right: 0pt;" <Card v-if="character" :card="character" style="margin-left: -30pt;margin-right: 0pt;"
@pointerenter.native="desc=($i18n.locale=='it'?character.desc:character.desc_eng)" @pointerleave.native="desc=''"/> @pointerenter.native="desc=($i18n.locale=='it'?character.desc:character.desc_eng)" @pointerleave.native="desc=''"/>
<transition-group name="list" tag="div" style="display: flex;flex-direction:column; justify-content: space-evenly; margin-left: 12pt;margin-right:-10pt;"> <transition-group name="list" tag="div" style="display: flex;flex-direction:column; justify-content: space-evenly; margin-left: 12pt;margin-right:-10pt;">
<span v-for="(n, i) in lives" v-bind:key="n" :alt="i"></span> <span v-for="(n, i) in lives" v-bind:key="i" :alt="i"></span>
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="n" :alt="i">💀</span> <span v-for="(n, i) in (max_lives-lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
</transition-group> </transition-group>
<transition-group v-if="lives > 0" name="list" tag="div" style="margin: 0 0 0 10pt; display:flex;"> <transition-group v-if="lives > 0 || is_ghost" name="list" tag="div" style="margin: 0 0 0 10pt; display:flex;">
<Card v-for="card in equipment" v-bind:key="card.name+card.number" :card="card" <Card v-for="card in equipment" v-bind:key="card.name+card.number" :card="card"
@pointerenter.native="desc=($i18n.locale=='it'?card.desc:card.desc_eng)" @pointerleave.native="desc=''" @pointerenter.native="desc=($i18n.locale=='it'?card.desc:card.desc_eng)" @pointerleave.native="desc=''"
@click.native="play_card(card, true)" /> @click.native="play_card(card, true)" :class="{'cant-play':((eventCard && eventCard.name == 'Lazo') || !card.can_be_used_now)}"/>
</transition-group> </transition-group>
</div> </div>
<transition name="list"> <transition name="list">
@ -24,29 +24,30 @@
<button v-if="is_my_turn && character.name === 'Chuck Wengam' && lives > 1" @click="chuckSpecial">{{$t('special_ability')}}</button> <button v-if="is_my_turn && character.name === 'Chuck Wengam' && lives > 1" @click="chuckSpecial">{{$t('special_ability')}}</button>
<button v-if="is_my_turn && character.name === 'José Delgrado' && special_use_count < 2 && hand.filter(x => x.is_equipment).length > 0" @click="joseScrap=true">{{$t('special_ability')}}</button> <button v-if="is_my_turn && character.name === 'José Delgrado' && special_use_count < 2 && hand.filter(x => x.is_equipment).length > 0" @click="joseScrap=true">{{$t('special_ability')}}</button>
<button v-if="is_my_turn && character.name === 'Doc Holyday' && special_use_count < 1 && hand.length > 1" @click="holydayScrap=true">{{$t('special_ability')}}</button> <button v-if="is_my_turn && character.name === 'Doc Holyday' && special_use_count < 1 && hand.length > 1" @click="holydayScrap=true">{{$t('special_ability')}}</button>
<div v-if="lives > 0" style="position:relative"> <div v-if="lives > 0 || is_ghost" style="position:relative">
<span id="hand_text">{{$t('hand')}}</span> <span id="hand_text">{{$t('hand')}}</span>
<transition-group name="list" tag="div" class="hand"> <transition-group name="list" tag="div" class="hand">
<Card v-for="card in hand" v-bind:key="card.name+card.number" :card="card" <Card v-for="card in handComputed" v-bind:key="card.name+card.number" :card="card"
@click.native="play_card(card, false)" @click.native="play_card(card, false)"
@pointerenter.native="hint=($i18n.locale=='it'?card.desc:card.desc_eng)" @pointerleave.native="hint=''"/> @pointerenter.native="hint=($i18n.locale=='it'?card.desc:card.desc_eng)" @pointerleave.native="hint=''"
:class="{'cant-play':card.cantBePlayed}"/>
</transition-group> </transition-group>
</div> </div>
<transition name="list"> <transition name="list">
<p v-if="hint"><i>{{hint}}</i></p> <p v-if="hint"><i>{{hint}}</i></p>
</transition> </transition>
<Chooser v-if="is_my_turn && pending_action == 4" :text="$t('wait')" :cards="[]"/> <Chooser v-if="is_my_turn && pending_action == 4 && (lives > 0 || is_ghost)" :text="$t('wait')" :cards="[]"/>
<Chooser v-if="card_against" :text="$t('card_against')" :cards="visiblePlayers" :select="selectAgainst" :cancel="cancelCardAgainst"/> <Chooser v-if="card_against" :text="$t('card_against')" :hint-text="visiblePlayers.length === 0 ? $t('no_players_in_range'):''" :cards="visiblePlayers" :select="selectAgainst" :cancel="cancelCardAgainst"/>
<Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond"/> <Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond"/>
<Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose"/> <Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose"/>
<Chooser v-if="lives <= 0 && max_lives > 0" :text="$t('you_died')" :cancelText="$t('spectate')" :cancel="()=>{max_lives = 0}"/> <Chooser v-if="lives <= 0 && max_lives > 0 && !is_ghost" :text="$t('you_died')" :cancelText="$t('spectate')" :cancel="()=>{max_lives = 0}"/>
<Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" /> <Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" />
<Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')" /> <Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')" />
<Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ${$t('did_pick_as')}:`" :cards="[notifycard.card]" :hintText="$t(notifycard.message)" class="turn-notify-4s"/> <Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ${$t('did_pick_as')}:`" :cards="[notifycard.card]" :hintText="$t(notifycard.message)" class="turn-notify-4s"/>
<Chooser v-if="!show_role && is_my_turn && pending_action < 2" :text="$t('play_your_turn')" :key="is_my_turn" class="turn-notify" /> <Chooser v-if="!show_role && is_my_turn && pending_action < 2" :text="$t('play_your_turn')" :key="is_my_turn" class="turn-notify" />
<Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/> <Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/>
<Chooser v-if="hasToPickResponse" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" /> <Chooser v-if="hasToPickResponse" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" />
<Chooser v-if="!card_against && card_with" :text="`${$t('choose_scarp_card_to')} ${card_with.name.toUpperCase()}`" :cards="hand.filter(x => x !== card_with)" :select="selectWith" :cancel="()=>{card_with = null}"/> <Chooser v-if="!card_against && card_with" :text="`${$t('choose_scarp_card_to')} ${card_with.name.toUpperCase()}`" :cards="handComputed.filter(x => x !== card_with)" :select="selectWith" :cancel="()=>{card_with = null}"/>
<Chooser v-if="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${lives}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/> <Chooser v-if="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${lives}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/>
<Chooser v-if="sidWantsScrapForHealth && scrapHand.length < 2" :text="`${$t('discard')} ${2 - scrapHand.length} ${$t('to_regain_1_hp')}`" <Chooser v-if="sidWantsScrapForHealth && scrapHand.length < 2" :text="`${$t('discard')} ${2 - scrapHand.length} ${$t('to_regain_1_hp')}`"
:cards="notScrappedHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;scrapHand=[]}"/> :cards="notScrappedHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;scrapHand=[]}"/>
@ -66,6 +67,7 @@ export default {
name: 'Player', name: 'Player',
props: { props: {
chooseCardFromPlayer: Function, chooseCardFromPlayer: Function,
cancelChooseCardFromPlayer: Function,
isEndingTurn: Boolean, isEndingTurn: Boolean,
cancelEndingTurn: Function, cancelEndingTurn: Function,
}, },
@ -106,7 +108,9 @@ export default {
holydayScrap: false, holydayScrap: false,
special_use_count: 0, special_use_count: 0,
mancato_needed: 0, mancato_needed: 0,
is_ghost: false,
name: '', name: '',
eventCard: false,
}), }),
sockets: { sockets: {
role(role) { role(role) {
@ -141,10 +145,14 @@ export default {
this.sight = self.sight this.sight = self.sight
this.attacker = self.attacker this.attacker = self.attacker
this.mancato_needed = self.mancato_needed this.mancato_needed = self.mancato_needed
this.is_ghost = self.is_ghost
if (this.pending_action == 5 && self.target_p) { if (this.pending_action == 5 && self.target_p) {
this.chooseCardFromPlayer(self.target_p) this.chooseCardFromPlayer(self.target_p)
} else if (this.pending_action == 5) { } else if (this.pending_action == 5) {
this.shouldChooseCard = true this.shouldChooseCard = true
} else {
this.cancelChooseCardFromPlayer()
this.shouldChooseCard = false
} }
}, },
self_vis(vis) { self_vis(vis) {
@ -157,7 +165,10 @@ export default {
setTimeout(function(){ setTimeout(function(){
this.notifycard = null this.notifycard = null
}.bind(this), 4000) }.bind(this), 4000)
} },
event_card(card) {
this.eventCard = card
},
}, },
computed:{ computed:{
respondText() { respondText() {
@ -231,6 +242,20 @@ export default {
cc.push(x) cc.push(x)
}) })
return cc return cc
},
handComputed() {
return this.hand.map(x=> {
let cantBePlayed = false
let calamity_special = (x.name === 'Mancato!' && this.character.name === 'Calamity Janet')
let cant_play_bang = (this.has_played_bang && this.equipment.filter(x => x.name == 'Volcanic').length == 0)
if ((x.name == 'Bang!' || (calamity_special && x.name=='Mancato!')) && (cant_play_bang || (this.eventCard && this.eventCard.name == "Sermone"))) cantBePlayed = true;
else if (this.eventCard && this.eventCard.name == "Il Giudice" && (x.is_equipment || !x.can_be_used_now)) cantBePlayed = true;
else if (this.eventCard && this.eventCard.name == "Il Reverendo" && (x.name == "Birra")) cantBePlayed = true;
return {
...x,
cantBePlayed: cantBePlayed
}
})
} }
}, },
methods: { methods: {
@ -276,6 +301,7 @@ export default {
let calamity_special = (card.name === 'Mancato!' && this.character.name === 'Calamity Janet') let calamity_special = (card.name === 'Mancato!' && this.character.name === 'Calamity Janet')
let cant_play_bang = (this.has_played_bang && this.equipment.filter(x => x.name == 'Volcanic').length == 0) let cant_play_bang = (this.has_played_bang && this.equipment.filter(x => x.name == 'Volcanic').length == 0)
if (this.pending_action == 2) { if (this.pending_action == 2) {
this.can_target_sheriff = (card.name !== 'Prigione')
if (card.need_with && !this.card_with) { if (card.need_with && !this.card_with) {
this.card_with = card this.card_with = card
} else if ((card.need_target || calamity_special) && !((card.name == 'Bang!' || (calamity_special && card.name=='Mancato!')) && cant_play_bang)) { } else if ((card.need_target || calamity_special) && !((card.name == 'Bang!' || (calamity_special && card.name=='Mancato!')) && cant_play_bang)) {
@ -283,7 +309,6 @@ export default {
this.range = this.sight this.range = this.sight
else else
this.range = card.range this.range = card.range
this.can_target_sheriff = (card.name !== 'Prigione')
if (this.visiblePlayers.length == 0 && this.hand.length > this.lives) { if (this.visiblePlayers.length == 0 && this.hand.length > this.lives) {
this.really_play_card(card, null) this.really_play_card(card, null)
} }
@ -312,9 +337,9 @@ export default {
this.card_with = card this.card_with = card
} else { } else {
let card_data = { let card_data = {
index: this.hand.indexOf(this.card_with), index: this.handComputed.indexOf(this.card_with),
against: null, against: null,
with: this.hand.indexOf(card), with: this.handComputed.indexOf(card),
} }
this.card_with = null this.card_with = null
this.$socket.emit('play_card', card_data) this.$socket.emit('play_card', card_data)
@ -325,7 +350,7 @@ export default {
this.card_with = null this.card_with = null
}, },
really_play_card(card, against) { really_play_card(card, against) {
let res = this.hand.indexOf(card) let res = this.handComputed.indexOf(card)
if (res === -1) { if (res === -1) {
res = this.equipment.indexOf(card) res = this.equipment.indexOf(card)
if (res !== -1) res += this.hand.length if (res !== -1) res += this.hand.length
@ -333,7 +358,7 @@ export default {
let card_data = { let card_data = {
index: res, index: res,
against: against, against: against,
with: this.hand.indexOf(this.card_with) > -1 ? this.hand.indexOf(this.card_with):null, with: this.handComputed.indexOf(this.card_with) > -1 ? this.handComputed.indexOf(this.card_with):null,
} }
this.card_with = null this.card_with = null
console.log(card_data) console.log(card_data)

View File

@ -40,6 +40,7 @@
"choose_card_to_get": "Choose a card", "choose_card_to_get": "Choose a card",
"choose_guess": "Guess the color of the suit", "choose_guess": "Guess the color of the suit",
"choose_ranch": "Choose the cards to replace", "choose_ranch": "Choose the cards to replace",
"choose_dalton": "Choose which equipment to discard",
"choose_fratelli_di_sangue": "Choose who you want to donate one of your lives", "choose_fratelli_di_sangue": "Choose who you want to donate one of your lives",
"choose_cecchino": "Choose who to shoot", "choose_cecchino": "Choose who to shoot",
"choose_rimbalzo_player": "Choose the target of the bang", "choose_rimbalzo_player": "Choose the target of the bang",
@ -60,6 +61,7 @@
"to_defend_from": "TO DEFEND YOURSELF FROM", "to_defend_from": "TO DEFEND YOURSELF FROM",
"submit": "Submit", "submit": "Submit",
"copy": "Copy invite", "copy": "Copy invite",
"no_players_in_range": "You can't see the other players, equip a weapon or a scope!",
"chat": { "chat": {
"chat": "Chat", "chat": "Chat",
"joined": "{0} joined the lobby", "joined": "{0} joined the lobby",
@ -90,7 +92,11 @@
"guess": "{0} guesses {1}.", "guess": "{0} guesses {1}.",
"guess_right": "{0} was right.", "guess_right": "{0} was right.",
"guess_wrong": "{0} was wrong.", "guess_wrong": "{0} was wrong.",
"fratelli_sangue": "{0} gave one of his lives to {1}." "fratelli_sangue": "{0} gave one of his lives to {1}.",
"doctor_heal": "{0} was healed by the doctor.",
"respond": "{0} responded with {1}.",
"change_username": "{0} is now {1}.",
"lobby_reset": "Going back to lobby in {0} seconds..."
}, },
"foc": { "foc": {
"leggedelwest": "He must play this card on this turn if possible." "leggedelwest": "He must play this card on this turn if possible."

View File

@ -40,6 +40,7 @@
"choose_card_to_get": "Scegli che carta pescare", "choose_card_to_get": "Scegli che carta pescare",
"choose_guess": "Indovina il colore del seme", "choose_guess": "Indovina il colore del seme",
"choose_ranch": "Scegli le carte da sostituire", "choose_ranch": "Scegli le carte da sostituire",
"choose_dalton": "Scegli che equipaggiamento scartare",
"choose_fratelli_di_sangue": "Scegli a chi donare una delle tue vite", "choose_fratelli_di_sangue": "Scegli a chi donare una delle tue vite",
"choose_cecchino": "Scegli contro chi sparare", "choose_cecchino": "Scegli contro chi sparare",
"choose_rimbalzo_player": "Scegli contro chi scartare il bang", "choose_rimbalzo_player": "Scegli contro chi scartare il bang",
@ -60,6 +61,7 @@
"to_defend_from": "PER DIFENDERTI DA", "to_defend_from": "PER DIFENDERTI DA",
"submit": "Invia", "submit": "Invia",
"copy": "Copia invito", "copy": "Copia invito",
"no_players_in_range": "Non vedi nessun giocatore, equipaggia un arma o un mirino!",
"chat": { "chat": {
"chat": "Chat", "chat": "Chat",
"joined": "{0} è entrato nella stanza", "joined": "{0} è entrato nella stanza",
@ -90,7 +92,11 @@
"guess": "{0} pensa sia {1}.", "guess": "{0} pensa sia {1}.",
"guess_right": "{0} ha indovinato.", "guess_right": "{0} ha indovinato.",
"guess_wrong": "{0} ha sbagliato.", "guess_wrong": "{0} ha sbagliato.",
"fratelli_sangue": "{0} ha donato una delle sue vite a {1}." "fratelli_sangue": "{0} ha donato una delle sue vite a {1}.",
"doctor_heal": "{0} è stato curato dal dottore.",
"respond": "{0} ha risposto con {1}.",
"change_username": "{0} ha cambiato nome in {1}.",
"lobby_reset": "Si ritorna alla stanza in {0} secondi..."
}, },
"foc": { "foc": {
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile" "leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"