Merge pull request #15 from albertoxamin/dev
completed the implementation of dodge city
This commit is contained in:
commit
7b1ff4cedf
2
.github/workflows/dev-image.yml
vendored
2
.github/workflows/dev-image.yml
vendored
@ -5,6 +5,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: '1'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the Unified Docker image
|
||||
|
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@ -5,6 +5,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: '1'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the Unified Docker image
|
||||
|
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
@ -4,6 +4,8 @@ on:
|
||||
jobs:
|
||||
test_build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: '1'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the Unified Docker image
|
||||
|
@ -67,7 +67,7 @@ def get_me(sid, room):
|
||||
print('room exists')
|
||||
if room['username'] != None and any([p.name == room['username'] for p in de_games[0].players if p.is_bot]):
|
||||
print('getting inside the bot')
|
||||
bot = [p for p in de_games[0].players if p.is_bot][0]
|
||||
bot = [p for p in de_games[0].players if p.is_bot and p.name == room['username'] ][0]
|
||||
bot.sid = sid
|
||||
bot.is_bot = False
|
||||
sio.enter_room(sid, de_games[0].name)
|
||||
@ -82,6 +82,8 @@ def get_me(sid, room):
|
||||
sio.get_session(sid).game = de_games[0]
|
||||
sio.enter_room(sid, de_games[0].name)
|
||||
de_games[0].notify_room(sid)
|
||||
de_games[0].notify_all()
|
||||
de_games[0].notify_event_card()
|
||||
else:
|
||||
create_room(sid, room['name'])
|
||||
if sio.get_session(sid).game == None:
|
||||
@ -171,18 +173,53 @@ def chat_message(sid, msg):
|
||||
elif '/suicide' in msg and ses.game.started and ses.lives > 0:
|
||||
ses.lives = 0
|
||||
ses.notify_self()
|
||||
elif '/nextevent' in msg and ses.game.started:
|
||||
ses.game.deck.flip_event()
|
||||
elif '/notify' in msg and ses.game.started:
|
||||
cmd = msg.split()
|
||||
if len(cmd) >= 3:
|
||||
if cmd[1] in ses.game.players_map:
|
||||
ses.game.get_player_named(cmd[1]).notify_card(ses, {
|
||||
'name': ' '.join(cmd[2:]),
|
||||
'icon': '🚨',
|
||||
'suit': 4,
|
||||
'number': ' '.join(cmd[2:])
|
||||
})
|
||||
else:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||
elif '/debug_show_cards' in msg and ses.game.started:
|
||||
cmd = msg.split()
|
||||
if len(cmd) == 2:
|
||||
if cmd[1] in ses.game.players_map:
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and is looking at {cmd[1]} hand'})
|
||||
for c in ses.game.get_player_named(cmd[1]).hand:
|
||||
ses.notify_card(ses, c)
|
||||
eventlet.sleep(0.3)
|
||||
else:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||
elif '/togglecomp' in msg and ses.game:
|
||||
ses.game.toggle_competitive()
|
||||
elif '/togglebot' in msg and ses.game:
|
||||
ses.game.toggle_disconnect_bot()
|
||||
elif '/cancelgame' in msg and ses.game.started:
|
||||
ses.game.reset()
|
||||
elif '/startgame' in msg and not ses.game.started:
|
||||
ses.game.start_game()
|
||||
elif '/addex' in msg and not ses.game.started:
|
||||
cmd = msg.split()
|
||||
if len(cmd) == 2:
|
||||
cmd[1] = cmd[1].replace('foc', 'fistful_of_cards')
|
||||
if cmd[1] not in ses.game.available_expansions:
|
||||
ses.game.available_expansions.append(cmd[1])
|
||||
ses.game.notify_room()
|
||||
else:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||
elif '/gameinfo' in msg:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'#black','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:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'#black','text':f'info: {ses.__dict__}'})
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'})
|
||||
else:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'#black','text':f'{msg} COMMAND NOT FOUND'})
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
|
||||
else:
|
||||
color = sid.encode('utf-8').hex()[-3:]
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'#{color}','text':f'[{ses.name}]: {msg}'})
|
||||
@ -238,5 +275,15 @@ def scrap(sid, card_index):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.scrap(card_index)
|
||||
|
||||
@sio.event
|
||||
def chuck_lose_hp_draw(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.chuck_lose_hp_draw()
|
||||
|
||||
@sio.event
|
||||
def holyday_special(sid, data):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.holyday_special(data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
eventlet.wsgi.server(eventlet.listen(('', 5001)), app)
|
||||
|
@ -180,7 +180,8 @@ class Bang(Card):
|
||||
self.need_target = True
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if player.has_played_bang and not any([isinstance(c, Volcanic) for c in player.equipment]) and against != None:
|
||||
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)) and against != None:
|
||||
return False
|
||||
elif against != None:
|
||||
import bang.characters as chars
|
||||
|
@ -137,7 +137,7 @@ class VultureSam(Character):
|
||||
super().__init__("Vulture Sam", max_lives=4)
|
||||
self.desc = "Quando un personaggio viene eliminato prendi tutte le carte di quel giocatore e aggiungile alla tua mano, sia le carte in mano che quelle in gioco"
|
||||
self.desc_eng = "When a player dies, he gets all the cards in the dead's hand and equipments"
|
||||
self.icon = '💰'
|
||||
self.icon = '🦉'
|
||||
|
||||
class WillyTheKid(Character):
|
||||
def __init__(self):
|
||||
|
@ -1,6 +1,7 @@
|
||||
from typing import List, Set, Dict, Tuple, Optional
|
||||
import random
|
||||
import bang.cards as cs
|
||||
import bang.expansions.fistful_of_cards.card_events as ce
|
||||
|
||||
class Deck:
|
||||
def __init__(self, game):
|
||||
@ -18,10 +19,19 @@ class Deck:
|
||||
if c.name not in self.all_cards_str:
|
||||
self.all_cards_str.append(c.name)
|
||||
self.game = game
|
||||
self.event_cards: List[ce.CardEvent] = []
|
||||
if 'fistful_of_cards' in game.expansions:
|
||||
self.event_cards.extend(ce.get_all_events())
|
||||
random.shuffle(self.event_cards)
|
||||
random.shuffle(self.cards)
|
||||
self.scrap_pile: List[cs.Card] = []
|
||||
print(f'Deck initialized with {len(self.cards)} cards')
|
||||
|
||||
def flip_event(self):
|
||||
if len(self.event_cards) > 0:
|
||||
self.event_cards.append(self.event_cards.pop(0))
|
||||
self.game.notify_event_card()
|
||||
|
||||
def peek(self, n_cards: int) -> list:
|
||||
return self.cards[:n_cards]
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
|
||||
from bang.expansions.dodge_city import cards, characters
|
||||
class DodgeCity():
|
||||
def get_characters():
|
||||
from bang.expansions.dodge_city import characters
|
||||
return characters.all_characters()
|
||||
|
||||
def get_cards():
|
||||
from bang.expansions.dodge_city import cards
|
||||
return cards.get_starting_deck()
|
@ -71,6 +71,41 @@ class BelleStar(Character):
|
||||
self.desc_eng = "During her turn the green cards of the other players do not work."
|
||||
self.icon = '❎'
|
||||
|
||||
class VeraCuster(Character):
|
||||
def __init__(self):
|
||||
super().__init__("Vera Custer", max_lives=3)
|
||||
self.desc = "Prima di pescare le sue carte può scegliere l'abilità speciale di un altro giocatore fino al prossimo turno."
|
||||
self.desc_eng = "Before drawing, she may choose the special ability on another alive player. This ability is used until next turn."
|
||||
self.icon = '🎭'
|
||||
|
||||
class ChuckWengam(Character):
|
||||
def __init__(self):
|
||||
super().__init__("Chuck Wengam", max_lives=4)
|
||||
self.desc = "Durante il suo turno può perdere una vita per pescare 2 carte dal mazzo."
|
||||
self.desc_eng = "On his turn he may decide to lose 1 HP to draw 2 cards from the deck."
|
||||
self.icon = '💰'
|
||||
|
||||
class PatBrennan(Character):
|
||||
def __init__(self):
|
||||
super().__init__("Pat Brennan", max_lives=4)
|
||||
self.desc = "Invece di pescare può prendere una carta dall'equipaggiamento di un altro giocatore."
|
||||
self.desc_eng = "Instead of drawing he can steal a card from the equipment of another player."
|
||||
self.icon = '🤗'
|
||||
|
||||
class JoseDelgrado(Character):
|
||||
def __init__(self):
|
||||
super().__init__("José Delgrado", max_lives=4)
|
||||
self.desc = "Può scartare una carta blu per pescare 2 carte."
|
||||
self.desc_eng = "He can discard a blue card to draw 2 cards."
|
||||
self.icon = '🎒'
|
||||
|
||||
class DocHolyday(Character):
|
||||
def __init__(self):
|
||||
super().__init__("Doc Holyday", max_lives=4)
|
||||
self.desc = "Nel suo turno può scartare 2 carte per fare un bang."
|
||||
self.desc_eng = "He can discard 2 cards to play a bang."
|
||||
self.icon = '✌🏻'
|
||||
|
||||
def all_characters() -> List[Character]:
|
||||
return [
|
||||
PixiePete(),
|
||||
@ -83,6 +118,11 @@ def all_characters() -> List[Character]:
|
||||
ApacheKid(),
|
||||
SeanMallory(),
|
||||
BelleStar(),
|
||||
VeraCuster(),
|
||||
ChuckWengam(),
|
||||
PatBrennan(),
|
||||
JoseDelgrado(),
|
||||
DocHolyday(),
|
||||
]
|
||||
|
||||
#Apache Kid: il suo effetto non conta nei duelli
|
||||
|
115
backend/bang/expansions/fistful_of_cards/card_events.py
Normal file
115
backend/bang/expansions/fistful_of_cards/card_events.py
Normal file
@ -0,0 +1,115 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class CardEvent(ABC):
|
||||
def __init__(self, name, icon):
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
|
||||
class Agguato(CardEvent):
|
||||
def __init__(self):
|
||||
super().__init__('Agguato', '🛁')
|
||||
self.desc = 'La distanza base di tra 2 qualsiasi giocatori è 1'
|
||||
self.desc_eng = 'The base distance from any 2 players is 1'
|
||||
|
||||
class Cecchino(CardEvent):
|
||||
def __init__(self): #TODO
|
||||
super().__init__('Cecchino', '👁')
|
||||
self.desc = 'Nel proprio turno i giocatori possono scartare 2 Bang assieme per sparare un bang a cui servono 2 mancato'
|
||||
self.desc_eng = 'During their turn, players can discard 2 Bang! to shoot a bang that requires 2 missed'
|
||||
|
||||
class DeadMan(CardEvent):
|
||||
def __init__(self):
|
||||
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_eng = 'The first player that died return back to life with 2 hp and 2 cards'
|
||||
|
||||
class FratelliDiSangue(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Fratelli Di Sangue', '💉')
|
||||
self.desc = 'All\'inizio del proprio turno i giocatori possono perdere 1 vita per darla a un altro giocatore'
|
||||
self.desc_eng = 'At the begin of their turn, payers can lose 1 hp to give it to another player'
|
||||
|
||||
class IlGiudice(CardEvent):
|
||||
def __init__(self):
|
||||
super().__init__('Il Giudice', '👨⚖️')
|
||||
self.desc = 'Non si possono equipaggiare carte a se stessi o agli altri'
|
||||
self.desc_eng = 'You can\'t equip cards on your or other players'
|
||||
|
||||
class Lazo(CardEvent):
|
||||
def __init__(self):
|
||||
super().__init__('Lazo', '📿')
|
||||
self.desc = 'Le carte equipaggiate non hanno effetto'
|
||||
self.desc_eng = 'Cards in the equipment slot do not work'
|
||||
|
||||
class LeggeDelWest(CardEvent):
|
||||
def __init__(self):
|
||||
super().__init__('Legge Del West', '⚖️')
|
||||
self.desc = 'I giocatori mostrano la seconda carta che pescano e sono obbligati a usarla in quel turno (se possibile)'
|
||||
self.desc_eng = 'Every player shows the second card that they draw and must use it in that round'
|
||||
|
||||
class LiquoreForte(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Liquore Forte', '🥃')
|
||||
self.desc = 'I giocatori possono evitare di pescare per recuperare 1 vita'
|
||||
self.desc_eng = 'Players can skip drawing to regain 1 HP'
|
||||
|
||||
class MinieraAbbandonata(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Miniera Abbandonata', '⛏')
|
||||
self.desc = 'I giocatori pescano dagli scarti e scartano in cima al mazzo'
|
||||
self.desc_eng = 'Players draw from the discarded pile and discard to the deck'
|
||||
|
||||
class PerUnPugnoDiCarte(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Per Un Pugno Di Carte', '🎴')
|
||||
self.desc = 'Il giocatore subisce tanti bang quante carte ha in mano'
|
||||
self.desc_eng = 'On his turn the player is target of as many Bang as how many cards he has in his hand'
|
||||
|
||||
class Peyote(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Peyote', '🌵')
|
||||
self.desc = 'Invece che pescare il giocatore prova a indovinare il colore del seme, se lo indovina continua'
|
||||
self.desc_eng = 'Instead of drawing, the player tries to guess the color of the suit, if he\'s right he repeats'
|
||||
|
||||
class Ranch(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Ranch', '🐮')
|
||||
self.desc = 'Dopo aver pescato il giocatore può scartare quante carte vuole dalla mano e pescarne altrettante dal mazzo'
|
||||
self.desc_eng = 'After drawing, the player can discard as many cards as he wants from his hand and draw as many from the deck'
|
||||
|
||||
class Rimbalzo(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Rimbalzo', '⏮')
|
||||
self.desc = 'Il giocatore di turno può giocare bang contro le carte equipaggiate dagli altri giocatori, se non giocano mancato vengono scartate'
|
||||
self.desc_eng = 'The player can play bang against the cards equipped by the other players, if they do not play miss they are discarded'
|
||||
|
||||
class RouletteRussa(CardEvent):
|
||||
def __init__(self):#TODO
|
||||
super().__init__('Roulette Russa', '🇷🇺')
|
||||
self.desc = 'A partire dallo sceriffo, ogni giocatore scarta 1 mancato, il primo che non lo fa perde 2 vite'
|
||||
self.desc_eng = 'Starting from the sheriff, every player discards 1 missed, the first one that doesn\'t loses 2 HP'
|
||||
|
||||
class Vendetta(CardEvent):
|
||||
def __init__(self):
|
||||
super().__init__('Vendetta', '😤')
|
||||
self.desc = 'Alla fine del proprio turno il giocatore estrae, se esce ♥️ gioca un altro turno'
|
||||
self.desc_eng = 'When ending the turn, the player flips a card, if it\'s ♥️ he plays another turn'
|
||||
|
||||
def get_all_events():
|
||||
return [
|
||||
Agguato(),
|
||||
# Cecchino(),
|
||||
DeadMan(),
|
||||
# FratelliDiSangue(),
|
||||
IlGiudice(),
|
||||
Lazo(),
|
||||
LeggeDelWest(),
|
||||
# LiquoreForte(),
|
||||
# MinieraAbbandonata(),
|
||||
# PerUnPugnoDiCarte(),
|
||||
# Peyote(),
|
||||
# Ranch(),
|
||||
# Rimbalzo(),
|
||||
# RouletteRussa(),
|
||||
Vendetta(),
|
||||
]
|
@ -6,6 +6,7 @@ import bang.players as players
|
||||
import bang.characters as characters
|
||||
from bang.deck import Deck
|
||||
import bang.roles as roles
|
||||
import bang.expansions.fistful_of_cards.card_events as ce
|
||||
import eventlet
|
||||
|
||||
class Game:
|
||||
@ -23,6 +24,7 @@ class Game:
|
||||
self.initial_players = 0
|
||||
self.password = ''
|
||||
self.expansions = []
|
||||
self.available_expansions = ['dodge_city']
|
||||
self.shutting_down = False
|
||||
self.is_competitive = False
|
||||
self.disconnect_bot = True
|
||||
@ -37,6 +39,7 @@ class Game:
|
||||
'is_competitive': self.is_competitive,
|
||||
'disconnect_bot': self.disconnect_bot,
|
||||
'expansions': self.expansions,
|
||||
'available_expansions': self.available_expansions
|
||||
})
|
||||
|
||||
def toggle_expansion(self, expansion_name):
|
||||
@ -90,7 +93,11 @@ class Game:
|
||||
for k in range(self.players[i].max_lives):
|
||||
self.players[i].hand.append(self.deck.draw())
|
||||
self.players[i].notify_self()
|
||||
self.players[self.turn].play_turn()
|
||||
current_roles = [type(x.role).__name__ for x in self.players]
|
||||
random.shuffle(current_roles)
|
||||
current_roles = str({x:current_roles.count(x) for x in current_roles}).replace('{','').replace('}','')
|
||||
self.sio.emit('chat_message', room=self.name, data=f'_allroles|{current_roles}')
|
||||
self.play_turn()
|
||||
|
||||
def choose_characters(self):
|
||||
char_cards = random.sample(characters.all_characters(self.expansions), len(self.players)*2)
|
||||
@ -121,6 +128,8 @@ class Game:
|
||||
elif len(self.players) >= 4:
|
||||
available_roles = [roles.Sheriff(), roles.Renegade(), roles.Outlaw(), roles.Outlaw(), roles.Vice(), roles.Outlaw(), roles.Vice(), roles.Renegade(), roles.Outlaw(), roles.Vice(), roles.Outlaw()]
|
||||
available_roles = available_roles[:len(self.players)]
|
||||
else:
|
||||
available_roles = [roles.Renegade(), roles.Renegade()]
|
||||
random.shuffle(available_roles)
|
||||
for i in range(len(self.players)):
|
||||
self.players[i].set_role(available_roles[i])
|
||||
@ -209,6 +218,15 @@ class Game:
|
||||
return self.players[(self.turn + 1) % len(self.players)]
|
||||
|
||||
def play_turn(self):
|
||||
if isinstance(self.players[self.turn].role, roles.Sheriff):
|
||||
self.deck.flip_event()
|
||||
if self.check_event(ce.DeadMan) and len(self.dead_players) > 0:
|
||||
self.players.append(self.dead_players.pop(0))
|
||||
self.players[-1].lives = 2
|
||||
self.players[-1].hand.append(self.deck.draw())
|
||||
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()
|
||||
self.players[self.turn].play_turn()
|
||||
|
||||
def next_turn(self):
|
||||
@ -217,6 +235,10 @@ class Game:
|
||||
self.turn = (self.turn + 1) % len(self.players)
|
||||
self.play_turn()
|
||||
|
||||
def notify_event_card(self):
|
||||
if len(self.deck.event_cards) > 0:
|
||||
self.sio.emit('event_card', room=self.name, data=self.deck.event_cards[0].__dict__)
|
||||
|
||||
def notify_scrap_pile(self):
|
||||
print('scrap')
|
||||
if self.deck.peek_scrap_pile():
|
||||
@ -229,6 +251,8 @@ class Game:
|
||||
if player in self.players:
|
||||
if self.disconnect_bot and self.started:
|
||||
player.is_bot = True
|
||||
eventlet.sleep(15) # he may reconnect
|
||||
player.notify_self()
|
||||
else:
|
||||
self.player_death(player=player, disconnected=True)
|
||||
else:
|
||||
@ -329,12 +353,17 @@ class Game:
|
||||
eventlet.sleep(0.5)
|
||||
self.notify_room()
|
||||
|
||||
def check_event(self, ev):
|
||||
if len(self.deck.event_cards) == 0: return False
|
||||
return isinstance(self.deck.event_cards[0], ev)
|
||||
|
||||
def get_visible_players(self, player: players.Player):
|
||||
i = self.players.index(player)
|
||||
sight = player.get_sight()
|
||||
mindist = 99 if not self.check_event(ce.Agguato) else 1
|
||||
return [{
|
||||
'name': self.players[j].name,
|
||||
'dist': min(abs(i - j), (i+ abs(j-len(self.players))), (j+ abs(i-len(self.players)))) + self.players[j].get_visibility() - (player.get_sight(countWeapon=False)-1),
|
||||
'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),
|
||||
'lives': self.players[j].lives,
|
||||
'max_lives': self.players[j].max_lives,
|
||||
'is_sheriff': isinstance(self.players[j].role, roles.Sheriff),
|
||||
@ -352,6 +381,7 @@ class Game:
|
||||
'is_my_turn': p.is_my_turn,
|
||||
'pending_action': p.pending_action,
|
||||
'character': p.character.__dict__ if p.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 '🤠'
|
||||
} for p in self.players]
|
||||
self.sio.emit('players_update', room=self.name, data=data)
|
||||
|
@ -8,6 +8,7 @@ import bang.cards as cs
|
||||
import bang.expansions.dodge_city.cards as csd
|
||||
import bang.characters as chars
|
||||
import bang.expansions.dodge_city.characters as chd
|
||||
import bang.expansions.fistful_of_cards.card_events as ce
|
||||
import eventlet
|
||||
|
||||
class PendingAction(IntEnum):
|
||||
@ -30,6 +31,7 @@ class Player:
|
||||
self.equipment: cs.Card = []
|
||||
self.role: r.Role = None
|
||||
self.character: chars.Character = None
|
||||
self.real_character: chars.Character = None
|
||||
self.lives = 0
|
||||
self.max_lives = 0
|
||||
self.game: g = None
|
||||
@ -49,12 +51,14 @@ class Player:
|
||||
self.mancato_needed = 0
|
||||
self.molly_discarded_cards = 0
|
||||
self.is_bot = bot
|
||||
self.special_use_count = 0
|
||||
|
||||
def reset(self):
|
||||
self.hand: cs.Card = []
|
||||
self.equipment: cs.Card = []
|
||||
self.role: r.Role = None
|
||||
self.character: chars.Character = None
|
||||
self.real_character: chars.Character = None
|
||||
self.lives = 0
|
||||
self.max_lives = 0
|
||||
self.is_my_turn = False
|
||||
@ -70,6 +74,7 @@ class Player:
|
||||
self.attacker: Player = None
|
||||
self.target_p: str = None
|
||||
self.is_drawing = False
|
||||
self.special_use_count = 0
|
||||
try:
|
||||
del self.win_status
|
||||
except:
|
||||
@ -92,13 +97,24 @@ class Player:
|
||||
|
||||
def set_character(self, character: str):
|
||||
print(self.available_characters, character)
|
||||
self.character = next(
|
||||
x for x in self.available_characters if x.name == character)
|
||||
self.available_characters = []
|
||||
print(f'I {self.name} chose character {self.character.name}')
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_did_choose_character|{self.name}')
|
||||
self.game.notify_character_selection()
|
||||
if self.character == None:
|
||||
self.character = next(
|
||||
x for x in self.available_characters if x.name == character)
|
||||
self.real_character = self.character
|
||||
self.available_characters = []
|
||||
print(f'I {self.name} chose character {self.character.name}')
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_did_choose_character|{self.name}')
|
||||
self.game.notify_character_selection()
|
||||
elif self.real_character and isinstance(self.real_character, chd.VeraCuster):
|
||||
self.character = next(
|
||||
x for x in self.available_characters if x.name == character)
|
||||
self.available_characters = []
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_did_choose_character|{self.name}')
|
||||
self.pending_action = PendingAction.DRAW
|
||||
self.notify_self()
|
||||
|
||||
|
||||
def prepare(self):
|
||||
self.max_lives = self.character.max_lives + self.role.health_mod
|
||||
@ -116,10 +132,15 @@ class Player:
|
||||
else:
|
||||
self.set_character(available[randrange(0, len(available))].name)
|
||||
|
||||
def notify_card(self, player, card):
|
||||
def notify_card(self, player, card, message=''):
|
||||
try:
|
||||
card = card.__dict__
|
||||
except:
|
||||
pass
|
||||
mess = {
|
||||
'player': player.name,
|
||||
'card': card.__dict__
|
||||
'card': card,
|
||||
'message':message
|
||||
}
|
||||
print('notifying card')
|
||||
self.sio.emit('notify_card', room=self.sid, data=mess)
|
||||
@ -184,7 +205,7 @@ class Player:
|
||||
self.draw('')
|
||||
elif self.pending_action == PendingAction.PLAY:
|
||||
has_played = False
|
||||
if len([c for c in self.hand if c.is_equipment or c.usable_next_turn]) > 0:
|
||||
if len([c for c in self.hand if (c.is_equipment or c.usable_next_turn) and not self.game.check_event(ce.IlGiudice)]) > 0:
|
||||
for i in range(len(self.hand)):
|
||||
if self.hand[i].is_equipment or self.hand[i].usable_next_turn:
|
||||
self.play_card(i)
|
||||
@ -246,7 +267,7 @@ class Player:
|
||||
did_respond = True
|
||||
break
|
||||
for i in range(len(self.equipment)):
|
||||
if self.equipment[i].name in self.expected_response:
|
||||
if not self.game.check_event(ce.Lazo) and self.equipment[i].name in self.expected_response:
|
||||
self.respond(len(self.hand)+i)
|
||||
did_respond = True
|
||||
break
|
||||
@ -274,10 +295,14 @@ class Player:
|
||||
self.is_my_turn = True
|
||||
self.is_waiting_for_action = True
|
||||
self.has_played_bang = False
|
||||
if any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
self.special_use_count = 0
|
||||
if not self.game.check_event(ce.Lazo) and any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
self.pending_action = PendingAction.PICK
|
||||
else:
|
||||
self.pending_action = PendingAction.DRAW
|
||||
if isinstance(self.real_character, chd.VeraCuster):
|
||||
self.set_available_character([p.character for p in self.game.players if p != self])
|
||||
else:
|
||||
self.pending_action = PendingAction.DRAW
|
||||
self.notify_self()
|
||||
|
||||
def draw(self, pile):
|
||||
@ -288,6 +313,11 @@ class Player:
|
||||
self.available_cards = [self.game.deck.draw() for i in range(3)]
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
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:
|
||||
self.is_drawing = True
|
||||
self.available_cards = self.game.get_player_named(pile).equipment
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
self.notify_self()
|
||||
else:
|
||||
self.pending_action = PendingAction.PLAY
|
||||
if pile == 'scrap' and isinstance(self.character, chars.PedroRamirez):
|
||||
@ -310,11 +340,11 @@ class Player:
|
||||
for i in range(2):
|
||||
card: cs.Card = self.game.deck.draw()
|
||||
self.hand.append(card)
|
||||
if i == 1 and isinstance(self.character, chars.BlackJack):
|
||||
if i == 1 and isinstance(self.character, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest):
|
||||
for p in self.game.players:
|
||||
if p != self:
|
||||
p.notify_card(self, card)
|
||||
if card.suit == cs.Suit.HEARTS or card.suit == cs.Suit.DIAMONDS:
|
||||
p.notify_card(self, card, 'blackjack_special' if isinstance(self.character, chars.BlackJack) else 'foc.leggedelwest')
|
||||
if card.suit == cs.Suit.HEARTS or card.suit == cs.Suit.DIAMONDS and isinstance(self.character, chars.BlackJack):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
if isinstance(self.character, chd.PixiePete):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
@ -371,7 +401,10 @@ class Player:
|
||||
if any([isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
self.notify_self()
|
||||
return
|
||||
self.pending_action = PendingAction.DRAW
|
||||
if isinstance(self.real_character, chd.VeraCuster):
|
||||
self.set_available_character([p.character for p in self.game.players if p != self])
|
||||
else:
|
||||
self.pending_action = PendingAction.DRAW
|
||||
self.notify_self()
|
||||
else:
|
||||
self.pending_action = PendingAction.WAIT
|
||||
@ -405,9 +438,10 @@ class Player:
|
||||
withCard = self.hand.pop(_with) if hand_index > _with else self.hand.pop(_with - 1)
|
||||
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
|
||||
did_play_card = False
|
||||
if not(against != None and isinstance(self.game.get_player_named(against).character, chd.ApacheKid) and card.suit == cs.Suit.DIAMONDS):
|
||||
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:
|
||||
did_play_card = card.play_card(self, against, withCard)
|
||||
if not card.is_equipment and not card.usable_next_turn:
|
||||
if not card.is_equipment and not card.usable_next_turn or event_blocks_card:
|
||||
if did_play_card:
|
||||
self.game.deck.scrap(card)
|
||||
else:
|
||||
@ -456,6 +490,14 @@ class Player:
|
||||
self.is_drawing = False
|
||||
self.pending_action = PendingAction.PLAY
|
||||
self.notify_self()
|
||||
elif self.is_drawing and isinstance(self.character, chd.PatBrennan):
|
||||
card = self.available_cards.pop(card_index)
|
||||
if card.usable_next_turn:
|
||||
card.can_be_used_now = False
|
||||
self.hand.append(card)
|
||||
self.available_cards = []
|
||||
self.pending_action = PendingAction.PLAY
|
||||
self.notify_self()
|
||||
else: # emporio
|
||||
self.game.respond_emporio(self, card_index)
|
||||
|
||||
@ -500,7 +542,7 @@ class Player:
|
||||
self.take_damage_response()
|
||||
return False
|
||||
else:
|
||||
if 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 isinstance(self.character, chars.Jourdonnais):
|
||||
print('has barrel')
|
||||
self.pending_action = PendingAction.PICK
|
||||
self.on_pick_cb = self.barrel_pick
|
||||
@ -508,7 +550,7 @@ class Player:
|
||||
print('has mancato')
|
||||
self.pending_action = PendingAction.RESPOND
|
||||
self.expected_response = self.game.deck.mancato_cards
|
||||
if self.attacker and isinstance(self.attacker.character, chd.BelleStar):
|
||||
if self.attacker and isinstance(self.attacker.character, chd.BelleStar) or self.game.check_event(ce.Lazo):
|
||||
self.expected_response = self.game.deck.mancato_cards_not_green
|
||||
if isinstance(self.character, chd.ElenaFuente):
|
||||
self.expected_response = self.game.deck.all_cards_str
|
||||
@ -621,6 +663,8 @@ class Player:
|
||||
range += card.range
|
||||
else:
|
||||
aim += card.sight_mod
|
||||
if self.game.check_event(ce.Lazo):
|
||||
return 1 + self.character.sight_mod
|
||||
return max(1, range) + aim + self.character.sight_mod
|
||||
|
||||
def get_visibility(self):
|
||||
@ -629,15 +673,38 @@ class Player:
|
||||
covers = 0
|
||||
for card in self.equipment:
|
||||
covers += card.vis_mod
|
||||
if self.game.check_event(ce.Lazo):
|
||||
return self.character.visibility_mod
|
||||
return self.character.visibility_mod + covers
|
||||
|
||||
def scrap(self, card_index):
|
||||
if self.is_my_turn or isinstance(self.character, chars.SidKetchum):
|
||||
self.scrapped_cards += 1
|
||||
card = self.hand.pop(card_index)
|
||||
if isinstance(self.character, chars.SidKetchum) and self.scrapped_cards == 2:
|
||||
self.scrapped_cards = 0
|
||||
self.lives = min(self.lives+1, self.max_lives)
|
||||
self.game.deck.scrap(self.hand.pop(card_index))
|
||||
elif isinstance(self.character, chd.JoseDelgrado) and card.is_equipment and self.special_use_count < 2:
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.special_use_count += 1
|
||||
self.game.deck.scrap(card)
|
||||
self.notify_self()
|
||||
|
||||
def holyday_special(self, data):
|
||||
if isinstance(self.character, chd.DocHolyday) and self.special_use_count < 1:
|
||||
self.special_use_count += 1
|
||||
cards = sorted(data['cards'], reverse=True)
|
||||
for c in cards:
|
||||
self.game.deck.scrap(self.hand.pop(c))
|
||||
self.notify_self()
|
||||
self.game.attack(self, data['against'])
|
||||
|
||||
def chuck_lose_hp_draw(self):
|
||||
if isinstance(self.character, chd.ChuckWengam) and self.lives > 1 and self.is_my_turn:
|
||||
self.lives -= 1
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.notify_self()
|
||||
|
||||
def end_turn(self, forced=False):
|
||||
@ -648,6 +715,12 @@ class Player:
|
||||
print(
|
||||
f"I {self.name} have to many cards in my hand and I can't end the turn")
|
||||
elif self.pending_action == PendingAction.PLAY or forced:
|
||||
if not forced and self.game.check_event(ce.Vendetta):
|
||||
picked: cs.Card = self.game.deck.pick_and_scrap()
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked}')
|
||||
if picked.suit == cs.Suit.HEARTS:
|
||||
self.play_turn()
|
||||
return
|
||||
self.is_my_turn = False
|
||||
for i in range(len(self.equipment)):
|
||||
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB |
@ -3,7 +3,7 @@
|
||||
<h4>{{card.name}}</h4>
|
||||
<div class="emoji">{{card.icon}}</div>
|
||||
<div class="alt_text">{{card.alt_text}}</div>
|
||||
<div class="suit">{{card.number}}{{suit}}</div>
|
||||
<div class="suit">{{number}}{{suit}}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -20,6 +20,13 @@ export default {
|
||||
return x[this.card.suit];
|
||||
}
|
||||
return '';
|
||||
},
|
||||
number() {
|
||||
if (this.card.number === 1) return 'A'
|
||||
else if (this.card.number === 11) return 'J'
|
||||
else if (this.card.number === 12) return 'Q'
|
||||
else if (this.card.number === 13) return 'K'
|
||||
else return this.card.number
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,6 +79,14 @@ export default {
|
||||
box-shadow:
|
||||
0 0 0 3pt #6aa16e, 0 0 0 6pt white, 0 0 5pt 6pt #aaa
|
||||
}
|
||||
.card.high-noon{
|
||||
box-shadow: 0 0 0pt 4pt white, 0 0 5pt 4pt #aaa;
|
||||
border: 2pt dotted rgb(198 78 45);
|
||||
}
|
||||
.card.fistful-of-cards{
|
||||
box-shadow: 0 0 0pt 4pt white, 0 0 5pt 4pt #aaa;
|
||||
border: 2pt dashed rgb(50 122 172);
|
||||
}
|
||||
.card h4 {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
@ -85,6 +100,10 @@ export default {
|
||||
font-size:26pt;
|
||||
top: 35%;
|
||||
}
|
||||
.fistful-of-cards .emoji, .high-noon .emoji{
|
||||
top:auto !important;
|
||||
bottom:15% !important;
|
||||
}
|
||||
.card .suit {
|
||||
position: absolute;
|
||||
bottom: 3pt;
|
||||
@ -135,5 +154,11 @@ export default {
|
||||
box-shadow:
|
||||
0 0 0 3pt #6aa16e, 0 0 0 6pt #181a1b, 0 0 5pt 6pt #aaa
|
||||
}
|
||||
.card.high-noon{
|
||||
box-shadow: 0 0 0pt 4pt #181a1b, 0 0 5pt 4pt #aaa;
|
||||
}
|
||||
.card.fistful-of-cards{
|
||||
box-shadow: 0 0 0pt 4pt #181a1b, 0 0 5pt 4pt #aaa;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -2,6 +2,11 @@
|
||||
<div>
|
||||
<div class="deck">
|
||||
<card v-if="endTurnAction && isPlaying" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/>
|
||||
<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:absolute; bottom:-1.5pt;right:-1.5pt;"/>
|
||||
<card :card="eventCard" :key="eventCard" :class="{'last-event':true,'fistful-of-cards':true}"/>
|
||||
</div>
|
||||
<div style="position:relative">
|
||||
<div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/>
|
||||
<div class="card back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
|
||||
@ -15,6 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<transition name="list">
|
||||
<p v-if="eventCard" class="center-stuff"><i>{{($i18n.locale=='it'?eventCard.desc:eventCard.desc_eng)}}</i></p>
|
||||
<p v-if="desc" class="center-stuff"><i>{{desc}}</i></p>
|
||||
</transition>
|
||||
</div>
|
||||
@ -37,6 +43,7 @@ export default {
|
||||
icon: '💥',
|
||||
},
|
||||
lastScrap: null,
|
||||
eventCard: null,
|
||||
previousScrap: null,
|
||||
pending_action: false,
|
||||
isPlaying: true,
|
||||
@ -50,7 +57,10 @@ export default {
|
||||
},
|
||||
scrap(card) {
|
||||
this.lastScrap = card
|
||||
}
|
||||
},
|
||||
event_card(card) {
|
||||
this.eventCard = card
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
endTurnCard() {
|
||||
@ -81,10 +91,10 @@ export default {
|
||||
</script>
|
||||
<style scoped>
|
||||
.deck {
|
||||
display:flex;
|
||||
margin:0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display:flex;
|
||||
margin:0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.last-scrap {
|
||||
@ -105,6 +115,20 @@ export default {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
}
|
||||
.last-event {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
animation-duration: 0.8s;
|
||||
animation-name: slidein;
|
||||
}
|
||||
@keyframes slidein {
|
||||
from {
|
||||
transform: translate(30px, 20px) scale(1.3) rotate(-10deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
}
|
||||
.pick:hover {
|
||||
transform: translate(-10px,0);
|
||||
z-index: 1;
|
||||
|
@ -22,6 +22,7 @@
|
||||
</transition-group>
|
||||
<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 && 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]"/>
|
||||
<tiny-hand :ncards="p.ncards" @click.native="drawFromPlayer(p.name)" :ismyturn="p.pending_action === 2"/>
|
||||
<span style="position:absolute;top:10pt;" class="center-stuff">{{getActionEmoji(p)}}</span>
|
||||
<div class="tiny-equipment">
|
||||
@ -33,7 +34,10 @@
|
||||
</div>
|
||||
<div v-if="!started">
|
||||
<h3>{{$t("expansions")}}</h3>
|
||||
<PrettyCheck @click.native="toggleExpansions('dodge_city')" :disabled="!isRoomOwner" v-model="useDodgeCity" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">Dodge City</PrettyCheck>
|
||||
<div v-for="ex in togglable_expansions" v-bind:key="ex">
|
||||
<PrettyCheck @click.native="toggleExpansions(ex)" :disabled="!isRoomOwner" :value="is_toggled_expansion(ex)" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{get_expansion_name(ex)}}</PrettyCheck>
|
||||
<br>
|
||||
</div>
|
||||
<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>
|
||||
<br>
|
||||
@ -82,7 +86,6 @@ export default {
|
||||
players: [],
|
||||
messages: [],
|
||||
distances: {},
|
||||
self: {},
|
||||
hasToChoose: false,
|
||||
target_p: '',
|
||||
chooseCards: [],
|
||||
@ -90,7 +93,8 @@ export default {
|
||||
selectedInfo: null,
|
||||
privateRoom: false,
|
||||
password: '',
|
||||
useDodgeCity: false,
|
||||
togglable_expansions: [],
|
||||
expansions: [],
|
||||
hasToSetUsername: false,
|
||||
is_competitive: false,
|
||||
disconnect_bot: false,
|
||||
@ -103,7 +107,8 @@ export default {
|
||||
this.privateRoom = data.password !== ''
|
||||
this.is_competitive = data.is_competitive
|
||||
this.disconnect_bot = data.disconnect_bot
|
||||
this.useDodgeCity = data.expansions.indexOf('dodge_city') !== -1
|
||||
this.togglable_expansions = data.available_expansions
|
||||
this.expansions = data.expansions
|
||||
this.players = data.players.map(x => {
|
||||
return {
|
||||
name: x.name,
|
||||
@ -168,6 +173,12 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
is_toggled_expansion(ex) {
|
||||
return this.expansions.indexOf(ex) !== -1
|
||||
},
|
||||
get_expansion_name(ex) {
|
||||
return ex.replace('_', ' ').replace(/\w\S*/g, m => m.charAt(0).toUpperCase()+m.substr(1).toLowerCase())
|
||||
},
|
||||
leaveRoom() {
|
||||
window.location.replace(window.location.origin)
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-if="instruction" class="center-stuff">{{instruction}}</p>
|
||||
<p v-if="instruction && lives > 0" class="center-stuff">{{instruction}}</p>
|
||||
<!-- <button v-if="canEndTurn" @click="end_turn">Termina Turno</button> -->
|
||||
<div class="equipment-slot">
|
||||
<Card v-if="my_role" :card="my_role" class="back"
|
||||
@ -20,6 +20,10 @@
|
||||
<transition name="list">
|
||||
<p v-if="desc"><i>{{desc}}</i></p>
|
||||
</transition>
|
||||
<button v-if="is_my_turn && character.name === 'Sid Ketchum' && lives < max_lives && hand.length > 1" @click="sidWantsScrapForHealth=true">{{$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 === 'Doc Holyday' && special_use_count < 1 && hand.length > 1" @click="holydayScrap=true">{{$t('special_ability')}}</button>
|
||||
<div v-if="lives > 0" style="position:relative">
|
||||
<span id="hand_text">{{$t('hand')}}</span>
|
||||
<transition-group name="list" tag="div" class="hand">
|
||||
@ -38,15 +42,19 @@
|
||||
<Chooser v-if="lives <= 0 && max_lives > 0" :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="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('if_card_red')" 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 && 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="!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="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${lives}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/>
|
||||
<Chooser v-if="sidWantsScrapForHealth && sidScrapForHealth.length < 2" :text="`${$t('discard')} ${2 - sidScrapForHealth.length} ${$t('to_regain_1_hp')}`"
|
||||
:cards="sidScrapHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;sidScrapForHealth=[]}"/>
|
||||
<button v-if="is_my_turn && character.name === 'Sid Ketchum'" @click="sidWantsScrapForHealth=true">{{$t('special_ability')}}</button>
|
||||
<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=[]}"/>
|
||||
<Chooser v-if="joseScrap" :text="`${$t('discard')}`"
|
||||
:cards="hand.filter(x => x.is_equipment)" :select="(card) => {joseScrap=false;scrap(card)}" :cancel="() => {joseScrap=false}"/>
|
||||
<Chooser v-if="holydayScrap && scrapHand.length < 2" :text="`${$t('discard')} ${2 - scrapHand.length}`"
|
||||
:cards="notScrappedHand" :select="holydayScrapAdd" :cancel="() => {holydayScrap = false;scrapHand=[]}"/>
|
||||
<Chooser v-if="holydayScrap && scrapHand.length == 2" :text="$t('card_against')" :cards="otherPlayers" :select="holydayScrapBang" :cancel="() => {holydayScrap = false;scrapHand=[]}"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -91,8 +99,11 @@ export default {
|
||||
attacker: undefined,
|
||||
notifycard: null,
|
||||
desc: '',
|
||||
sidScrapForHealth: [],
|
||||
scrapHand: [],
|
||||
sidWantsScrapForHealth: false,
|
||||
joseScrap: false,
|
||||
holydayScrap: false,
|
||||
special_use_count: 0,
|
||||
mancato_needed: 0,
|
||||
name: '',
|
||||
}),
|
||||
@ -116,6 +127,7 @@ export default {
|
||||
this.lives = self.lives
|
||||
this.max_lives = self.max_lives
|
||||
this.has_played_bang = self.has_played_bang
|
||||
this.special_use_count = self.special_use_count
|
||||
this.is_my_turn = self.is_my_turn
|
||||
if (this.is_my_turn) document.title = this.$t('your_turn')+' | PewPew!'
|
||||
else if (this.pending_action == 3) document.title = this.$t('your_response')+' | PewPew!'
|
||||
@ -152,8 +164,20 @@ export default {
|
||||
showScrapScreen() {
|
||||
return this.isEndingTurn && !this.canEndTurn && this.is_my_turn;
|
||||
},
|
||||
sidScrapHand() {
|
||||
return this.hand.filter((x, i) => (this.sidScrapForHealth.indexOf(i) === -1))
|
||||
notScrappedHand() {
|
||||
return this.hand.filter((x, i) => (this.scrapHand.indexOf(i) === -1))
|
||||
},
|
||||
otherPlayers() {
|
||||
let vis = this.playersDistances.filter(x => {
|
||||
return x.name !== this.name
|
||||
}).map(player => {
|
||||
return {
|
||||
name: player.name,
|
||||
number: player.dist !== undefined ? `${player.dist}⛰` : '',
|
||||
icon: player.is_sheriff ? '⭐' : '🤠',
|
||||
is_character: true,
|
||||
}})
|
||||
return vis
|
||||
},
|
||||
visiblePlayers() {
|
||||
this.range;
|
||||
@ -212,14 +236,29 @@ export default {
|
||||
this.$socket.emit('set_character', char.name)
|
||||
},
|
||||
sidScrap(c) {
|
||||
this.sidScrapForHealth.push(this.hand.indexOf(c))
|
||||
if (this.sidScrapForHealth.length == 2) {
|
||||
this.$socket.emit('scrap', this.hand.indexOf(this.sidScrapForHealth[0]))
|
||||
this.$socket.emit('scrap', this.hand.indexOf(this.sidScrapForHealth[1]))
|
||||
this.sidScrapForHealth = []
|
||||
this.scrapHand.push(this.hand.indexOf(c))
|
||||
if (this.scrapHand.length == 2) {
|
||||
let x = [this.hand.indexOf(this.scrapHand[0]), this.hand.indexOf(this.scrapHand[1])].sort().reverse()
|
||||
this.$socket.emit('scrap', x[0])
|
||||
this.$socket.emit('scrap', x[1])
|
||||
this.scrapHand = []
|
||||
this.sidWantsScrapForHealth = false
|
||||
}
|
||||
},
|
||||
holydayScrapAdd(c) {
|
||||
this.scrapHand.push(this.hand.indexOf(c))
|
||||
},
|
||||
holydayScrapBang(other) {
|
||||
this.$socket.emit('holyday_special', {
|
||||
cards : [this.hand.indexOf(this.scrapHand[0]), this.hand.indexOf(this.scrapHand[1])],
|
||||
against: other.name
|
||||
})
|
||||
this.scrapHand = []
|
||||
this.holydayScrap = false
|
||||
},
|
||||
chuckSpecial(){
|
||||
this.$socket.emit('chuck_lose_hp_draw')
|
||||
},
|
||||
end_turn(){
|
||||
console.log('ending turn')
|
||||
this.cancelEndingTurn()
|
||||
|
@ -48,7 +48,7 @@
|
||||
"play_your_turn": "PLAY YOUR TURN",
|
||||
"you_are": "You are",
|
||||
"did_pick_as": "picked this as second card",
|
||||
"if_card_red":"If the card is diamonds or hearts, he picks another card.",
|
||||
"blackjack_special":"If the card is diamonds or hearts, he picks another card.",
|
||||
"choose_scarp_card_to": "CHOOSE WHICH CARD TO DISCARD TO USE",
|
||||
"pick_a_card": "FLIP A CARD",
|
||||
"to_defend_from": "TO DEFEND YOURSELF FROM",
|
||||
@ -79,7 +79,11 @@
|
||||
"saloon": "{0} player {1} and heals 1 HP to everyone alive.",
|
||||
"special_bart_cassidy": "{0} received a compensation because he was injured.",
|
||||
"special_el_gringo": "{0} stole a card from {1} when he was was injured.",
|
||||
"special_calamity": "{0} played {1} as Bang! against {2}."
|
||||
"special_calamity": "{0} played {1} as Bang! against {2}.",
|
||||
"allroles": "In the game there are: {0}."
|
||||
},
|
||||
"foc": {
|
||||
"leggedelwest": "He must play this card on this turn if possible."
|
||||
},
|
||||
"mods": "Modifiers",
|
||||
"mod_comp": "Competitive mode (disables automatic take damage)",
|
||||
|
@ -48,7 +48,7 @@
|
||||
"play_your_turn": "GIOCA IL TUO TURNO",
|
||||
"you_are": "Tu sei",
|
||||
"did_pick_as": "ha pescato come seconda carta",
|
||||
"if_card_red": "Se la carta è cuori o quadri ne pesca un'altra",
|
||||
"blackjack_special": "Se la carta è cuori o quadri ne pesca un'altra",
|
||||
"choose_scarp_card_to": "SCEGLI CHE CARTA SCARTARE PER USARE",
|
||||
"pick_a_card": "ESTRAI UNA CARTA",
|
||||
"to_defend_from": "PER DIFENDERTI DA",
|
||||
@ -79,7 +79,11 @@
|
||||
"saloon": "{0} ha giocato {1} e ha curato 1 punto vita a tutti.",
|
||||
"special_bart_cassidy": "{0} ha ricevuto un risarcimento perchè è stato ferito.",
|
||||
"special_el_gringo": "{0} rubato una carta a {1} mentre veniva colpito.",
|
||||
"special_calamity": "{0} ha giovato {1} come un Bang! contro {2}."
|
||||
"special_calamity": "{0} ha giovato {1} come un Bang! contro {2}.",
|
||||
"allroles": "Nella partita ci sono: {0}."
|
||||
},
|
||||
"foc": {
|
||||
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"
|
||||
},
|
||||
"mods": "Modificatori",
|
||||
"mod_comp": "Modalità competitiva (disattiva il prendi danno automatico)",
|
||||
|
Loading…
Reference in New Issue
Block a user