Merge branch 'main' into dev
This commit is contained in:
commit
31bdeb3436
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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),
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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':
|
||||
|
@ -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
|
||||
|
@ -6,9 +6,16 @@
|
||||
<div v-else class="center-stuff">
|
||||
<h2>{{$t("warning")}}</h2>
|
||||
<p>{{$t("connection_error")}}</p>
|
||||
<ul v-if="shouldShowBackendSuggestions">
|
||||
Connect to one of these backends:
|
||||
<li v-for="suggestion in backendSuggestions" :key="suggestion.name" @click="changeBackend(suggestion)">
|
||||
{{suggestion.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<help v-if="showHelp"/>
|
||||
<div style="position:fixed;bottom:4pt;right:4pt;display:flex;z-index:10">
|
||||
<input v-if="connect_dev" type=button class="btn" style="min-width:28pt;cursor:pointer;" @click="resetConnection" :value="'💚'+connect_dev" />
|
||||
<input type=button class="btn" style="min-width:28pt;cursor:pointer;" @click="()=>{sending_report = true}" :value=" $t('report') " />
|
||||
<input type="button" class="btn" value="" style="min-width:28pt;cursor:pointer;background-position:center;background-image:url('https://img.icons8.com/color/48/discord-logo.png');background-size:1.5em;background-repeat: no-repeat;" @click="joinDiscord"/>
|
||||
<input type="button" class="btn" :value="(showHelp?'X':'?')" style="min-width:28pt;border-radius:100%;cursor:pointer;" @click="getHelp"/>
|
||||
@ -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({
|
||||
|
@ -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,
|
||||
|
@ -61,7 +61,7 @@
|
||||
<div class="tiny-equipment">
|
||||
<Card v-for="(card, i) in p.equipment" v-bind:key="card.name+card.number"
|
||||
:card="card" @click.native="selectedInfo = p.equipment"
|
||||
:style="`margin-top: ${i<1?10:-(Math.min((p.equipment.length+p.gold_rush_equipment.length+1)*12,80))}pt`"/>
|
||||
:style="`margin-top: ${i < 1 ? 10 : -(Math.min((p.equipment.length + p.gold_rush_equipment.length + 1) * 12, 80))}pt;`"/>
|
||||
<Card v-for="(card, i) in p.gold_rush_equipment" v-bind:key="card.name+card.number"
|
||||
:card="card" @click.native="selectedInfo = p.gold_rush_equipment"
|
||||
:style="`margin-top: ${i+p.equipment.length<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 {
|
||||
|
@ -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)
|
||||
|
@ -3,13 +3,13 @@
|
||||
<p v-if="instruction && (lives > 0 || is_ghost)" class="center-stuff">{{instruction}}</p>
|
||||
<!-- <button v-if="canEndTurn" @click="end_turn">Termina Turno</button> -->
|
||||
<div class="equipment-slot">
|
||||
<Card v-if="my_role" :card="my_role" class="back"
|
||||
<Card v-if="my_role" :card="my_role" class="back" style="transform:rotate(-2deg)"
|
||||
@pointerenter.native="desc=($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" @pointerleave.native="desc=''"/>
|
||||
<Card v-if="character" :card="character" style="margin-left: -30pt;margin-right: 0pt;"
|
||||
@pointerenter.native="setDesc(character)" @pointerleave.native="desc=''"/>
|
||||
<transition-group name="list" tag="div" style="display: flex;flex-direction:column; justify-content: space-evenly; margin-left: 12pt;margin-right:-10pt;">
|
||||
<span v-for="(n, i) in lives" v-bind:key="i" :alt="i">❤️</span>
|
||||
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
||||
<span v-for="(n, i) in lives" v-bind:key="i" :alt="i">❤️</span>
|
||||
</transition-group>
|
||||
<div v-if="gold_nuggets > 0" style="display: flex;align-items: center;margin-left: 12pt;margin-right: -10pt;justify-content: space-evenly;width: 25pt;">
|
||||
<transition name="list">
|
||||
@ -17,7 +17,7 @@
|
||||
</transition>
|
||||
<span>💵️</span>
|
||||
</div>
|
||||
<transition-group v-if="lives > 0 || is_ghost" name="list" tag="div" style="margin: 0 0 0 10pt; display:flex;">
|
||||
<transition-group v-if="lives > 0 || is_ghost" name="list" id="equipment" tag="div" style="margin: 0 0 0 10pt; display:flex;">
|
||||
<Card v-for="card in equipmentComputed" v-bind:key="card.name+card.number" :card="card"
|
||||
@pointerenter.native="setDesc(card)" @pointerleave.native="desc=''"
|
||||
@click.native="play_card(card, true)" :class="{'cant-play':((eventCard && eventCard.name == 'Lazo') || (!card.can_be_used_now && !card.is_equipment))}"/>
|
||||
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user