From 17a3568ba507e174666d388d0bb78df00cf42d95 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 2 Feb 2023 12:41:58 +0000 Subject: [PATCH 01/13] bullet holes in card borders and less tidy equipments --- frontend/src/components/Card.vue | 13 +++++++++++++ frontend/src/components/Lobby.vue | 10 ++++++++-- frontend/src/components/Player.vue | 20 +++++++++++++++----- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Card.vue b/frontend/src/components/Card.vue index 552c035..6718b03 100644 --- a/frontend/src/components/Card.vue +++ b/frontend/src/components/Card.vue @@ -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/Player.vue b/frontend/src/components/Player.vue index 1e96ac7..bc106dc 100644 --- a/frontend/src/components/Player.vue +++ b/frontend/src/components/Player.vue @@ -3,13 +3,13 @@

{{instruction}}

- - ❤️ 💀 + ❤️
@@ -17,7 +17,7 @@ 💵️
- + @@ -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; From 1f175607290a6dca3061d61dfa5d71d27cdbf26a Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 4 Feb 2023 22:06:04 +0000 Subject: [PATCH 02/13] improve load handling --- backend/server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/server.py b/backend/server.py index 5382f0f..0d2d7b1 100644 --- a/backend/server.py +++ b/backend/server.py @@ -85,8 +85,9 @@ 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]) + open_lobbies = [g for g in games if and 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)]) Metrics.send_metric('online_players', points=[online_players]) @@ -244,7 +245,7 @@ def disconnect(sid): 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)}' + room_name += f'_{random.randint(0, 10000)}' sio.leave_room(sid, 'lobby') sio.enter_room(sid, room_name) g = Game(room_name) From 7de4fb1135b58a75ebbf7fece2971e6865544b59 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sat, 4 Feb 2023 22:12:17 +0000 Subject: [PATCH 03/13] fix typo --- backend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index 0d2d7b1..64c53ba 100644 --- a/backend/server.py +++ b/backend/server.py @@ -85,7 +85,7 @@ def bang_handler(func): return wrapper_func def advertise_lobbies(): - open_lobbies = [g for g in games if and 0 < len(g.players) < 10 and not g.is_hidden][-10:] + open_lobbies = [g for g in games 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)]) From 2418f7cc53bce008a9de9be319a5427d64786f45 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Sun, 5 Feb 2023 09:27:30 +0000 Subject: [PATCH 04/13] use dict instead of list --- backend/server.py | 55 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/backend/server.py b/backend/server.py index 64c53ba..82acc73 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,10 +85,10 @@ def bang_handler(func): return wrapper_func def advertise_lobbies(): - open_lobbies = [g for g in games if 0 < len(g.players) < 10 and not g.is_hidden][-10:] + 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)]) + 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 @@ -179,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: @@ -234,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]) @@ -244,7 +243,7 @@ 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)): + while room_name in games: room_name += f'_{random.randint(0, 10000)}' sio.leave_room(sid, 'lobby') sio.enter_room(sid, room_name) @@ -252,7 +251,7 @@ def create_room(sid, 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() @@ -283,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 @@ -325,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 @@ -340,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: @@ -798,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 @@ -831,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': From 3262580592ab7fb377676e8001a589e2bee3f0a1 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Wed, 8 Feb 2023 13:54:43 +0000 Subject: [PATCH 05/13] fix blackflower --- .../the_valley_of_shadows/characters.py | 4 ++-- .../valley_of_shadows_characters_test.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/backend/bang/expansions/the_valley_of_shadows/characters.py b/backend/bang/expansions/the_valley_of_shadows/characters.py index 56d4347..5cd1649 100644 --- a/backend/bang/expansions/the_valley_of_shadows/characters.py +++ b/backend/bang/expansions/the_valley_of_shadows/characters.py @@ -9,12 +9,12 @@ 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): 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.notify_self() 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 From 62ac700c5b8bd731bfc736c2661076f168a1ead0 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Wed, 8 Feb 2023 15:56:53 +0000 Subject: [PATCH 06/13] minor refactor --- backend/bang/cards.py | 15 ++++++++++----- backend/bang/players.py | 7 +++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 21542a8..5011c1b 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,6 +7,10 @@ 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 # ♣ @@ -64,7 +69,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 +103,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): diff --git a/backend/bang/players.py b/backend/bang/players.py index b78be96..891af98 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -50,7 +50,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, @@ -89,7 +89,7 @@ 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) + G.sio.start_background_task(self._get_avatar) self.game: g = None self.reset() @@ -187,7 +187,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 @@ -874,7 +873,7 @@ class Player: 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(cs.Suit.CLUBS, 42)) self.pending_action = PendingAction.PLAY self.notify_self() elif 'choose_tornado' in self.choose_text: From 84d4dcd98e9d58188d09e194e1950ae5c4e4439f Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 09:54:00 +0000 Subject: [PATCH 07/13] blackflower to choose_play_as_bang --- backend/bang/expansions/the_valley_of_shadows/characters.py | 2 +- backend/bang/game.py | 2 +- backend/bang/players.py | 2 +- frontend/src/i18n/en.json | 1 + frontend/src/i18n/it.json | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/bang/expansions/the_valley_of_shadows/characters.py b/backend/bang/expansions/the_valley_of_shadows/characters.py index 5cd1649..bc04ce8 100644 --- a/backend/bang/expansions/the_valley_of_shadows/characters.py +++ b/backend/bang/expansions/the_valley_of_shadows/characters.py @@ -16,7 +16,7 @@ class BlackFlower(Character): 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..76d6e3d 100644 --- a/backend/bang/game.py +++ b/backend/bang/game.py @@ -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 891af98..ebeac03 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -869,7 +869,7 @@ 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) diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 5d84e7c..a332cc1 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", diff --git a/frontend/src/i18n/it.json b/frontend/src/i18n/it.json index 3dbbae0..1fd2437 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", From 8d06cc2a77c974064ce09c31e49c6c6a986b6303 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 10:52:05 +0000 Subject: [PATCH 08/13] fix discord login errors --- backend/server.py | 2 +- frontend/src/components/Menu.vue | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/server.py b/backend/server.py index 82acc73..8017a2a 100644 --- a/backend/server.py +++ b/backend/server.py @@ -138,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: 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) From 621739546ec73e29de5ea269b8d836924d51105b Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 11:45:32 +0000 Subject: [PATCH 09/13] add lemat --- backend/bang/cards.py | 7 +++--- .../bang/expansions/gold_rush/shop_cards.py | 4 ++-- .../expansions/the_valley_of_shadows/cards.py | 23 ++++++++++++++++--- backend/bang/players.py | 8 +++++-- frontend/src/components/Card.vue | 2 +- frontend/src/components/Player.vue | 2 +- frontend/src/i18n/en.json | 2 +- frontend/src/i18n/it.json | 2 +- 8 files changed, 36 insertions(+), 14 deletions(-) diff --git a/backend/bang/cards.py b/backend/bang/cards.py index 5011c1b..98432f0 100644 --- a/backend/bang/cards.py +++ b/backend/bang/cards.py @@ -16,6 +16,7 @@ class Suit(IntEnum): CLUBS = 1 # ♣ HEARTS = 2 # ♥ SPADES = 3 # ♠ + GOLD = 4 # 🤑 class Card(ABC): @@ -52,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: @@ -231,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/expansions/gold_rush/shop_cards.py b/backend/bang/expansions/gold_rush/shop_cards.py index 5b4ef8f..9ed63b0 100644 --- a/backend/bang/expansions/gold_rush/shop_cards.py +++ b/backend/bang/expansions/gold_rush/shop_cards.py @@ -58,7 +58,7 @@ 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)] for i in range(len(player.available_cards)): player.available_cards[i].must_be_used = True player.choose_text = 'choose_bottiglia' @@ -73,7 +73,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..add61f7 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: + 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/players.py b/backend/bang/players.py index ebeac03..b6a7942 100644 --- a/backend/bang/players.py +++ b/backend/bang/players.py @@ -716,6 +716,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: @@ -743,7 +744,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: @@ -873,7 +877,7 @@ class Player: 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/frontend/src/components/Card.vue b/frontend/src/components/Card.vue index 6718b03..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; diff --git a/frontend/src/components/Player.vue b/frontend/src/components/Player.vue index bc106dc..685616e 100644 --- a/frontend/src/components/Player.vue +++ b/frontend/src/components/Player.vue @@ -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) diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index a332cc1..5fa1bed 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -695,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 1fd2437..05522c0 100644 --- a/frontend/src/i18n/it.json +++ b/frontend/src/i18n/it.json @@ -695,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", From cbe1945f5deea177a75c15d5319d95d487d39ec4 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 14:40:11 +0000 Subject: [PATCH 10/13] add possibility to use a different backend --- frontend/src/App.vue | 10 ++++++++++ frontend/src/main.js | 15 +++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b6f2df5..b464a74 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -9,6 +9,7 @@
+ @@ -63,6 +64,7 @@ export default { theme: 'light', report: '', sending_report: false, + connect_dev: undefined, }), computed: { }, @@ -98,6 +100,11 @@ export default { // Send message to SW to skip the waiting and activate the new SW this.registration.waiting.postMessage({ type: 'SKIP_WAITING' }) }, + resetConnection() { + this.$socket.disconnect(); + window.localStorage.removeItem('connect-dev'); + window.navigation.reload(); + }, detectColorScheme() { if(localStorage.getItem("theme")){ this.theme = localStorage.getItem("theme") @@ -146,6 +153,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/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) From 005ec8dd3cb816f51dc632f605a18b4d2427e0bf Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 15:41:30 +0000 Subject: [PATCH 11/13] add link for backend switching --- frontend/src/App.vue | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b464a74..ec30545 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,6 +6,12 @@

{{$t("warning")}}

{{$t("connection_error")}}

+
    + Connect to one of these backends: +
  • + {{suggestion.name}} +
  • +
@@ -65,8 +71,16 @@ export default { 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() { @@ -100,6 +114,11 @@ 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'); From d3f70454be6a9e6f043eb83a1f4ad143dedc7666 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 16:39:29 +0000 Subject: [PATCH 12/13] fix no players in range --- backend/bang/characters.py | 11 ++++++++--- backend/bang/expansions/gold_rush/shop_cards.py | 2 ++ .../bang/expansions/the_valley_of_shadows/cards.py | 2 +- .../expansions/the_valley_of_shadows/characters.py | 2 +- backend/bang/players.py | 9 ++++++--- 5 files changed, 18 insertions(+), 8 deletions(-) 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 9ed63b0..5ff68c9 100644 --- a/backend/bang/expansions/gold_rush/shop_cards.py +++ b/backend/bang/expansions/gold_rush/shop_cards.py @@ -59,6 +59,8 @@ class Bottiglia(ShopCard): def play_card(self, player, against=None, _with=None): # bang, birra, panico 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' diff --git a/backend/bang/expansions/the_valley_of_shadows/cards.py b/backend/bang/expansions/the_valley_of_shadows/cards.py index add61f7..ac742ee 100644 --- a/backend/bang/expansions/the_valley_of_shadows/cards.py +++ b/backend/bang/expansions/the_valley_of_shadows/cards.py @@ -44,7 +44,7 @@ class Lemat(Card): player.equipment.append(self) player.notify_self() return True - elif not player.has_played_bang: + 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 diff --git a/backend/bang/expansions/the_valley_of_shadows/characters.py b/backend/bang/expansions/the_valley_of_shadows/characters.py index bc04ce8..60e4ddf 100644 --- a/backend/bang/expansions/the_valley_of_shadows/characters.py +++ b/backend/bang/expansions/the_valley_of_shadows/characters.py @@ -11,7 +11,7 @@ class BlackFlower(Character): def special(self, player, data): #fiori = suit.Clubs 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.available_cards = [c for c in player.hand if c.suit == cs.Suit.CLUBS] player.special_use_count += 1 diff --git a/backend/bang/players.py b/backend/bang/players.py index b6a7942..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', @@ -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 @@ -90,7 +93,7 @@ class Player: self.avatar = robot_pictures[randrange(len(robot_pictures))] if self.discord_token: G.sio.start_background_task(self._get_avatar) - self.game: g = None + self.game: Game = None self.reset() def reset(self): From 840677f8394be6756b6cceab1f2b9fb7c68476e4 Mon Sep 17 00:00:00 2001 From: Alberto Xamin Date: Thu, 9 Feb 2023 16:40:45 +0000 Subject: [PATCH 13/13] make the valley of shadows generally available --- backend/bang/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/bang/game.py b/backend/bang/game.py index 76d6e3d..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