diff --git a/.gitignore b/.gitignore index b43fa6c..6e83984 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,4 @@ dmypy.json cython_debug/ frontend/package-lock.json +bang-workspace.code-workspace diff --git a/backend/__init__.py b/backend/__init__.py index 6dfda89..db07c6e 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -19,7 +19,7 @@ games: List[Game] = [] online_players = 0 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 def connect(sid, environ): @@ -90,58 +90,58 @@ def join_room(sid, room): @sio.event 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.event def start_game(sid): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.game.start_game() advertise_lobbies() @sio.event def set_character(sid, name): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.set_character(name) @sio.event def refresh(sid): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.notify_self() @sio.event def draw(sid, pile): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.draw(pile) @sio.event def pick(sid): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.pick() @sio.event def end_turn(sid): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.end_turn() @sio.event def play_card(sid, data): - ses = sio.get_session(sid) - ses.play_card(data['index'], data['against']) + ses: Player = sio.get_session(sid) + ses.play_card(data['index'], data['against'], data['with']) @sio.event def respond(sid, data): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.respond(data) @sio.event def choose(sid, card_index): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.choose(card_index) @sio.event def scrap(sid, card_index): - ses = sio.get_session(sid) + ses: Player = sio.get_session(sid) ses.scrap(card_index) if __name__ == '__main__': diff --git a/backend/bang/cards.py b/backend/bang/cards.py index c10f400..7cce19e 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -35,13 +35,15 @@ class Card(ABC): self.alt_text = f'{self.range} 🔍' self.desc = desc self.need_target = False + self.can_target_self = False + self.need_with = False def __str__(self): char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)] return f'{self.name} {char}{self.number}' 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_weapon: 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.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): 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).notify_self() return False @@ -158,10 +160,10 @@ class Bang(Card): def __init__(self, suit, number): super().__init__(suit, 'Bang!', number) 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 - 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: return False elif against != None: @@ -180,8 +182,8 @@ class Birra(Card): 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" - def play_card(self, player, against): - if len(player.game.players) != 2 and player.lives != player.max_lives: + def play_card(self, player, against, _with=None): + if len(player.game.players) != 2: super().play_card(player, against=against) player.lives = min(player.lives+1, player.max_lives) 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.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: - super().play_card(player, against=against) + if self.name == 'Cat Balou': + super().play_card(player, against=against) from bang.players import PendingAction player.pending_action = PendingAction.CHOOSE player.choose_action = 'discard' @@ -217,7 +220,7 @@ class Diligenza(Card): self.icon = '🚡' 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) player.sio.emit('chat_message', room=player.game.name, data=f'{player.name} ha giocato {self.name} e ha pescato 2 carte.') @@ -233,7 +236,7 @@ class Duello(Card): 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" - def play_card(self, player, against): + def play_card(self, player, against, _with=None): if against != None: super().play_card(player, against=against) player.game.duel(player, against) @@ -247,7 +250,7 @@ class Emporio(Card): 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" - def play_card(self, player, against): + def play_card(self, player, against, _with=None): super().play_card(player, against=against) player.game.emporio() return True @@ -259,7 +262,7 @@ class Gatling(Card): self.icon = '🛰' 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) player.game.attack_others(player) return True @@ -271,7 +274,7 @@ class Indiani(Card): self.icon = '🏹' 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) player.game.indian_others(player) return True @@ -283,7 +286,7 @@ class Mancato(Card): self.icon = '😅' 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 if (not player.has_played_bang and against != None and isinstance(player.character, chars.CalamityJanet)): player.sio.emit('chat_message', room=player.game.name, @@ -301,7 +304,7 @@ class Panico(Card): 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" - 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: super().play_card(player, against=against) 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.icon = '🍻' - def play_card(self, player, against): + def play_card(self, player, against, _with=None): 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.') for p in player.game.players: @@ -334,7 +337,7 @@ class WellsFargo(Card): self.desc = "Pesca 3 carte dalla cima del mazzo" 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, data=f'{player.name} ha giocato {self.name} e ha pescato 3 carte.') for i in range(3): diff --git a/backend/bang/expansions/dodge_city/cards.py b/backend/bang/expansions/dodge_city/cards.py index 0e4c029..b5f57bc 100644 --- a/backend/bang/expansions/dodge_city/cards.py +++ b/backend/bang/expansions/dodge_city/cards.py @@ -16,12 +16,11 @@ class Pugno(Card): def __init__(self, suit, number): super().__init__(suit, 'Pugno!', number, range=1) self.icon = '👊' - self.desc = "Spara a un giocatore a distanta 1" + self.desc = "Spara a un giocatore a distanza 1" self.need_target = True - def play_card(self, player, against): + def play_card(self, player, against, _with=None): if against != None: - import bang.characters as chars super().play_card(player, against=against) player.game.attack(player, against) return True @@ -33,14 +32,105 @@ class Schivata(Mancato): self.name = 'Schivata' self.icon = '🙅‍♂️' 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 def use_card(self, player): player.hand.append(player.game.deck.draw()) 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]: return [ #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), Panico(Suit.HEARTS, 'J'), Pugno(Suit.SPADES, 10), + RagTime(Suit.HEARTS, 9), + Rissa(Suit.SPADES, 'J'), Schivata(Suit.DIAMONDS, 7), Schivata(Suit.HEARTS, 'K'), + SpringField(Suit.SPADES, 'K'), + Tequila(Suit.CLUBS, 9), + Whisky(Suit.HEARTS, 'Q'), ] diff --git a/backend/bang/game.py b/backend/bang/game.py index 27bb909..7c69cbe 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -41,8 +41,11 @@ class Game: self.notify_room() 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 + if len(self.players) > 7: + if 'dodge_city' not in self.expansions: + self.expansions.append('dodge_city') player.join_game(self) self.players.append(player) print(f'Added player {player.name} to game') @@ -61,7 +64,13 @@ class Game: self.readyCount += 1 self.notify_room() 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): char_cards = random.sample(characters.all_characters(), len(self.players)*2) @@ -78,6 +87,7 @@ class Game: self.started = True self.deck = Deck(self) self.initial_players = len(self.players) + self.distribute_roles() self.choose_characters() def distribute_roles(self): @@ -89,21 +99,16 @@ class Game: roles.Outlaw('Elimina il Vice 🎖, se non lo elimini tu elimina anche il Rinnegato') ] 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)] random.shuffle(available_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].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): self.sio.emit('chat_message', room=self.name, data=f'{self.players[i].name} È lo sceriffo') 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.play_turn() def attack_others(self, attacker: players.Player): attacker.pending_action = players.PendingAction.WAIT diff --git a/backend/bang/players.py b/backend/bang/players.py index 381f702..fd9162d 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -203,6 +203,7 @@ class Player: else: self.game.next_player().equipment.append(self.equipment.pop(i)) self.game.next_player().notify_self() + break if any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]): self.notify_self() return @@ -218,7 +219,7 @@ class Player: self.game.deck.scrap(self.equipment.pop(i)) self.end_turn(forced=True) return - else: + elif pickable_cards == 0: self.game.deck.scrap(self.equipment.pop(i)) break break @@ -248,18 +249,23 @@ class Player: s += f"equipment {[str(c) for c in self.equipment]}" 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)): print('illegal') return card: cs.Card = self.hand.pop(hand_index) - print(self.name, 'is playing ', card, ' against:', against) - did_play_card = card.play_card(self, against) + withCard: cs.Card = None + 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 did_play_card: self.game.deck.scrap(card) else: self.hand.insert(hand_index, card) + if withCard: + self.hand.insert(_with, withCard) self.notify_self() def choose(self, card_index): @@ -277,9 +283,16 @@ class Player: self.hand.append(card) else: self.game.deck.scrap(card) - self.target_p = '' - self.choose_action = '' - self.pending_action = PendingAction.PLAY + 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.event_type = '' + 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() # specifico per personaggio elif self.is_drawing and isinstance(self.character, chars.KitCarlson): diff --git a/backend/bang/roles.py b/backend/bang/roles.py index 834f173..e311a43 100644 --- a/backend/bang/roles.py +++ b/backend/bang/roles.py @@ -75,7 +75,7 @@ class Renegade(Role): return True elif initial_players == 3 and attacker_role != None: 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!") return True return False diff --git a/frontend/package.json b/frontend/package.json index b552a24..9ad6b0c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "pretty-checkbox-vue": "^1.1.9", "socket.io-client": "^3.0.3", "vue": "^2.6.11", + "vue-i18n": "^8.22.2", "vue-socket.io": "^3.0.10" }, "devDependencies": { diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 39f33c1..9052697 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -2,29 +2,29 @@
-

Scegli un username:

+

{{$t("choose_username")}}

-

Giocatori online: {{onlinePlayers}}

+

{{$t("online_players")}}{{onlinePlayers}}

-

Giocatori online: {{onlinePlayers}}

+

{{$t("online_players")}}{{onlinePlayers}}

-

Lobby disponibili:

+

{{$t("available_lobbies")}}

-

Nessuna lobby disponibile

+

{{$t("no_lobby_available")}}

-

Crea una lobby:

-

Nome:

+

{{$t("create_lobby")}}

+

{{$t("lobby_name")}}

@@ -33,9 +33,17 @@
-

Attenzione!

-

Connessione al server assente.

+

{{$t("warning")}}

+

{{$t("connection_error")}}

+
@@ -66,7 +74,7 @@ export default { getSelfCard() { return { name: this.username, - number: 'YOU', + number: this.$t('you'), icon: '🤠', is_character: true, } @@ -120,7 +128,7 @@ export default { e.preventDefault(); }, 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}) }, init() { @@ -217,7 +225,7 @@ h1,h2,h3,h4,p,span,b,label{ transform: scale(0); } } -input { +input, select { border: 2px solid; border-radius: 4px; font-size: 1rem; @@ -227,7 +235,7 @@ input { transition: border-color 0.5s ease-out; } @media (prefers-color-scheme: dark) { - :root, #app, input { + :root, #app, input, select { background-color: #181a1b; color: rgb(174, 194, 211); } diff --git a/frontend/src/components/Chat.vue b/frontend/src/components/Chat.vue index 1528f07..1ec4706 100644 --- a/frontend/src/components/Chat.vue +++ b/frontend/src/components/Chat.vue @@ -1,6 +1,6 @@ @@ -24,13 +24,14 @@ export default { cancel: Function, cancelText: { type: String, - default: 'ANNULLA', + default: '', }, text: String, hintText: String, }, data: () => ({ - desc: '' + desc: '', + realCancelText: '' }), computed: { showCancelBtn() { @@ -43,7 +44,13 @@ export default { showDesc(card) { this.desc = card.desc } - } + }, + mounted() { + this.realCancelText = this.cancelText + if (this.realCancelText == '') { + this.realCancelText = this.$t('cancel') + } + }, } diff --git a/frontend/src/components/Deck.vue b/frontend/src/components/Deck.vue index 5537291..dec366f 100644 --- a/frontend/src/components/Deck.vue +++ b/frontend/src/components/Deck.vue @@ -1,16 +1,22 @@ @@ -30,14 +36,12 @@ export default { name: 'PewPew!', icon: '💥', }, - endTurnCard: { - name: 'Termina turno!', - icon: '⛔️' - }, + endTurnCard: null, lastScrap: null, previousScrap: null, pending_action: false, isPlaying: true, + desc: '', }), sockets: { self(self){ @@ -49,6 +53,12 @@ export default { this.lastScrap = card } }, + mounted() { + this.endTurnCard = { + name: this.$t('end_turn'), + icon: '⛔️' + } + }, methods: { action(pile) { if (this.pending_action !== false && this.pending_action < 2) { diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index 073c40e..8457c6e 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -1,11 +1,11 @@ @@ -74,9 +73,9 @@ export default { players: [], messages: [], distances: {}, - availableCharacters: [], self: {}, hasToChoose: false, + target_p: '', chooseCards: [], wantsToEndTurn: false, selectedInfo: null, @@ -98,9 +97,6 @@ export default { } }) }, - characters(data) { - this.availableCharacters = JSON.parse(data) - }, start() { this.started = true; }, @@ -121,7 +117,7 @@ export default { startGameCard() { if (!this.started && this.players.length > 2 && this.isRoomOwner) { return { - name: 'Start', + name: this.$t('start_game'), icon: '▶️', is_equipment: true, number: `${this.players.length}🤠` @@ -129,9 +125,6 @@ export default { } return null; }, - showChooser() { - return this.availableCharacters.length > 0; - }, playersTable() { console.log('update players') return this.players.map((x,i) => { @@ -164,7 +157,7 @@ export default { getPlayerCard(player) { return { 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)?'👍': '🤠') ) : '☠️', is_character: true, } @@ -173,12 +166,9 @@ export default { this.started = true; this.$socket.emit('start_game') }, - setCharacter(char) { - this.availableCharacters = [] - this.$socket.emit('set_character', char.name) - }, choose(player_name) { console.log('choose from' + player_name) + this.target_p = player_name let pl = this.players.filter(x=>x.name === player_name)[0] console.log(pl) let arr = [] @@ -197,6 +187,7 @@ export default { console.log(card + ' ' + this.chooseCards.indexOf(card)) this.chooseCards = [] this.hasToChoose = false + this.target_p = '' }, drawFromPlayer(name) { console.log(name) diff --git a/frontend/src/components/Player.vue b/frontend/src/components/Player.vue index 6d3ebeb..6048977 100644 --- a/frontend/src/components/Player.vue +++ b/frontend/src/components/Player.vue @@ -7,37 +7,43 @@ @pointerenter.native="desc=my_role.goal" @pointerleave.native="desc=''"/> + + ❤️ + 💀 +
-

{{desc}}

- - ❤️ - 💀 - + +

{{desc}}

+
- Mano + {{$t('hand')}}
-

{{hint}}

- + +

{{hint}}

+
+ - - - - - - - - - + + + + + + + + + + - + @@ -59,6 +65,7 @@ export default { data: () => ({ my_role: null, character: null, + availableCharacters: [], equipment: [], hand: [], lives: 0, @@ -66,6 +73,7 @@ export default { hint: '', pending_action: null, card_against: null, + card_with: null, has_played_bang: false, playersDistances: [], is_my_turn: false, @@ -83,6 +91,7 @@ export default { sidScrapForHealth: [], sidWantsScrapForHealth: false, mancato_needed: 0, + name: '', }), sockets: { role(role) { @@ -90,8 +99,12 @@ export default { this.my_role.is_back = true this.show_role = true }, + characters(data) { + this.availableCharacters = JSON.parse(data) + }, self(self) { self = JSON.parse(self) + this.name = self.name this.pending_action = self.pending_action this.character = self.character this.character.is_character = true @@ -122,11 +135,14 @@ export default { }, notify_card(mess) { this.notifycard = mess + setTimeout(function(){ + this.notifycard = null + }.bind(this), 4000) } }, computed:{ 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() { return this.isEndingTurn && !this.canEndTurn && this.is_my_turn; @@ -136,7 +152,7 @@ export default { }, visiblePlayers() { this.range; - return this.playersDistances.filter(x => { + let vis = this.playersDistances.filter(x => { if (!this.can_target_sheriff && x.is_sheriff) return false else @@ -148,6 +164,15 @@ export default { icon: player.is_sheriff ? '⭐' : '🤠', 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() { return !this.is_my_turn && this.pending_action == 0 @@ -155,7 +180,7 @@ export default { instruction() { if (this.pending_action == null) 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] }, canEndTurn() { @@ -163,7 +188,7 @@ export default { }, respondCards() { let cc = [{ - name: 'Prendi Danno', + name: this.$t('take_dmg'), icon: '❌', is_equipment: true, }] @@ -174,6 +199,10 @@ export default { } }, methods: { + setCharacter(char) { + this.availableCharacters = [] + this.$socket.emit('set_character', char.name) + }, sidScrap(c) { this.sidScrapForHealth.push(this.hand.indexOf(c)) if (this.sidScrapForHealth.length == 2) { @@ -195,7 +224,9 @@ export default { 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) 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) this.range = this.sight else @@ -217,14 +248,32 @@ export default { this.really_play_card(this.card_against, player.name) 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() { this.card_against = null + this.card_with = null }, really_play_card(card, against) { let card_data = { 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) this.$socket.emit('play_card', card_data) }, diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json new file mode 100644 index 0000000..7fd328b --- /dev/null +++ b/frontend/src/i18n/en.json @@ -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" +} \ No newline at end of file diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js new file mode 100644 index 0000000..56667db --- /dev/null +++ b/frontend/src/i18n/index.js @@ -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, +} \ No newline at end of file diff --git a/frontend/src/i18n/it.json b/frontend/src/i18n/it.json new file mode 100644 index 0000000..7d256d5 --- /dev/null +++ b/frontend/src/i18n/it.json @@ -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" +} \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index d86bdbf..83cb373 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -11,6 +11,18 @@ Vue.use(new VueSocketIO({ import PrettyCheckbox from 'pretty-checkbox-vue'; 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({ + i18n, render: h => h(App), }).$mount('#app') diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9c6e53c..c6335a4 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -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" 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": 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"