Merge pull request #4 from albertoxamin/dev

merge Dev
This commit is contained in:
Alberto Xamin 2020-11-28 11:43:14 +01:00 committed by GitHub
commit b1fdc440db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 449 additions and 130 deletions

1
.gitignore vendored
View File

@ -138,3 +138,4 @@ dmypy.json
cython_debug/ cython_debug/
frontend/package-lock.json frontend/package-lock.json
bang-workspace.code-workspace

View File

@ -19,7 +19,7 @@ games: List[Game] = []
online_players = 0 online_players = 0
def advertise_lobbies(): def advertise_lobbies():
sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if not g.started and len(g.players) < 7]) sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if not g.started and len(g.players) < 10])
@sio.event @sio.event
def connect(sid, environ): def connect(sid, environ):
@ -90,58 +90,58 @@ def join_room(sid, room):
@sio.event @sio.event
def chat_message(sid, msg): def chat_message(sid, msg):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
sio.emit('chat_message', room=ses.game.name, data=f'[{ses.name}]: {msg}') sio.emit('chat_message', room=ses.game.name, data=f'[{ses.name}]: {msg}')
@sio.event @sio.event
def start_game(sid): def start_game(sid):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.game.start_game() ses.game.start_game()
advertise_lobbies() advertise_lobbies()
@sio.event @sio.event
def set_character(sid, name): def set_character(sid, name):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.set_character(name) ses.set_character(name)
@sio.event @sio.event
def refresh(sid): def refresh(sid):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.notify_self() ses.notify_self()
@sio.event @sio.event
def draw(sid, pile): def draw(sid, pile):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.draw(pile) ses.draw(pile)
@sio.event @sio.event
def pick(sid): def pick(sid):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.pick() ses.pick()
@sio.event @sio.event
def end_turn(sid): def end_turn(sid):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.end_turn() ses.end_turn()
@sio.event @sio.event
def play_card(sid, data): def play_card(sid, data):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.play_card(data['index'], data['against']) ses.play_card(data['index'], data['against'], data['with'])
@sio.event @sio.event
def respond(sid, data): def respond(sid, data):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.respond(data) ses.respond(data)
@sio.event @sio.event
def choose(sid, card_index): def choose(sid, card_index):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.choose(card_index) ses.choose(card_index)
@sio.event @sio.event
def scrap(sid, card_index): def scrap(sid, card_index):
ses = sio.get_session(sid) ses: Player = sio.get_session(sid)
ses.scrap(card_index) ses.scrap(card_index)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -35,13 +35,15 @@ class Card(ABC):
self.alt_text = f'{self.range} 🔍' self.alt_text = f'{self.range} 🔍'
self.desc = desc self.desc = desc
self.need_target = False self.need_target = False
self.can_target_self = False
self.need_with = False
def __str__(self): def __str__(self):
char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)] char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)]
return f'{self.name} {char}{self.number}' return f'{self.name} {char}{self.number}'
return super().__str__() return super().__str__()
def play_card(self, player, against):#self --> carta def play_card(self, player, against=None, _with=None):#self --> carta
if self.is_equipment: if self.is_equipment:
if self.is_weapon: if self.is_weapon:
has_weapon = False has_weapon = False
@ -106,10 +108,10 @@ 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.need_target = True self.need_target = True
def play_card(self, player, against): 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):
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
data=f'{self.name} ha giocato {self.name} contro {against}.') data=f'{player.name} ha giocato {self.name} contro {against}.')
player.game.get_player_named(against).equipment.append(self) player.game.get_player_named(against).equipment.append(self)
player.game.get_player_named(against).notify_self() player.game.get_player_named(against).notify_self()
return False return False
@ -158,10 +160,10 @@ class Bang(Card):
def __init__(self, suit, number): def __init__(self, suit, number):
super().__init__(suit, 'Bang!', number) super().__init__(suit, 'Bang!', number)
self.icon = '💥' self.icon = '💥'
self.desc = "Spara a un giocatore a distanta raggiungibile. Se non hai armi la distanza di default è 1" self.desc = "Spara a un giocatore a distanza raggiungibile. Se non hai armi la distanza di default è 1"
self.need_target = True self.need_target = True
def play_card(self, player, against): 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: if player.has_played_bang and not any([isinstance(c, Volcanic) for c in player.equipment]) and against != None:
return False return False
elif against != None: elif against != None:
@ -180,8 +182,8 @@ class Birra(Card):
self.icon = '🍺' self.icon = '🍺'
self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori" self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
if len(player.game.players) != 2 and player.lives != player.max_lives: if len(player.game.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)
return True return True
@ -199,9 +201,10 @@ class CatBalou(Card):
self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco" self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
self.need_target = True self.need_target = True
def play_card(self, player, against): def play_card(self, player, against, _with=None):
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0: if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0:
super().play_card(player, against=against) if self.name == 'Cat Balou':
super().play_card(player, against=against)
from bang.players import PendingAction from bang.players import PendingAction
player.pending_action = PendingAction.CHOOSE player.pending_action = PendingAction.CHOOSE
player.choose_action = 'discard' player.choose_action = 'discard'
@ -217,7 +220,7 @@ class Diligenza(Card):
self.icon = '🚡' self.icon = '🚡'
self.desc = "Pesca 2 carte dalla cima del mazzo" self.desc = "Pesca 2 carte dalla cima del mazzo"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
super().play_card(player, against=against) super().play_card(player, against=against)
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
data=f'{player.name} ha giocato {self.name} e ha pescato 2 carte.') data=f'{player.name} ha giocato {self.name} e ha pescato 2 carte.')
@ -233,7 +236,7 @@ class Duello(Card):
self.icon = '⚔️' self.icon = '⚔️'
self.desc = "Gioca questa carta contro un qualsiasi giocatore. A turno, cominciando dal tuo avversario, potete scartare una carta Bang!, il primo giocatore che non lo fa perde 1 vita" self.desc = "Gioca questa carta contro un qualsiasi giocatore. A turno, cominciando dal tuo avversario, potete scartare una carta Bang!, il primo giocatore che non lo fa perde 1 vita"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
if against != None: if against != None:
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.duel(player, against) player.game.duel(player, against)
@ -247,7 +250,7 @@ class Emporio(Card):
self.icon = '🏪' self.icon = '🏪'
self.desc = "Scopri dal mazzo tante carte quanto il numero di giocatori, a turno, partendo da te, scegliete una carta e aggiungetela alla vostra mano" self.desc = "Scopri dal mazzo tante carte quanto il numero di giocatori, a turno, partendo da te, scegliete una carta e aggiungetela alla vostra mano"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.emporio() player.game.emporio()
return True return True
@ -259,7 +262,7 @@ class Gatling(Card):
self.icon = '🛰' self.icon = '🛰'
self.desc = "Spara a tutti gli altri giocatori" self.desc = "Spara a tutti gli altri giocatori"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.attack_others(player) player.game.attack_others(player)
return True return True
@ -271,7 +274,7 @@ class Indiani(Card):
self.icon = '🏹' self.icon = '🏹'
self.desc = "Tutti gli altri giocatori devono scartare un Bang! o perdere una vita" self.desc = "Tutti gli altri giocatori devono scartare un Bang! o perdere una vita"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.indian_others(player) player.game.indian_others(player)
return True return True
@ -283,7 +286,7 @@ class Mancato(Card):
self.icon = '😅' self.icon = '😅'
self.desc = "Usa questa carta per annullare un bang" self.desc = "Usa questa carta per annullare un bang"
def play_card(self, player, against): 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 (not player.has_played_bang and against != None and isinstance(player.character, chars.CalamityJanet)):
player.sio.emit('chat_message', room=player.game.name, player.sio.emit('chat_message', room=player.game.name,
@ -301,7 +304,7 @@ class Panico(Card):
self.need_target = True self.need_target = True
self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco" self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
def play_card(self, player, against): def play_card(self, player, against, _with=None):
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0: if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0:
super().play_card(player, against=against) super().play_card(player, against=against)
from bang.players import PendingAction from bang.players import PendingAction
@ -319,7 +322,7 @@ 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.icon = '🍻' self.icon = '🍻'
def play_card(self, player, against): 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'{player.name} ha giocato {self.name} e ha curato 1 punto vita a tutti.') data=f'{player.name} ha giocato {self.name} e ha curato 1 punto vita a tutti.')
for p in player.game.players: for p in player.game.players:
@ -334,7 +337,7 @@ class WellsFargo(Card):
self.desc = "Pesca 3 carte dalla cima del mazzo" self.desc = "Pesca 3 carte dalla cima del mazzo"
self.icon = '💸' self.icon = '💸'
def play_card(self, player, against): 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'{player.name} ha giocato {self.name} e ha pescato 3 carte.') data=f'{player.name} ha giocato {self.name} e ha pescato 3 carte.')
for i in range(3): for i in range(3):

View File

@ -16,12 +16,11 @@ 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.desc = "Spara a un giocatore a distanta 1" self.desc = "Spara a un giocatore a distanza 1"
self.need_target = True self.need_target = True
def play_card(self, player, against): def play_card(self, player, against, _with=None):
if against != None: if against != None:
import bang.characters as chars
super().play_card(player, against=against) super().play_card(player, against=against)
player.game.attack(player, against) player.game.attack(player, against)
return True return True
@ -33,14 +32,105 @@ class Schivata(Mancato):
self.name = 'Schivata' self.name = 'Schivata'
self.icon = '🙅‍♂️' self.icon = '🙅‍♂️'
self.desc += " e poi pesca una carta" self.desc += " e poi pesca una carta"
self.alt_text = '☝️🆓'
def play_card(self, player, against): def play_card(self, player, against, _with=None):
return False return False
def use_card(self, player): def use_card(self, player):
player.hand.append(player.game.deck.draw()) player.hand.append(player.game.deck.draw())
player.notify_self() player.notify_self()
class RagTime(Panico):
def __init__(self, suit, number):
Card.__init__(self, suit, 'Rag Time', number)
self.icon = '🎹'
self.desc = "Ruba 1 carta dalla mano di un giocatore a prescindere dalla distanza"
self.need_target = True
self.need_with = True
self.alt_text = '2🃏'
def play_card(self, player, against, _with):
if against != None and _with != None:
player.game.deck.scrap(_with)
super().play_card(player, against=against)
return True
return False
class Rissa(CatBalou):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = 'Rissa'
self.icon = '🥊'
self.desc = "Fai scartare una carta a tutti gli altri giocatori, scegli a caso dalla mano, oppure fra quelle che hanno in gioco"
self.need_with = True
self.need_target = False
self.alt_text = '2🃏'
def play_card(self, player, against, _with):
if _with != None:
player.game.deck.scrap(_with)
player.event_type = 'rissa'
super().play_card(player, against=[p.name for p in player.game.players if p != player and (len(p.hand)+len(p.equipment)) > 0][0])
player.sio.emit('chat_message', room=player.game.name, data=f'{player.name} ha giocato {self.name}')
return True
return False
class SpringField(Card):
def __init__(self, suit, number):
super().__init__(suit, 'Springfield', number)
self.icon = '🌵'
self.desc = "Spara a un giocatore a prescindere dalla distanza"
self.need_target = True
self.need_with = True
self.alt_text = '2🃏'
def play_card(self, player, against, _with=None):
if against != None and _with != None:
player.game.deck.scrap(_with)
super().play_card(player, against=against)
player.game.attack(player, against)
return True
return False
class Tequila(Card):
def __init__(self, suit, number):
super().__init__(suit, 'Tequila', number)
self.icon = '🍹'
self.desc = "Fai recuperare 1 vita a un giocatore a tua scelta, anche te stesso"
self.need_target = True
self.can_target_self = True
self.need_with = True
self.alt_text = '2🃏'
def play_card(self, player, against, _with=None):
if against != None and _with != None:
beneficiario = f'{against}' if against != player.name else 'se stesso'
player.sio.emit('chat_message', room=player.game.name, data=f'{player.name} ha giocato {self.name} per {beneficiario}')
player.game.deck.scrap(_with)
player.game.get_player_named(against).lives = min(player.game.get_player_named(against).lives+1, player.game.get_player_named(against).max_lives)
player.game.get_player_named(against).notify_self()
return True
return False
class Whisky(Card):
def __init__(self, suit, number):
super().__init__(suit, 'Whisky', number)
self.icon = '🥃'
self.desc = "Gioca questa carta per recuperare fino a 2 punti vita"
self.need_with = True
self.alt_text = '2🃏'
def play_card(self, player, against, _with=None):
if _with != None:
super().play_card(player, against=against)
player.game.deck.scrap(_with)
player.lives = min(player.lives+2, player.max_lives)
player.notify_self()
return True
return False
def get_starting_deck() -> List[Card]: def get_starting_deck() -> List[Card]:
return [ return [
#TODO: aggiungere anche le carte normalmente presenti https://bang.dvgiochi.com/cardslist.php?id=3 #TODO: aggiungere anche le carte normalmente presenti https://bang.dvgiochi.com/cardslist.php?id=3
@ -63,6 +153,11 @@ def get_starting_deck() -> List[Card]:
Mancato(Suit.DIAMONDS, 8), Mancato(Suit.DIAMONDS, 8),
Panico(Suit.HEARTS, 'J'), Panico(Suit.HEARTS, 'J'),
Pugno(Suit.SPADES, 10), Pugno(Suit.SPADES, 10),
RagTime(Suit.HEARTS, 9),
Rissa(Suit.SPADES, 'J'),
Schivata(Suit.DIAMONDS, 7), Schivata(Suit.DIAMONDS, 7),
Schivata(Suit.HEARTS, 'K'), Schivata(Suit.HEARTS, 'K'),
SpringField(Suit.SPADES, 'K'),
Tequila(Suit.CLUBS, 9),
Whisky(Suit.HEARTS, 'Q'),
] ]

View File

@ -41,8 +41,11 @@ class Game:
self.notify_room() self.notify_room()
def add_player(self, player: players.Player): def add_player(self, player: players.Player):
if player in self.players or len(self.players) >= 7: if player in self.players or len(self.players) >= 10:
return return
if len(self.players) > 7:
if 'dodge_city' not in self.expansions:
self.expansions.append('dodge_city')
player.join_game(self) player.join_game(self)
self.players.append(player) self.players.append(player)
print(f'Added player {player.name} to game') print(f'Added player {player.name} to game')
@ -61,7 +64,13 @@ class Game:
self.readyCount += 1 self.readyCount += 1
self.notify_room() self.notify_room()
if self.readyCount == len(self.players): if self.readyCount == len(self.players):
self.distribute_roles() for i in range(len(self.players)):
self.sio.emit('chat_message', room=self.name, data=f'{self.players[i].name} ha come personaggio {self.players[i].character.name}, la sua abilità speciale è: {self.players[i].character.desc}')
self.players[i].prepare()
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()
def choose_characters(self): def choose_characters(self):
char_cards = random.sample(characters.all_characters(), len(self.players)*2) char_cards = random.sample(characters.all_characters(), len(self.players)*2)
@ -78,6 +87,7 @@ class Game:
self.started = True self.started = True
self.deck = Deck(self) self.deck = Deck(self)
self.initial_players = len(self.players) self.initial_players = len(self.players)
self.distribute_roles()
self.choose_characters() self.choose_characters()
def distribute_roles(self): def distribute_roles(self):
@ -89,21 +99,16 @@ class Game:
roles.Outlaw('Elimina il Vice 🎖, se non lo elimini tu elimina anche il Rinnegato') roles.Outlaw('Elimina il Vice 🎖, se non lo elimini tu elimina anche il Rinnegato')
] ]
elif len(self.players) >= 4: elif len(self.players) >= 4:
available_roles = [roles.Sheriff(), roles.Renegade(), roles.Outlaw(), roles.Outlaw(), roles.Vice(), roles.Outlaw(), roles.Vice()] 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)] available_roles = available_roles[:len(self.players)]
random.shuffle(available_roles) random.shuffle(available_roles)
for i in range(len(self.players)): for i in range(len(self.players)):
self.sio.emit('chat_message', room=self.name, data=f'{self.players[i].name} ha come personaggio {self.players[i].character.name}, la sua abilità speciale è: {self.players[i].character.desc}')
self.players[i].set_role(available_roles[i]) self.players[i].set_role(available_roles[i])
if isinstance(available_roles[i], roles.Sheriff) or (len(available_roles) == 3 and isinstance(available_roles[i], roles.Vice)): if isinstance(available_roles[i], roles.Sheriff) or (len(available_roles) == 3 and isinstance(available_roles[i], roles.Vice)):
if isinstance(available_roles[i], roles.Sheriff): if isinstance(available_roles[i], roles.Sheriff):
self.sio.emit('chat_message', room=self.name, data=f'{self.players[i].name} È lo sceriffo') self.sio.emit('chat_message', room=self.name, data=f'{self.players[i].name} È lo sceriffo')
self.turn = i self.turn = i
self.players[i].prepare()
for k in range(self.players[i].max_lives):
self.players[i].hand.append(self.deck.draw())
self.players[i].notify_self() self.players[i].notify_self()
self.play_turn()
def attack_others(self, attacker: players.Player): def attack_others(self, attacker: players.Player):
attacker.pending_action = players.PendingAction.WAIT attacker.pending_action = players.PendingAction.WAIT

View File

@ -203,6 +203,7 @@ class Player:
else: else:
self.game.next_player().equipment.append(self.equipment.pop(i)) self.game.next_player().equipment.append(self.equipment.pop(i))
self.game.next_player().notify_self() self.game.next_player().notify_self()
break
if any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]): if any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]):
self.notify_self() self.notify_self()
return return
@ -218,7 +219,7 @@ class Player:
self.game.deck.scrap(self.equipment.pop(i)) self.game.deck.scrap(self.equipment.pop(i))
self.end_turn(forced=True) self.end_turn(forced=True)
return return
else: elif pickable_cards == 0:
self.game.deck.scrap(self.equipment.pop(i)) self.game.deck.scrap(self.equipment.pop(i))
break break
break break
@ -248,18 +249,23 @@ class Player:
s += f"equipment {[str(c) for c in self.equipment]}" s += f"equipment {[str(c) for c in self.equipment]}"
return s return s
def play_card(self, hand_index: int, against=None): def play_card(self, hand_index: int, against=None, _with=None):
if not (0 <= hand_index < len(self.hand)): if not (0 <= hand_index < len(self.hand)):
print('illegal') print('illegal')
return return
card: cs.Card = self.hand.pop(hand_index) card: cs.Card = self.hand.pop(hand_index)
print(self.name, 'is playing ', card, ' against:', against) withCard: cs.Card = None
did_play_card = card.play_card(self, against) if _with != None:
withCard = self.hand.pop(_with) if hand_index > _with else self.hand.pop(_with - 1)
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
did_play_card = card.play_card(self, against, withCard)
if not card.is_equipment: if not card.is_equipment:
if did_play_card: if did_play_card:
self.game.deck.scrap(card) self.game.deck.scrap(card)
else: else:
self.hand.insert(hand_index, card) self.hand.insert(hand_index, card)
if withCard:
self.hand.insert(_with, withCard)
self.notify_self() self.notify_self()
def choose(self, card_index): def choose(self, card_index):
@ -277,9 +283,16 @@ class Player:
self.hand.append(card) self.hand.append(card)
else: else:
self.game.deck.scrap(card) self.game.deck.scrap(card)
self.target_p = '' 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]):
self.choose_action = '' self.event_type = ''
self.pending_action = PendingAction.PLAY self.target_p = ''
self.choose_action = ''
self.pending_action = PendingAction.PLAY
else:
while len(self.game.players[self.game.players_map[self.target_p]+1].hand) + len(self.game.players[self.game.players_map[self.target_p]+1].equipment) == 0:
self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name
if self.target_p == self.name:
self.target_p = self.game.players[self.game.players_map[self.target_p]+1].name
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 isinstance(self.character, chars.KitCarlson):

View File

@ -75,7 +75,7 @@ class Renegade(Role):
return True return True
elif initial_players == 3 and attacker_role != None: elif initial_players == 3 and attacker_role != None:
return isinstance(dead_role, Outlaw) and isinstance(attacker_role, Renegade) return isinstance(dead_role, Outlaw) and isinstance(attacker_role, Renegade)
elif initial_players != 3 and len(alive_players) == 1 and isinstance(alive_players[0].role, Renegade): elif initial_players != 3 and len(alive_players) == 1 and alive_players[0].role == self:
print("The Renegade won!") print("The Renegade won!")
return True return True
return False return False

View File

@ -12,6 +12,7 @@
"pretty-checkbox-vue": "^1.1.9", "pretty-checkbox-vue": "^1.1.9",
"socket.io-client": "^3.0.3", "socket.io-client": "^3.0.3",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-i18n": "^8.22.2",
"vue-socket.io": "^3.0.10" "vue-socket.io": "^3.0.10"
}, },
"devDependencies": { "devDependencies": {

View File

@ -2,29 +2,29 @@
<div id="app" class="dark-mode"> <div id="app" class="dark-mode">
<div v-if="!isInLobby" id="logo" class="center-stuff" style="margin-bottom:10pt;"> <div v-if="!isInLobby" id="logo" class="center-stuff" style="margin-bottom:10pt;">
<h1 style="margin-bottom:0pt;">PewPew!</h1> <h1 style="margin-bottom:0pt;">PewPew!</h1>
<i style="font-size: x-small;">Bang! è un marchio registrato DVGiochi</i> <i style="font-size: x-small;">{{$t("trademark")}}</i>
</div> </div>
<div v-if="isConnected"> <div v-if="isConnected">
<div v-if="!didSetUsername"> <div v-if="!didSetUsername">
<p>Scegli un username:</p> <p>{{$t("choose_username")}}</p>
<form @submit="setUsername"> <form @submit="setUsername">
<input v-model="username" /> <input v-model="username" />
<input type="submit"/> <input type="submit"/>
</form> </form>
<p>Giocatori online: {{onlinePlayers}}</p> <p>{{$t("online_players")}}{{onlinePlayers}}</p>
</div> </div>
<div v-else> <div v-else>
<div v-if="!isInLobby" > <div v-if="!isInLobby" >
<p>Giocatori online: {{onlinePlayers}}</p> <p>{{$t("online_players")}}{{onlinePlayers}}</p>
<Card :card="getSelfCard" style="position:absolute; bottom:10pt; right: 10pt;"/> <Card :card="getSelfCard" style="position:absolute; bottom:10pt; right: 10pt;"/>
<h2>Lobby disponibili:</h2> <h2>{{$t("available_lobbies")}}</h2>
<div style="display: flex"> <div style="display: flex">
<Card v-for="lobby in openLobbies" v-bind:key="lobby.name" :card="getLobbyCard(lobby)" @click.native="joinLobby(lobby)"/> <Card v-for="lobby in openLobbies" v-bind:key="lobby.name" :card="getLobbyCard(lobby)" @click.native="joinLobby(lobby)"/>
<p v-if="noLobbyAvailable">Nessuna lobby disponibile</p> <p v-if="noLobbyAvailable">{{$t("no_lobby_available")}}</p>
</div> </div>
<form @submit="createLobby"> <form @submit="createLobby">
<h2>Crea una lobby:</h2> <h2>{{$t("create_lobby")}}</h2>
<p>Nome:</p> <p>{{$t("lobby_name")}}</p>
<input v-model="lobbyName"/> <input v-model="lobbyName"/>
<input type="submit" /> <input type="submit" />
</form> </form>
@ -33,9 +33,17 @@
</div> </div>
</div> </div>
<div v-else class="center-stuff"> <div v-else class="center-stuff">
<h2>Attenzione!</h2> <h2>{{$t("warning")}}</h2>
<p>Connessione al server assente.</p> <p>{{$t("connection_error")}}</p>
</div> </div>
<select style="position:absolute;bottom:4pt;right:4pt;" v-model="$i18n.locale">
<option
v-for="(lang, i) in ['it.🇮🇹', 'en.🇬🇧']"
:key="`lang-${i}`"
:value="lang.split('.')[0]">
{{lang.split('.')[1]}} {{lang.split('.')[0]}}
</option>
</select>
</div> </div>
</template> </template>
@ -66,7 +74,7 @@ export default {
getSelfCard() { getSelfCard() {
return { return {
name: this.username, name: this.username,
number: 'YOU', number: this.$t('you'),
icon: '🤠', icon: '🤠',
is_character: true, is_character: true,
} }
@ -120,7 +128,7 @@ export default {
e.preventDefault(); e.preventDefault();
}, },
joinLobby(lobby) { joinLobby(lobby) {
let password = lobby.locked ? prompt("Room password:", "") : ''; let password = lobby.locked ? prompt(this.$t("room_password_prompt"), "") : '';
this.$socket.emit('join_room', {name:lobby.name,password:password}) this.$socket.emit('join_room', {name:lobby.name,password:password})
}, },
init() { init() {
@ -217,7 +225,7 @@ h1,h2,h3,h4,p,span,b,label{
transform: scale(0); transform: scale(0);
} }
} }
input { input, select {
border: 2px solid; border: 2px solid;
border-radius: 4px; border-radius: 4px;
font-size: 1rem; font-size: 1rem;
@ -227,7 +235,7 @@ input {
transition: border-color 0.5s ease-out; transition: border-color 0.5s ease-out;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root, #app, input { :root, #app, input, select {
background-color: #181a1b; background-color: #181a1b;
color: rgb(174, 194, 211); color: rgb(174, 194, 211);
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="chat"> <div class="chat">
<h3>Chat</h3> <h3>{{$t("chat")}}</h3>
<div id="chatbox"> <div id="chatbox">
<p style="margin:1pt;" class="chat-message" v-for="msg in messages" v-bind:key="msg">{{msg}}</p> <p style="margin:1pt;" class="chat-message" v-for="msg in messages" v-bind:key="msg">{{msg}}</p>
<p class="end">.</p> <p class="end">.</p>

View File

@ -5,7 +5,7 @@
<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 in cards" v-bind:key="c" :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>{{cancelText}}</span></div> <div style="margin-top:6pt;" class="button center-stuff" v-if="showCancelBtn" @click="cancel"><span>{{realCancelText}}</span></div>
<p v-if="desc" style="bottom:10pt;right:0;left:0;position:absolute;margin:16pt;font-size:18pt">{{desc}}</p> <p v-if="desc" style="bottom:10pt;right:0;left:0;position:absolute;margin:16pt;font-size:18pt">{{desc}}</p>
</div> </div>
</template> </template>
@ -24,13 +24,14 @@ export default {
cancel: Function, cancel: Function,
cancelText: { cancelText: {
type: String, type: String,
default: 'ANNULLA', default: '',
}, },
text: String, text: String,
hintText: String, hintText: String,
}, },
data: () => ({ data: () => ({
desc: '' desc: '',
realCancelText: ''
}), }),
computed: { computed: {
showCancelBtn() { showCancelBtn() {
@ -43,7 +44,13 @@ export default {
showDesc(card) { showDesc(card) {
this.desc = card.desc this.desc = card.desc
} }
} },
mounted() {
this.realCancelText = this.cancelText
if (this.realCancelText == '') {
this.realCancelText = this.$t('cancel')
}
},
} }
</script> </script>

View File

@ -1,16 +1,22 @@
<template> <template>
<div class="deck"> <div>
<card v-if="endTurnAction && isPlaying" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/> <div class="deck">
<div style="position:relative"> <card v-if="endTurnAction && isPlaying" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/>
<div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/> <div style="position:relative">
<div class="card back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/> <div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/>
<card :card="card" :class="{back:true, pick:pending_action === 0, draw:pending_action === 1}" @click.native="action"/> <div class="card back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
</div> <card :card="card" :class="{back:true, pick:pending_action === 0, draw:pending_action === 1}" @click.native="action"/>
<div style="position:relative;"> </div>
<card v-if="previousScrap" :card="previousScrap"/> <div style="position:relative;">
<card v-else :card="card" class="back" style="opacity:0"/> <card v-if="previousScrap" :card="previousScrap"/>
<card v-if="lastScrap" :card="lastScrap" :key="lastScrap" class="last-scrap" @click.native="action('scrap')"/> <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')"
@pointerenter.native="desc=lastScrap.desc" @pointerleave.native="desc=''" />
</div>
</div> </div>
<transition name="list">
<p v-if="desc" class="center-stuff"><i>{{desc}}</i></p>
</transition>
</div> </div>
</template> </template>
@ -30,14 +36,12 @@ export default {
name: 'PewPew!', name: 'PewPew!',
icon: '💥', icon: '💥',
}, },
endTurnCard: { endTurnCard: null,
name: 'Termina turno!',
icon: '⛔️'
},
lastScrap: null, lastScrap: null,
previousScrap: null, previousScrap: null,
pending_action: false, pending_action: false,
isPlaying: true, isPlaying: true,
desc: '',
}), }),
sockets: { sockets: {
self(self){ self(self){
@ -49,6 +53,12 @@ export default {
this.lastScrap = card this.lastScrap = card
} }
}, },
mounted() {
this.endTurnCard = {
name: this.$t('end_turn'),
icon: '⛔️'
}
},
methods: { methods: {
action(pile) { action(pile) {
if (this.pending_action !== false && this.pending_action < 2) { if (this.pending_action !== false && this.pending_action < 2) {

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="lobby"> <div class="lobby">
<div style="flex-grow: 4;"> <div style="flex-grow: 4;">
<h2 v-if="!started">Lobby: {{ lobbyName }}</h2> <h2 v-if="!started">{{$t('room')}}{{ lobbyName }}</h2>
<h3>Giocatori (tu sei {{username}})</h3> <h3>{{$t('room_players', {username:username})}}</h3>
<div v-if="!started"> <div v-if="!started">
<PrettyCheck v-if="isRoomOwner" class="p-switch p-fill" v-model="privateRoom" style="margin-top:5px; margin-bottom:3px;">Stanza Privata</PrettyCheck> <PrettyCheck v-if="isRoomOwner" class="p-switch p-fill" v-model="privateRoom" style="margin-top:5px; margin-bottom:3px;">{{$t("private_room")}}</PrettyCheck>
<label v-if="password !== ''">Password: <b class="selectable" style="font-size:larger;">{{ password }}</b></label> <label v-if="password !== ''">{{$t('password')}}<b class="selectable" style="font-size:larger;">{{ password }}</b></label>
</div> </div>
<div class="players-table"> <div class="players-table">
@ -28,7 +28,7 @@
<!-- </div> --> <!-- </div> -->
</div> </div>
<div v-if="!started"> <div v-if="!started">
<h3>Espansioni (NON COMPLETE)</h3> <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> <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> </div>
<div v-if="started"> <div v-if="started">
@ -37,10 +37,9 @@
</div> </div>
</div> </div>
<chat/> <chat/>
<Chooser v-if="selectedInfo" text="Dettagli" :cards="selectedInfo" cancelText="OK" :cancel="()=>{selectedInfo = null}" :select="()=>{selectedInfo = null}"/> <Chooser v-if="selectedInfo" :text="$t('details')" :cards="selectedInfo" :cancelText="$t('ok')" :cancel="()=>{selectedInfo = null}" :select="()=>{selectedInfo = null}"/>
<transition name="bounce"> <transition name="bounce">
<Chooser v-if="showChooser" text="Scegli il tuo personaggio" :cards="availableCharacters" :select="setCharacter"/> <Chooser v-if="hasToChoose" :text="`${$t('choose_card')}${target_p?$t('choose_card_from') + target_p:''}`" :cards="chooseCards" :select="chooseCard"/>
<Chooser v-if="hasToChoose" text="Scegli una carta" :cards="chooseCards" :select="chooseCard"/>
</transition> </transition>
</div> </div>
</template> </template>
@ -74,9 +73,9 @@ export default {
players: [], players: [],
messages: [], messages: [],
distances: {}, distances: {},
availableCharacters: [],
self: {}, self: {},
hasToChoose: false, hasToChoose: false,
target_p: '',
chooseCards: [], chooseCards: [],
wantsToEndTurn: false, wantsToEndTurn: false,
selectedInfo: null, selectedInfo: null,
@ -98,9 +97,6 @@ export default {
} }
}) })
}, },
characters(data) {
this.availableCharacters = JSON.parse(data)
},
start() { start() {
this.started = true; this.started = true;
}, },
@ -121,7 +117,7 @@ export default {
startGameCard() { startGameCard() {
if (!this.started && this.players.length > 2 && this.isRoomOwner) { if (!this.started && this.players.length > 2 && this.isRoomOwner) {
return { return {
name: 'Start', name: this.$t('start_game'),
icon: '▶️', icon: '▶️',
is_equipment: true, is_equipment: true,
number: `${this.players.length}🤠` number: `${this.players.length}🤠`
@ -129,9 +125,6 @@ export default {
} }
return null; return null;
}, },
showChooser() {
return this.availableCharacters.length > 0;
},
playersTable() { playersTable() {
console.log('update players') console.log('update players')
return this.players.map((x,i) => { return this.players.map((x,i) => {
@ -164,7 +157,7 @@ export default {
getPlayerCard(player) { getPlayerCard(player) {
return { return {
name: player.name, name: player.name,
number: ((this.username == player.name) ? 'YOU' : (this.players[0].name == player.name) ? 'OWNER' :'') + (player.dist ? `${player.dist}` : ''), number: ((this.username == player.name) ? this.$t('you') : (this.players[0].name == player.name) ? this.$t('owner') :'') + (player.dist ? `${player.dist}` : ''),
icon: (player.lives === undefined || player.lives > 0) ? (player.is_sheriff ? '⭐' : player.icon || ((player.ready)?'👍': '🤠') ) : '☠️', icon: (player.lives === undefined || player.lives > 0) ? (player.is_sheriff ? '⭐' : player.icon || ((player.ready)?'👍': '🤠') ) : '☠️',
is_character: true, is_character: true,
} }
@ -173,12 +166,9 @@ export default {
this.started = true; this.started = true;
this.$socket.emit('start_game') this.$socket.emit('start_game')
}, },
setCharacter(char) {
this.availableCharacters = []
this.$socket.emit('set_character', char.name)
},
choose(player_name) { choose(player_name) {
console.log('choose from' + player_name) console.log('choose from' + player_name)
this.target_p = player_name
let pl = this.players.filter(x=>x.name === player_name)[0] let pl = this.players.filter(x=>x.name === player_name)[0]
console.log(pl) console.log(pl)
let arr = [] let arr = []
@ -197,6 +187,7 @@ export default {
console.log(card + ' ' + this.chooseCards.indexOf(card)) console.log(card + ' ' + this.chooseCards.indexOf(card))
this.chooseCards = [] this.chooseCards = []
this.hasToChoose = false this.hasToChoose = false
this.target_p = ''
}, },
drawFromPlayer(name) { drawFromPlayer(name) {
console.log(name) console.log(name)

View File

@ -7,37 +7,43 @@
@pointerenter.native="desc=my_role.goal" @pointerleave.native="desc=''"/> @pointerenter.native="desc=my_role.goal" @pointerleave.native="desc=''"/>
<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=character.desc" @pointerleave.native="desc=''"/> @pointerenter.native="desc=character.desc" @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;">
<span v-for="(n, i) in lives" v-bind:key="n" :alt="i"></span>
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="n" :alt="i">💀</span>
</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" 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" @pointerenter.native="desc=card.desc" @pointerleave.native="desc=''" /> <Card v-for="card in equipment" v-bind:key="card.name+card.number" :card="card" @pointerenter.native="desc=card.desc" @pointerleave.native="desc=''" />
</transition-group> </transition-group>
</div> </div>
<p v-if="desc">{{desc}}</p> <transition name="list">
<transition-group name="list" tag="div" style="display: flex; justify-content: space-evenly; margin-bottom:2pt;"> <p v-if="desc"><i>{{desc}}</i></p>
<span v-for="(n, i) in lives" v-bind:key="n" :alt="i"></span> </transition>
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="n" :alt="i">💀</span>
</transition-group>
<div v-if="lives > 0"> <div v-if="lives > 0">
<span>Mano</span> <span>{{$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 hand" v-bind:key="card.name+card.number" :card="card"
@click.native="play_card(card)" @click.native="play_card(card)"
@pointerenter.native="hint=card.desc" @pointerleave.native="hint=''"/> @pointerenter.native="hint=card.desc" @pointerleave.native="hint=''"/>
</transition-group> </transition-group>
</div> </div>
<p>{{hint}}</p> <transition name="list">
<Chooser v-if="card_against" text="Contro chi vuoi giocare la carta" :cards="visiblePlayers" :select="selectAgainst" :cancel="cancelCardAgainst"/> <p v-if="hint"><i>{{hint}}</i></p>
</transition>
<Chooser v-if="card_against" :text="$t('card_against')" :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="Scegli che carta pescare" :cards="available_cards" :select="choose"/> <Chooser v-if="shouldChooseCard" :text="$t('choose_card_to_get')" :cards="available_cards" :select="choose"/>
<Chooser v-if="lives <= 0 && max_lives > 0" text="SEI MORTO" cancelText="SPETTATORE" :cancel="()=>{max_lives = 0}"/> <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?'HAI VINTO':'HAI PERSO'" /> <Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" />
<Chooser v-if="show_role" text="Tu sei" :cards="[my_role]" :hintText="my_role.goal" :select="() => {show_role=false}" :cancel="() => {show_role=false}" cancelText="OK" /> <Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="my_role.goal" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')" />
<Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ha pescato come seconda carta:`" :cards="[notifycard.card]" hintText="Se la carta è cuori o quadri ne pesca un'altra" class="turn-notify-4s"/> <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="!show_role && is_my_turn" text="GIOCA IL TUO TURNO" :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="hasToPickResponse" :text="`ESTRAI UNA CARTA ${attacker?('PER DIFENDERTI DA '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" /> <Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/>
<Chooser v-if="showScrapScreen" :text="`SCARTA ${hand.length}/${lives}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/> <Chooser v-if="hasToPickResponse" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" />
<Chooser v-if="sidWantsScrapForHealth && sidScrapForHealth.length < 2" :text="`SCARTA ${2 - sidScrapForHealth.length} PER RECUPERARE 1 VITA`" <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=[]}"/> :cards="sidScrapHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;sidScrapForHealth=[]}"/>
<button v-if="is_my_turn && character.name === 'Sid Ketchum'" @click="sidWantsScrapForHealth=true">ABILITÀ SPECIALE</button> <button v-if="is_my_turn && character.name === 'Sid Ketchum'" @click="sidWantsScrapForHealth=true">{{$t('special_ability')}}</button>
</div> </div>
</template> </template>
@ -59,6 +65,7 @@ export default {
data: () => ({ data: () => ({
my_role: null, my_role: null,
character: null, character: null,
availableCharacters: [],
equipment: [], equipment: [],
hand: [], hand: [],
lives: 0, lives: 0,
@ -66,6 +73,7 @@ export default {
hint: '', hint: '',
pending_action: null, pending_action: null,
card_against: null, card_against: null,
card_with: null,
has_played_bang: false, has_played_bang: false,
playersDistances: [], playersDistances: [],
is_my_turn: false, is_my_turn: false,
@ -83,6 +91,7 @@ export default {
sidScrapForHealth: [], sidScrapForHealth: [],
sidWantsScrapForHealth: false, sidWantsScrapForHealth: false,
mancato_needed: 0, mancato_needed: 0,
name: '',
}), }),
sockets: { sockets: {
role(role) { role(role) {
@ -90,8 +99,12 @@ export default {
this.my_role.is_back = true this.my_role.is_back = true
this.show_role = true this.show_role = true
}, },
characters(data) {
this.availableCharacters = JSON.parse(data)
},
self(self) { self(self) {
self = JSON.parse(self) self = JSON.parse(self)
this.name = self.name
this.pending_action = self.pending_action this.pending_action = self.pending_action
this.character = self.character this.character = self.character
this.character.is_character = true this.character.is_character = true
@ -122,11 +135,14 @@ export default {
}, },
notify_card(mess) { notify_card(mess) {
this.notifycard = mess this.notifycard = mess
setTimeout(function(){
this.notifycard = null
}.bind(this), 4000)
} }
}, },
computed:{ computed:{
respondText() { respondText() {
return `Scegli come rispondere ${this.attacker?('a '+this.attacker):''}${(this.mancato_needed>1)?(' (NE OCCORRONO ' + this.mancato_needed + ')'):''}` return `${this.$t('choose_response')}${this.attacker?(this.$t('choose_response_to')+this.attacker):''}${(this.mancato_needed>1)?(` (${this.$t('choose_response_needed')} ` + this.mancato_needed + ')'):''}`
}, },
showScrapScreen() { showScrapScreen() {
return this.isEndingTurn && !this.canEndTurn && this.is_my_turn; return this.isEndingTurn && !this.canEndTurn && this.is_my_turn;
@ -136,7 +152,7 @@ export default {
}, },
visiblePlayers() { visiblePlayers() {
this.range; this.range;
return this.playersDistances.filter(x => { let vis = this.playersDistances.filter(x => {
if (!this.can_target_sheriff && x.is_sheriff) if (!this.can_target_sheriff && x.is_sheriff)
return false return false
else else
@ -148,6 +164,15 @@ export default {
icon: player.is_sheriff ? '⭐' : '🤠', icon: player.is_sheriff ? '⭐' : '🤠',
is_character: true, is_character: true,
}}) }})
if (this.card_against && this.card_against.can_target_self) {
vis.push({
name: this.name,
number: 0,
icon: this.$t('you'),
is_character: true,
})
}
return vis
}, },
hasToPickResponse() { hasToPickResponse() {
return !this.is_my_turn && this.pending_action == 0 return !this.is_my_turn && this.pending_action == 0
@ -155,7 +180,7 @@ export default {
instruction() { instruction() {
if (this.pending_action == null) if (this.pending_action == null)
return '' return ''
let x = ['▶️ Estrai una carta', '▶️ Pesca le tue carte', '▶️ Gioca le tue carte', '▶️ Rispondi alla carta', '⏸ Attendi', '▶️ Scegli una carta'] let x = [this.$t('flip_card'), this.$t('draw_cards'), this.$t('play_cards'), this.$t('respond_card'), this.$t('wait'), this.$t('choose_cards')]
return x[this.pending_action] return x[this.pending_action]
}, },
canEndTurn() { canEndTurn() {
@ -163,7 +188,7 @@ export default {
}, },
respondCards() { respondCards() {
let cc = [{ let cc = [{
name: 'Prendi Danno', name: this.$t('take_dmg'),
icon: '❌', icon: '❌',
is_equipment: true, is_equipment: true,
}] }]
@ -174,6 +199,10 @@ export default {
} }
}, },
methods: { methods: {
setCharacter(char) {
this.availableCharacters = []
this.$socket.emit('set_character', char.name)
},
sidScrap(c) { sidScrap(c) {
this.sidScrapForHealth.push(this.hand.indexOf(c)) this.sidScrapForHealth.push(this.hand.indexOf(c))
if (this.sidScrapForHealth.length == 2) { if (this.sidScrapForHealth.length == 2) {
@ -195,7 +224,9 @@ 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) {
if ((card.need_target || calamity_special) && !((card.name == 'Bang!' || (calamity_special && card.name=='Mancato!')) && cant_play_bang)) { if (card.need_with && !this.card_with) {
this.card_with = card
} else if ((card.need_target || calamity_special) && !((card.name == 'Bang!' || (calamity_special && card.name=='Mancato!')) && cant_play_bang)) {
if (card.name == 'Bang!' || calamity_special) if (card.name == 'Bang!' || calamity_special)
this.range = this.sight this.range = this.sight
else else
@ -217,14 +248,32 @@ export default {
this.really_play_card(this.card_against, player.name) this.really_play_card(this.card_against, player.name)
this.card_against = null this.card_against = null
}, },
selectWith(card) {
if (this.card_with.need_target) {
this.card_against = this.card_with
this.range = this.card_against.range
this.card_with = card
} else {
let card_data = {
index: this.hand.indexOf(this.card_with),
against: null,
with: this.hand.indexOf(card),
}
this.card_with = null
this.$socket.emit('play_card', card_data)
}
},
cancelCardAgainst() { cancelCardAgainst() {
this.card_against = null this.card_against = null
this.card_with = null
}, },
really_play_card(card, against) { really_play_card(card, against) {
let card_data = { let card_data = {
index: this.hand.indexOf(card), index: this.hand.indexOf(card),
against: against against: against,
with: this.hand.indexOf(this.card_with) > -1 ? this.hand.indexOf(this.card_with):null,
} }
this.card_with = null
console.log(card_data) console.log(card_data)
this.$socket.emit('play_card', card_data) this.$socket.emit('play_card', card_data)
}, },

55
frontend/src/i18n/en.json Normal file
View File

@ -0,0 +1,55 @@
{
"trademark": "Bang! is a trademark owned by DVGiochi",
"online_players": "Online players: ",
"choose_username": "Pick an username:",
"available_lobbies": "Available Lobbies:",
"no_lobby_available": "No lobbies available",
"create_lobby": "Open a lobby:",
"lobby_name": "Name:",
"warning": "Warning!",
"connection_error": "Cannot connect to server.",
"chat": "Chat",
"end_turn": "End Turn!",
"start_game": "Start!",
"expansions": "Expansions",
"details": "Details",
"ok": "OK",
"you": "YOU",
"owner": "OWNER",
"cancel": "CANCEL",
"password": "Password: ",
"room_password_prompt": "Lobby Password: ",
"private_room": "Private Lobby",
"room": "Lobby: ",
"room_players": "Players (you are {username})",
"choose_character": "Choose your character",
"choose_card": "Choose a card",
"choose_card_from": " from ",
"flip_card": "▶️ Flip a card",
"draw_cards": "▶️ Draw you cards from the deck",
"play_cards": "▶️ Play your cards",
"respond_card":"▶️ Respond to the card",
"wait": "⏸ Wait",
"choose_cards": "▶️ Choose a card",
"take_dmg": "Take damage",
"choose_response": "Choose your response ",
"choose_response_to": "to ",
"choose_response_needed": "NEEDED ",
"hand": "HAND",
"card_against": "Who will you play your card against?",
"choose_card_to_get": "Choose a card",
"you_died":"YOU DIED",
"spectate":"SPECTATE",
"you_win":"YOU WON",
"you_lose":"YOU LOST",
"special_ability": "SPECIAL ABILITY",
"discard": "DISCARD",
"to_regain_1_hp": "TO REGAIN 1 HP",
"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.",
"choose_scarp_card_to": "CHOOSE WHICH CARD TO DISCARD TO USE",
"pick_a_card": "FLIP A CARD",
"to_defend_from": "TO DEFEND YOURSELF FROM"
}

View File

@ -0,0 +1,9 @@
import it from './it.json'
import en from './en.json'
export const defaultLocale = 'it'
export const languages = {
it: it,
en: en,
}

55
frontend/src/i18n/it.json Normal file
View File

@ -0,0 +1,55 @@
{
"trademark": "Bang! è un marchio registrato DVGiochi",
"online_players": "Giocatori online: ",
"choose_username": "Scegli un username:",
"available_lobbies": "Stanze disponibili:",
"no_lobby_available": "Nessuna stanza disponibile",
"create_lobby": "Crea una stanza:",
"lobby_name": "Nome:",
"warning": "Attenzione!",
"connection_error": "Connessione al server assente.",
"chat": "Chat",
"end_turn": "Termina turno!",
"start_game": "Avvia!",
"expansions": "Espansioni",
"details": "Dettagli",
"ok": "OK",
"you": "TU",
"owner": "GESTORE",
"cancel": "ANNULLA",
"password": "Password: ",
"room_password_prompt": "Password Stanza: ",
"private_room": "Stanza Privata",
"room": "Stanza: ",
"room_players": "Giocatori (tu sei {username})",
"choose_character": "Scegli il tuo personaggio",
"choose_card": "Scegli una carta",
"choose_card_from": " da ",
"flip_card": "▶️ Estrai una carta",
"draw_cards": "▶️ Pesca le tue carte",
"play_cards": "▶️ Gioca le tue carte",
"respond_card": "▶️ Rispondi alla carta",
"wait": "⏸ Attendi",
"choose_cards": "▶️ Scegli una carta",
"take_dmg": "Prendi Danno",
"choose_response": "Scegli come rispondere ",
"choose_response_to": "a ",
"choose_response_needed": "NE OCCORRONO ",
"hand": "MANO",
"card_against": "Contro chi vuoi giocare la carta",
"choose_card_to_get": "Scegli che carta pescare",
"you_died": "SEI MORTO",
"spectate": "SPETTATORE",
"you_win": "HAI VINTO",
"you_lose": "HAI PERSO",
"special_ability": "ABILITÀ SPECIALE",
"discard": "SCARTA",
"to_regain_1_hp": "PER RECUPERARE 1 VITA",
"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",
"choose_scarp_card_to": "SCEGLI CHE CARTA SCARTARE PER USARE",
"pick_a_card": "ESTRAI UNA CARTA",
"to_defend_from": "PER DIFENDERTI DA"
}

View File

@ -11,6 +11,18 @@ Vue.use(new VueSocketIO({
import PrettyCheckbox from 'pretty-checkbox-vue'; import PrettyCheckbox from 'pretty-checkbox-vue';
Vue.use(PrettyCheckbox) Vue.use(PrettyCheckbox)
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
import { languages, defaultLocale } from './i18n';
const messages = Object.assign(languages)
const i18n = new VueI18n({
locale: defaultLocale,
messages
})
new Vue({ new Vue({
i18n,
render: h => h(App), render: h => h(App),
}).$mount('#app') }).$mount('#app')

View File

@ -8310,6 +8310,11 @@ vue-hot-reload-api@^2.3.0:
resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI= integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=
vue-i18n@^8.22.2:
version "8.22.2"
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.22.2.tgz#58299a5a050e67b4f799d96fee7dd8bd269e0907"
integrity sha512-rb569fVJInPUgS/bbCxEQ9DrAoFTntuJvYoK4Fpk2VfNbA09WzdTKk57ppjz3S+ps9hW+p9H+2ASgMvojedkow==
"vue-loader-v16@npm:vue-loader@^16.0.0-beta.7": "vue-loader-v16@npm:vue-loader@^16.0.0-beta.7":
version "16.0.0-rc.2" version "16.0.0-rc.2"
resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.0.0-rc.2.tgz?cache=0&sync_timestamp=1605670716799&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.0.0-rc.2.tgz#b6a7e7f30d28f35659a83de41f4a1831a4232a04" resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.0.0-rc.2.tgz?cache=0&sync_timestamp=1605670716799&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.0.0-rc.2.tgz#b6a7e7f30d28f35659a83de41f4a1831a4232a04"