diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 21542a8..98432f0 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -1,4 +1,5 @@ -from typing import List, Set, Dict, Tuple, Optional +from __future__ import annotations +from typing import List, Set, Dict, Tuple, Optional, TYPE_CHECKING import bang.expansions.fistful_of_cards.card_events as ce import bang.expansions.high_noon.card_events as ceh from abc import ABC, abstractmethod @@ -6,11 +7,16 @@ from enum import IntEnum import bang.roles as r from globals import G +if TYPE_CHECKING: + from bang.players import Player + from bang.game import Game + class Suit(IntEnum): DIAMONDS = 0 # ♦ CLUBS = 1 # ♣ HEARTS = 2 # ♥ SPADES = 3 # ♠ + GOLD = 4 # 🤑 class Card(ABC): @@ -47,14 +53,14 @@ class Card(ABC): def __str__(self): if str(self.suit).isnumeric(): - char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)] + char = ['♦️', '♣️', '♥️', '♠️', '🤑'][int(self.suit)] else: char = self.suit return f'{self.name} {char}{self.number}' return super().__str__() def num_suit(self): - return f"{['♦️', '♣️', '♥️', '♠️'][int(self.suit)]}{self.number}" + return f"{['♦️', '♣️', '♥️', '♠️', '🤑'][int(self.suit)]}{self.number}" def reset_card(self): if self.usable_next_turn: @@ -64,7 +70,7 @@ class Card(ABC): if self.must_be_used: self.must_be_used = False - def play_card(self, player, against=None, _with=None):#self --> carta + def play_card(self, player:Player, against:str=None, _with:int=None):#self --> carta if (player.game.check_event(ce.IlGiudice)) and self.usable_next_turn and not self.can_be_used_now: return False if self.is_equipment: @@ -98,10 +104,10 @@ class Card(ABC): def use_card(self, player): pass - def is_duplicate_card(self, player): - return self.name in [c.name for c in player.equipment] or self.name in [c.name for c in player.gold_rush_equipment] + def is_duplicate_card(self, player:Player): + return any(c.name==self.name for c in player.equipment) or any(c.name==self.name for c in player.gold_rush_equipment) - def check_suit(self, game, accepted): + def check_suit(self, game:Game, accepted:List[Suit]): if game.check_event(ceh.Benedizione): return Suit.HEARTS in accepted elif game.check_event(ceh.Maledizione): @@ -226,7 +232,7 @@ class Bang(Card): elif against is not None: import bang.characters as chars super().play_card(player, against=against) - if not self.number == 42: # 42 gold rush + if not (self.number == 42 and self.suit == Suit.GOLD): # 42 gold rush player.bang_used += 1 player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1 if player.character.check(player.game, chars.WillyTheKid): diff --git a/backend/bang/characters.py b/backend/bang/characters.py index 2925e21..620599a 100644 --- a/backend/bang/characters.py +++ b/backend/bang/characters.py @@ -1,8 +1,13 @@ +from __future__ import annotations from abc import ABC, abstractmethod from bang.expansions import * -from typing import List +from typing import List, TYPE_CHECKING from globals import G +if TYPE_CHECKING: + from bang.players import Player + from bang.game import Game + class Character(ABC): def __init__(self, name: str, max_lives: int, sight_mod: int = 0, visibility_mod: int = 0, pick_mod: int = 0, desc: str = ''): super().__init__() @@ -16,13 +21,13 @@ class Character(ABC): self.icon = '🤷‍♂️' self.number = ''.join(['❤️']*self.max_lives) - def check(self, game, character): + def check(self, game:Game, character:Character): import bang.expansions.high_noon.card_events as ceh if game.check_event(ceh.Sbornia): return False return isinstance(self, character) - def special(self, player, data): + def special(self, player:Player, data): import bang.expansions.high_noon.card_events as ceh if player.game.check_event(ceh.Sbornia): return False diff --git a/backend/bang/expansions/gold_rush/shop_cards.py b/backend/bang/expansions/gold_rush/shop_cards.py index 5b4ef8f..5ff68c9 100644 --- a/backend/bang/expansions/gold_rush/shop_cards.py +++ b/backend/bang/expansions/gold_rush/shop_cards.py @@ -58,7 +58,9 @@ class Bottiglia(ShopCard): def play_card(self, player, against=None, _with=None): # bang, birra, panico - player.available_cards = [Bang(1,42), Birra(1,42), Panico(1,42)] + player.available_cards = [Bang(4,42), Birra(4,42), Panico(4,42)] + if not any((player.get_sight() >= p['dist'] for p in player.game.get_visible_players(player))): + player.available_cards.pop(0) for i in range(len(player.available_cards)): player.available_cards[i].must_be_used = True player.choose_text = 'choose_bottiglia' @@ -73,7 +75,7 @@ class Complice(ShopCard): def play_card(self, player, against=None, _with=None): # emporio, duello, Cat balou - player.available_cards = [Emporio(1,42), Duello(1,42), CatBalou(1,42)] + player.available_cards = [Emporio(4,42), Duello(4,42), CatBalou(4,42)] for i in range(len(player.available_cards)): player.available_cards[i].must_be_used = True player.choose_text = 'choose_complice' diff --git a/backend/bang/expansions/the_valley_of_shadows/cards.py b/backend/bang/expansions/the_valley_of_shadows/cards.py index 9982937..ac742ee 100644 --- a/backend/bang/expansions/the_valley_of_shadows/cards.py +++ b/backend/bang/expansions/the_valley_of_shadows/cards.py @@ -33,7 +33,24 @@ class Lemat(Card): def __init__(self, suit, number): super().__init__(suit, 'Lemat', number, is_equipment=True, is_weapon=True, range=1) self.icon = '🔫' # ogni carta può essere usata come bang, conta per il conteggio dei bang per turno - #TODO + + def play_card(self, player, against, _with=None): + if (player.game.check_event(ce.IlGiudice) and self.can_be_used_now) or (not self.can_be_used_now and player.game.check_event(ce.Lazo)): + return False + if self.can_be_used_now: + self.can_be_used_now = False + G.sio.emit('chat_message', room=player.game.name, + data=f'_play_card|{player.name}|{self.name}') + player.equipment.append(self) + player.notify_self() + return True + elif not player.has_played_bang and any((player.get_sight() >= p['dist'] for p in player.game.get_visible_players(player))): + from bang.players import PendingAction + player.available_cards = player.hand.copy() + player.pending_action = PendingAction.CHOOSE + player.choose_text = 'choose_play_as_bang' + player.notify_self() + return False class SerpenteASonagli(Card): def __init__(self, suit, number): @@ -165,7 +182,7 @@ class Mira(Card): def play_card(self, player, against, _with=None): if against is not None and _with is not None: - super().play_card(player, against=against) + super().play_card(player, against=against, _with=_with) player.game.attack(player, against, card_name=self.name) return True return False @@ -218,7 +235,7 @@ def get_starting_deck() -> List[Card]: cards = [ Fantasma(Suit.SPADES, 9), Fantasma(Suit.SPADES, 10), - # Lemat(Suit.DIAMONDS, 4), + Lemat(Suit.DIAMONDS, 4), SerpenteASonagli(Suit.HEARTS, 7), Shotgun(Suit.SPADES, 'K'), Taglia(Suit.CLUBS, 9), diff --git a/backend/bang/expansions/the_valley_of_shadows/characters.py b/backend/bang/expansions/the_valley_of_shadows/characters.py index 56d4347..60e4ddf 100644 --- a/backend/bang/expansions/the_valley_of_shadows/characters.py +++ b/backend/bang/expansions/the_valley_of_shadows/characters.py @@ -9,14 +9,14 @@ class BlackFlower(Character): self.icon = '🥀' def special(self, player, data): #fiori = suit.Clubs - if player.special_use_count > 0 and not any((c.suit == cs.Suit.CLUBS for c in player.hand)): + if player.special_use_count > 0 or not any((c.suit == cs.Suit.CLUBS for c in player.hand)): return False - if super().special(player, data): + if any((player.get_sight() >= p['dist'] for p in player.game.get_visible_players(player))) and super().special(player, data): from bang.players import PendingAction - player.special_use_count += 1 player.available_cards = [c for c in player.hand if c.suit == cs.Suit.CLUBS] + player.special_use_count += 1 player.pending_action = PendingAction.CHOOSE - player.choose_text = 'blackflower_special' + player.choose_text = 'choose_play_as_bang' player.notify_self() class ColoradoBill(Character): diff --git a/backend/bang/game.py b/backend/bang/game.py index bd64659..82f006e 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -61,7 +61,7 @@ class Game: self.initial_players = 0 self.password = '' self.expansions: List[str] = [] - self.available_expansions = ['dodge_city', 'fistful_of_cards', 'high_noon', 'gold_rush'] + self.available_expansions = ['dodge_city', 'fistful_of_cards', 'high_noon', 'gold_rush', 'the_valley_of_shadows'] self.shutting_down = False self.is_competitive = False self.disconnect_bot = True @@ -446,7 +446,7 @@ class Game: {'name':self.players[self.turn].name,'cards': self.available_cards}, default=lambda o: o.__dict__)) self.players[self.turn].notify_self() - def respond_emporio(self, player, i): + def respond_emporio(self, player:pl.Player, i:int): card = self.available_cards.pop(i) G.sio.emit('chat_message', room=self.name, data=f'_choose_emporio|{player.name}|{card.name}') player.hand.append(card) diff --git a/backend/bang/players.py b/backend/bang/players.py index b78be96..8bcd78f 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -1,3 +1,4 @@ +from __future__ import annotations from enum import IntEnum import json from random import random, randrange, sample, uniform @@ -14,11 +15,14 @@ import bang.expansions.gold_rush.shop_cards as grc import bang.expansions.gold_rush.characters as grch import bang.expansions.the_valley_of_shadows.cards as tvosc import bang.expansions.the_valley_of_shadows.characters as tvosch -from typing import List +from typing import List, TYPE_CHECKING from metrics import Metrics from globals import G import sys +if TYPE_CHECKING: + from bang.game import Game + robot_pictures = [ 'https://i.imgur.com/40rAFIb.jpg', 'https://i.imgur.com/gG77VRR.jpg', @@ -50,7 +54,7 @@ class Player: def is_admin(self): return self.discord_id in {'244893980960096266', '539795574019457034'} - def get_avatar(self): + def _get_avatar(self): import requests headers = { 'Authorization': 'Bearer ' + self.discord_token, @@ -78,7 +82,6 @@ class Player: print(r) def __init__(self, name, sid, bot=False, discord_token=None): - import bang.game as g super().__init__() self.name = name self.sid = sid @@ -89,8 +92,8 @@ class Player: if self.is_bot: self.avatar = robot_pictures[randrange(len(robot_pictures))] if self.discord_token: - G.sio.start_background_task(self.get_avatar) - self.game: g = None + G.sio.start_background_task(self._get_avatar) + self.game: Game = None self.reset() def reset(self): @@ -187,7 +190,6 @@ class Player: self.pending_action = PendingAction.DRAW self.notify_self() - def prepare(self): self.max_lives = self.character.max_lives + self.role.health_mod self.lives = self.max_lives @@ -717,6 +719,7 @@ class Player: print('which is a gold rush black card') card: grc.ShopCard = self.gold_rush_equipment[hand_index - len(self.hand) - len(self.equipment)] return card.play_card(self) + from_hand = hand_index < len(self.hand) card: cs.Card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand)) withCard: cs.Card = None if _with is not None: @@ -744,7 +747,10 @@ class Player: self.equipment.insert(hand_index-len(self.hand), card) elif card.is_equipment or (card.usable_next_turn and not card.can_be_used_now): if not did_play_card: - self.hand.insert(hand_index, card) + if from_hand: + self.hand.insert(hand_index, card) + else: + self.equipment.insert(hand_index-len(self.hand), card) else: did_play_card = True if not self.game.is_replay: @@ -870,11 +876,11 @@ class Player: G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Sventagliata|{player.name}') self.pending_action = PendingAction.PLAY self.notify_self() - elif 'blackflower_special' in self.choose_text: + elif 'choose_play_as_bang' in self.choose_text: if card_index <= len(self.available_cards): self.hand.remove(self.available_cards[card_index]) self.game.deck.scrap(self.available_cards[card_index], player=self) - self.hand.append(cs.Bang(cs.Suit.CLUBS,42)) + self.hand.append(cs.Bang(self.available_cards[card_index].suit, 42)) self.pending_action = PendingAction.PLAY self.notify_self() elif 'choose_tornado' in self.choose_text: diff --git a/backend/server.py b/backend/server.py index 5382f0f..8017a2a 100644 --- a/backend/server.py +++ b/backend/server.py @@ -56,12 +56,12 @@ HASTEBIN_HEADERS = { } app = socketio.WSGIApp(sio, static_files=static_files) -games: List[Game] = [] +games: dict[str, Game] = {} online_players = 0 blacklist: List[str] = [] def send_to_debug(error): - for g in games: + for g in games.values(): if g.debug: sio.emit('chat_message', room=g.name, data={'color': f'red','text':json.dumps({'ERROR':error}), 'type':'json'}) elif any((p.is_admin() for p in g.players)): @@ -85,9 +85,10 @@ def bang_handler(func): return wrapper_func 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) < 10 and not g.is_hidden]) - sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if g.started and not g.is_hidden and len(g.players) > 0]) - Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) + open_lobbies = [g for g in games.values() if 0 < len(g.players) < 10 and not g.is_hidden][-10:] + sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in open_lobbies if not g.started]) + sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in open_lobbies if g.started]) + Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games.values())]) Metrics.send_metric('online_players', points=[online_players]) @sio.event @@ -137,7 +138,7 @@ def set_username(sid, username): ses = sio.get_session(sid) if not isinstance(ses, Player): dt = username["discord_token"] if 'discord_token' in username else None - sio.save_session(sid, Player(username["name"], sid, discord_token=dt)) + sio.save_session(sid, Player(username.get('name', 'player'), sid, discord_token=dt)) print(f'{sid} is now {username}') advertise_lobbies() elif ses.game is None or not ses.game.started: @@ -178,7 +179,7 @@ def get_me(sid, data): else: sid.game.replay(log, speed=0, fast_forward=int(data['ffw'])) return - if (room := next((g for g in games if g.name == data['name']), None)) is not None: + if data['name'] in games and (room := games[data['name']]) is not None: if not room.started: join_room(sid, data) elif room.started: @@ -233,8 +234,7 @@ def disconnect(sid): sio.emit('players', room='lobby', data=online_players) if p.game and p.disconnect(): sio.close_room(p.game.name) - if p.game in games: - games.pop(games.index(p.game)) + games.pop(p.game.name) print('disconnect ', sid) advertise_lobbies() Metrics.send_metric('online_players', points=[online_players]) @@ -243,15 +243,15 @@ def disconnect(sid): @bang_handler def create_room(sid, room_name): if (p := sio.get_session(sid)).game is None: - while any((g.name == room_name for g in games)): - room_name += f'_{random.randint(0,100)}' + while room_name in games: + room_name += f'_{random.randint(0, 10000)}' sio.leave_room(sid, 'lobby') sio.enter_room(sid, room_name) g = Game(room_name) g.add_player(p) if room_name in blacklist: g.is_hidden = True - games.append(g) + games[room_name] = g print(f'{sid} created a room named {room_name}') advertise_lobbies() @@ -282,27 +282,26 @@ def toggle_replace_with_bot(sid): @bang_handler def join_room(sid, room): room_name = room['name'] - i = [g.name for g in games].index(room_name) - if games[i].password != '' and games[i].password != room.get('password', '').upper(): + if games[room_name].password != '' and games[room_name].password != room.get('password', '').upper(): return - if not games[i].started: + if not games[room_name].started: print(f'{sid} joined a room named {room_name}') sio.leave_room(sid, 'lobby') sio.enter_room(sid, room_name) - while any((p.name == sio.get_session(sid).name and not p.is_bot for p in games[i].players)): + while any((p.name == sio.get_session(sid).name and not p.is_bot for p in games[room_name].players)): sio.get_session(sid).name += f'_{random.randint(0,100)}' sio.emit('me', data=sio.get_session(sid).name, room=sid) - games[i].add_player(sio.get_session(sid)) + games[room_name].add_player(sio.get_session(sid)) advertise_lobbies() else: - games[i].spectators.append(sio.get_session(sid)) - sio.get_session(sid).game = games[i] + games[room_name].spectators.append(sio.get_session(sid)) + sio.get_session(sid).game = games[room_name] sio.get_session(sid).pending_action = PendingAction.WAIT - sio.enter_room(sid, games[0].name) - games[i].notify_room(sid) + sio.enter_room(sid, games[room_name].name) + games[room_name].notify_room(sid) eventlet.sleep(0.5) - games[i].notify_room(sid) - games[i].notify_all() + games[room_name].notify_room(sid) + games[room_name].notify_all() """ Sockets for the status page @@ -324,7 +323,7 @@ def get_all_rooms(sid, deploy_key): 'incremental_turn': g.incremental_turn, 'debug': g.debug, 'spectators': len(g.spectators) - } for g in games]) + } for g in games.values()]) @sio.event @bang_handler @@ -339,16 +338,16 @@ def reset(sid, data): global games ses = sio.get_session(sid) if ('DEPLOY_KEY' in os.environ and 'key' in data and data['key'] == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): - for g in games: + for g in games.values(): sio.emit('kicked', room=g.name) - games = [] + games = {} @sio.event @bang_handler def hide_toogle(sid, data): ses = sio.get_session(sid) if ('DEPLOY_KEY' in os.environ and 'key' in data and data['key'] == os.environ['DEPLOY_KEY']) or (isinstance(ses, Player) and ses.is_admin()): - game = [g for g in games if g.name==data['room']] + game = games['room'] if len(games) > 0: game[0].is_hidden = not game[0].is_hidden if game[0].is_hidden: @@ -797,7 +796,7 @@ def discord_auth(sid, data): def pool_metrics(): while True: sio.sleep(60) - Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)]) + Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games.values())]) Metrics.send_metric('online_players', points=[online_players]) import urllib.parse @@ -830,14 +829,15 @@ def save_games(): if not os.path.exists("save"): os.mkdir("save") with open('./save/games.pickle', 'wb') as f: - pickle.dump([g for g in games if g.started and not g.is_replay and not g.is_hidden and len(g.players) > 0], f) + pickle.dump([g for g in games.values() if g.started and not g.is_replay and not g.is_hidden and len(g.players) > 0], f) if __name__ == '__main__': if os.path.exists('./save/games.pickle'): try: with open('./save/games.pickle', 'rb') as file: - games = pickle.load(file) - for g in games: + temp_g = pickle.load(file) + games = {g.name: g for g in temp_g} + for g in games.values(): g.spectators = [] for p in g.players: if p.sid != 'bot': diff --git a/backend/tests/valley_of_shadows_characters_test.py b/backend/tests/valley_of_shadows_characters_test.py index ecd9171..a144a68 100644 --- a/backend/tests/valley_of_shadows_characters_test.py +++ b/backend/tests/valley_of_shadows_characters_test.py @@ -50,3 +50,22 @@ def test_ColoradoBill(): assert p1.lives == 3 p.play_card(0, p1.name) assert p1.pending_action == PendingAction.RESPOND + +# test BlackFlower +def test_BlackFlower(): + g = Game('test') + ps = [Player(f'p{i}', f'p{i}') for i in range(2)] + for p in ps: + g.add_player(p) + g.start_game() + for p in ps: + p.available_characters = [BlackFlower()] + p.set_character(p.available_characters[0].name) + p = g.players[g.turn] + p.draw('') + p.hand = [cs.Volcanic(cs.Suit.DIAMONDS,0)] + p.special('') + assert p.pending_action == PendingAction.PLAY + p.hand = [cs.Volcanic(cs.Suit.CLUBS,0)] + p.special('') + assert p.pending_action == PendingAction.CHOOSE diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b6f2df5..ec30545 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,9 +6,16 @@

{{$t("warning")}}

{{$t("connection_error")}}

+
+ @@ -63,8 +70,17 @@ export default { theme: 'light', report: '', sending_report: false, + connect_dev: undefined, + backendSuggestions: [ + { name: 'Bang Xamin', url: 'https://bang.xamin.it' }, + { name: 'Bang Miga', url: 'https://bang.migani.synology.me/' }, + { name: 'Localhost', url: 'http://localhost:5001' }, + ], }), computed: { + shouldShowBackendSuggestions() { + return window.location.origin.indexOf('vercel') !== -1 || window.location.origin.indexOf('localhost') !== -1 + }, }, sockets: { connect() { @@ -98,6 +114,16 @@ export default { // Send message to SW to skip the waiting and activate the new SW this.registration.waiting.postMessage({ type: 'SKIP_WAITING' }) }, + changeBackend(suggestion) { + this.$socket.disconnect(); + window.localStorage.setItem('connect-dev', suggestion.url); + window.location.reload(); + }, + resetConnection() { + this.$socket.disconnect(); + window.localStorage.removeItem('connect-dev'); + window.navigation.reload(); + }, detectColorScheme() { if(localStorage.getItem("theme")){ this.theme = localStorage.getItem("theme") @@ -146,6 +172,9 @@ export default { userLang = 'en'; this.$i18n.locale = userLang.split('-')[0] } + if (window.localStorage.getItem('connect-dev')) { + this.connect_dev = window.localStorage.getItem('connect-dev') + } this.detectColorScheme() if (window.location.origin.indexOf('localhost') !== -1) return; datadogRum.init({ diff --git a/frontend/src/components/Card.vue b/frontend/src/components/Card.vue index 552c035..966cb51 100644 --- a/frontend/src/components/Card.vue +++ b/frontend/src/components/Card.vue @@ -32,7 +32,7 @@ export default { }, suit() { if (this.card && !isNaN(this.card.suit)) { - let x = ['♦️','♣️','♥️','♠️'] + let x = ['♦️','♣️','♥️','♠️','🤑'] return x[this.card.suit]; } else if (this.card.suit) { return this.card.suit; @@ -89,6 +89,19 @@ export default { #816b45 10px ); } +.card:not(.back,.fistful-of-cards,.high-noon,.gold-rush):before{ + content: ""; + background-image: radial-gradient(var(--bg-color) 13%, #0000 5%), + radial-gradient(var(--bg-color) 14%, transparent 5%), + radial-gradient(var(--bg-color) 8%, transparent 5%); + background-position: -12px 0, 12px 14px, 0 -12pt; + background-size: 50px 50px; + position: absolute; + top: -10px; + left: -10px; + bottom: -4px; + right: -4px; +} .card.equipment { box-shadow: 0 0 0 3pt #5c5e83, diff --git a/frontend/src/components/Lobby.vue b/frontend/src/components/Lobby.vue index 2c29f19..90172ac 100644 --- a/frontend/src/components/Lobby.vue +++ b/frontend/src/components/Lobby.vue @@ -61,7 +61,7 @@
+ :style="`margin-top: ${i < 1 ? 10 : -(Math.min((p.equipment.length + p.gold_rush_equipment.length + 1) * 12, 80))}pt;`"/> @@ -531,8 +531,14 @@ export default { transform: scale(0.8); margin-bottom: -4pt; } +.tiny-equipment .card { + transform: rotate(2deg); +} +.tiny-equipment .card:nth-child(odd) { + transform: rotate(-2deg); +} .tiny-equipment .card:hover { - transform: translateY(10px) scale(1.1); + transform: translateY(10px) scale(1.2); z-index: 1; } .tiny-character { diff --git a/frontend/src/components/Menu.vue b/frontend/src/components/Menu.vue index 59ac503..c8959d4 100644 --- a/frontend/src/components/Menu.vue +++ b/frontend/src/components/Menu.vue @@ -169,6 +169,10 @@ export default { 'Authorization': 'Bearer ' + localStorage.getItem('discord_token') } }) + .then((res) => { + if (res.status !== 200) throw new Error(res.status) + return res + }) .then(response => response.json()) .then(data => { console.log(data) diff --git a/frontend/src/components/Player.vue b/frontend/src/components/Player.vue index 1e96ac7..685616e 100644 --- a/frontend/src/components/Player.vue +++ b/frontend/src/components/Player.vue @@ -3,13 +3,13 @@

{{instruction}}

- - ❤️ 💀 + ❤️
@@ -17,7 +17,7 @@ 💵️
- + @@ -390,7 +390,7 @@ export default { }, play_card(card, from_equipment) { console.log('play ' + card.name) - if (from_equipment && (!card.can_be_used_now || (this.eventCard && this.eventCard.name == "Lazo"))) return; + if (from_equipment && ((!card.can_be_used_now && !card.name == 'Lemat') || (this.eventCard && this.eventCard.name == "Lazo"))) return; else if (card.usable_next_turn && !card.can_be_used_now) return this.really_play_card(card, null); let calamity_special = (card.name === 'Mancato!' && this.character.name === 'Calamity Janet') let cant_play_bang = (this.has_played_bang && card.number !==42 && this.equipment.filter(x => x.name == 'Volcanic').length == 0) @@ -514,8 +514,12 @@ export default { margin-left: -30pt; } .hand>.card:hover { - margin-right:35pt; - transform: translateY(-15px); + transform: translateY(-15px) translateX(-15px) rotate(-2deg); + z-index: 1; +} +.hand>.card:nth-child(1):hover, .hand>.card:last-child:hover { + transform: translateY(-15px) translateX(0) rotate(2deg); + z-index: 1; } #hand_text{ color: var(--muted-color); @@ -531,6 +535,12 @@ export default { margin: 10pt 0pt; overflow:auto; } +#equipment .card:nth-child(even) { + transform: rotate(1deg); +} +#equipment .card:nth-child(odd) { + transform: rotate(-1deg); +} .hurt-notify { pointer-events: none; animation: disappear 0.5s ease-in forwards; diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 5d84e7c..5fa1bed 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -75,6 +75,7 @@ "choose_complice": "Choose how you will play Pardner!", "choose_ricercato": "Choose who you will play Wanted against.", "choose_birra_function": "Choose between getting 1 gold nugget by discarding beer or if you want to play the beer.", + "choose_play_as_bang": "Choose which card to play as Bang!", "emporio_others": "{0} is choosing which card to get from the General Store", "you_died": "YOU DIED", "spectate": "SPECTATE", @@ -694,7 +695,7 @@ }, "Lemat": { "name": "Lemat", - "desc": "During your turn you can use any card as BANG!." + "desc": "During your turn you can use any card as BANG! (Click on the card once equipped)." }, "SerpenteASonagli": { "name": "Serpente a Sonagli", diff --git a/frontend/src/i18n/it.json b/frontend/src/i18n/it.json index 3dbbae0..05522c0 100644 --- a/frontend/src/i18n/it.json +++ b/frontend/src/i18n/it.json @@ -75,6 +75,7 @@ "choose_complice": "Scegli come giocare Complice!", "choose_ricercato": "Scegli il giocatore su cui vuoi giocare Ricercato", "choose_birra_function": "Scegli tra ottenere 1 pepita scartando la birra oppure giocare la birra.", + "choose_play_as_bang": "Scegli che carta giocare come Bang!", "emporio_others": "{0} sta scegliendo che carta prendere dall'emporio", "you_died": "SEI MORTO", "spectate": "SPETTATORE", @@ -694,7 +695,7 @@ }, "Lemat": { "name": "Lemat", - "desc": "Puoi usare ogni carta in mano come BANG!." + "desc": "Puoi usare ogni carta in mano come BANG! (Clicca la carta una volta equipaggiata)." }, "SerpenteASonagli": { "name": "Serpente a Sonagli", diff --git a/frontend/src/main.js b/frontend/src/main.js index 38ee88c..e2a945a 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -3,10 +3,17 @@ import App from './App.vue' Vue.config.productionTip = false import VueSocketIO from 'bang-vue-socket.io' -Vue.use(new VueSocketIO({ - debug: Vue.config.devtools, - connection: Vue.config.devtools ? `http://${window.location.hostname}:5001` : window.location.origin, -})) +if (window.localStorage.getItem('connect-dev')) { + Vue.use(new VueSocketIO({ + debug: true, + connection: window.localStorage.getItem('connect-dev'), + })) +} else { + Vue.use(new VueSocketIO({ + debug: Vue.config.devtools, + connection: (Vue.config.devtools) ? `http://${window.location.hostname}:5001` : window.location.origin, + })) +} import PrettyCheckbox from 'pretty-checkbox-vue'; Vue.use(PrettyCheckbox)