Merge branch 'main' into dev
This commit is contained in:
commit
5a309aeac5
48
.github/workflows/tests.yaml
vendored
48
.github/workflows/tests.yaml
vendored
@ -3,7 +3,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
buildx:
|
||||
build-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
@ -31,6 +31,24 @@ jobs:
|
||||
--output "type=image,push=false" \
|
||||
--tag albertoxamin/bang:test \
|
||||
--file ./Dockerfile ./
|
||||
build-arm64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v3
|
||||
-
|
||||
name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
-
|
||||
name: Docker Buildx (test build arm64)
|
||||
run: |
|
||||
@ -41,3 +59,31 @@ jobs:
|
||||
--output "type=image,push=false" \
|
||||
--tag albertoxamin/bang:test \
|
||||
--file ./Dockerfile ./
|
||||
build-armv-7:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v3
|
||||
-
|
||||
name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
-
|
||||
name: Docker Buildx (test build armv-7)
|
||||
run: |
|
||||
docker buildx build \
|
||||
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||
--platform linux/arm/v7 \
|
||||
--output "type=image,push=false" \
|
||||
--tag albertoxamin/bang:test \
|
||||
--file ./Dockerfile ./
|
||||
|
@ -216,7 +216,7 @@ class Bang(Card):
|
||||
def play_card(self, player, against, _with=None):
|
||||
if player.game.check_event(ceh.Sermone) and not self.number == 42: # 42 gold rush
|
||||
return False
|
||||
if ((player.has_played_bang and not self.number == 42) and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)) and against != None): # 42 gold rush:
|
||||
if ((player.has_played_bang and not self.number == 42) and (not any((isinstance(c, Volcanic) for c in player.equipment)) or player.game.check_event(ce.Lazo)) and against != None): # 42 gold rush:
|
||||
return False
|
||||
elif against != None:
|
||||
import bang.characters as chars
|
||||
@ -377,7 +377,7 @@ class Mancato(Card):
|
||||
def play_card(self, player, against, _with=None):
|
||||
import bang.characters as chars
|
||||
if against != None and player.character.check(player.game, chars.CalamityJanet):
|
||||
if player.has_played_bang and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)):
|
||||
if player.has_played_bang and (not any((isinstance(c, Volcanic) for c in player.equipment)) or player.game.check_event(ce.Lazo)):
|
||||
return False
|
||||
if player.game.check_event(ceh.Sermone):
|
||||
return False
|
||||
@ -445,7 +445,7 @@ class WellsFargo(Card):
|
||||
|
||||
|
||||
def get_starting_deck(expansions:List[str]) -> List[Card]:
|
||||
from bang.expansions import DodgeCity
|
||||
from bang.expansions import DodgeCity, TheValleyOfShadows
|
||||
base_cards = [
|
||||
Barile(Suit.SPADES, 'Q'),
|
||||
Barile(Suit.SPADES, 'K'),
|
||||
@ -530,5 +530,7 @@ def get_starting_deck(expansions:List[str]) -> List[Card]:
|
||||
]
|
||||
if 'dodge_city' in expansions:
|
||||
base_cards.extend(DodgeCity.get_cards())
|
||||
if 'the_valley_of_shadows' in expansions:
|
||||
base_cards.extend(TheValleyOfShadows.get_cards())
|
||||
return base_cards
|
||||
|
||||
|
@ -9,6 +9,7 @@ class Character(ABC):
|
||||
self.max_lives = max_lives
|
||||
self.sight_mod = sight_mod
|
||||
self.visibility_mod = visibility_mod
|
||||
self.is_character = True
|
||||
self.pick_mod = pick_mod
|
||||
self.desc = desc
|
||||
self.icon = '🤷♂️'
|
||||
@ -145,7 +146,7 @@ class WillyTheKid(Character):
|
||||
self.icon = '🎉'
|
||||
|
||||
def all_characters(expansions: List[str]):
|
||||
from bang.expansions import DodgeCity
|
||||
from bang.expansions import DodgeCity, TheValleyOfShadows
|
||||
base_chars = [
|
||||
BartCassidy(),
|
||||
BlackJack(),
|
||||
@ -168,4 +169,6 @@ def all_characters(expansions: List[str]):
|
||||
base_chars.extend(DodgeCity.get_characters())
|
||||
if 'gold_rush' in expansions:
|
||||
base_chars.extend(GoldRush.get_characters())
|
||||
if 'the_valley_of_shadows' in expansions:
|
||||
base_chars.extend(TheValleyOfShadows.get_characters())
|
||||
return base_chars
|
@ -52,7 +52,7 @@ class Deck:
|
||||
self.game.notify_event_card()
|
||||
|
||||
def fill_gold_rush_shop(self):
|
||||
if not any([c == None for c in self.shop_cards]):
|
||||
if not any((c == None for c in self.shop_cards)):
|
||||
return
|
||||
for i in range(3):
|
||||
if self.shop_cards[i] == None:
|
||||
|
@ -13,3 +13,12 @@ class GoldRush():
|
||||
def get_characters():
|
||||
from bang.expansions.gold_rush import characters
|
||||
return characters.all_characters()
|
||||
|
||||
class TheValleyOfShadows():
|
||||
def get_characters():
|
||||
from bang.expansions.the_valley_of_shadows import characters
|
||||
return characters.all_characters()
|
||||
|
||||
def get_cards():
|
||||
from bang.expansions.the_valley_of_shadows import cards
|
||||
return cards.get_starting_deck()
|
||||
|
@ -75,7 +75,7 @@ class Rissa(CatBalou):
|
||||
|
||||
def play_card(self, player, against, _with):
|
||||
if _with != None:
|
||||
if len([p for p in player.game.players if p != player and (len(p.hand)+len(p.equipment)) > 0]) == 0:
|
||||
if not any((p != player and (len(p.hand)+len(p.equipment)) > 0 for p in player.game.players)):
|
||||
return False
|
||||
#se sono qui vuol dire che ci sono giocatori con carte in mano oltre a me
|
||||
player.rissa_targets = []
|
||||
|
@ -90,9 +90,9 @@ class ChuckWengam(Character):
|
||||
if player.lives > 1 and player.is_my_turn:
|
||||
import bang.expansions.gold_rush.shop_cards as grc
|
||||
player.lives -= 1
|
||||
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in player.gold_rush_equipment)):
|
||||
player.gold_nuggets += 1
|
||||
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in player.gold_rush_equipment)):
|
||||
player.hand.append(player.game.deck.draw(True))
|
||||
player.hand.append(player.game.deck.draw(True))
|
||||
player.hand.append(player.game.deck.draw(True))
|
||||
|
231
backend/bang/expansions/the_valley_of_shadows/cards.py
Normal file
231
backend/bang/expansions/the_valley_of_shadows/cards.py
Normal file
@ -0,0 +1,231 @@
|
||||
from typing import List
|
||||
import bang.roles as r
|
||||
import bang.players as pl
|
||||
from bang.cards import Card, Suit, Bang, Mancato
|
||||
import bang.expansions.fistful_of_cards.card_events as ce
|
||||
|
||||
class Fantasma(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Fantasma', number, is_equipment=True)
|
||||
self.icon = '👻️' #porta in vita i giocatori morti ma non
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if (player.game.check_event(ce.IlGiudice)):
|
||||
return False
|
||||
if len(player.game.get_dead_players(include_ghosts=False)) > 0:
|
||||
player.pending_action = pl.PendingAction.CHOOSE
|
||||
player.choose_text = 'choose_fantasma'
|
||||
player.available_cards = [{
|
||||
'name': p.name,
|
||||
'icon': p.role.icon if(player.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||
'avatar': p.avatar,
|
||||
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
||||
'is_character': True,
|
||||
'noDesc': True
|
||||
} for p in player.game.get_dead_players(include_ghosts=False)]
|
||||
player.game.deck.scrap(self, True)
|
||||
return True
|
||||
return False
|
||||
|
||||
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
|
||||
#TODO
|
||||
|
||||
class SerpenteASonagli(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'SerpenteASonagli', number, is_equipment=True)
|
||||
self.need_target = True
|
||||
self.icon = '🐍️' # Ogni turno pesca se il seme picche -1hp
|
||||
self.alt_text = "♠️ =💔"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if (player.game.check_event(ce.IlGiudice)):
|
||||
return False
|
||||
if against != None:
|
||||
self.reset_card()
|
||||
player.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_play_card_against|{player.name}|{self.name}|{against}')
|
||||
player.game.get_player_named(against).equipment.append(self)
|
||||
player.game.get_player_named(against).notify_self()
|
||||
return True
|
||||
return False
|
||||
|
||||
class Shotgun(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Shotgun', number, is_equipment=True, is_weapon=True, range=1)
|
||||
self.icon = '🔫' # Ogni volta che colpisci un giocatore deve scartare una carta
|
||||
|
||||
class Taglia(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Taglia', number, is_equipment=True)
|
||||
self.need_target = True
|
||||
self.icon = '💰' # chiunque colpisca il giocatore con la taglia pesca una carta dal mazzo, si toglie solo con panico, cat balou, dalton
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if (player.game.check_event(ce.IlGiudice)):
|
||||
return False
|
||||
if against != None:
|
||||
self.reset_card()
|
||||
player.sio.emit('chat_message', room=player.game.name,
|
||||
data=f'_play_card_against|{player.name}|{self.name}|{against}')
|
||||
player.game.get_player_named(against).equipment.append(self)
|
||||
player.game.get_player_named(against).notify_self()
|
||||
return True
|
||||
return False
|
||||
|
||||
class UltimoGiro(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'UltimoGiro', number)
|
||||
self.icon = '🥂'
|
||||
# self.desc = 'Recupera 1 vita'
|
||||
# self.desc_eng = 'Regain 1 HP'
|
||||
self.alt_text = "🍺"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
super().play_card(player, against)
|
||||
player.lives = min(player.lives+1, player.max_lives)
|
||||
player.notify_self()
|
||||
return True
|
||||
|
||||
class Tomahawk(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Tomahawk', number, range=2)
|
||||
self.icon = '🪓️'
|
||||
self.alt_text = "2🔎 💥"
|
||||
# "Spara a un giocatore a distanza 2"
|
||||
self.need_target = True
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if against != None and player.game.can_card_reach(self, player, against):
|
||||
super().play_card(player, against=against)
|
||||
player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
class Tornado(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Tornado', number)
|
||||
self.icon = '🌪️'
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
player.game.discard_others(player, card_name=self.name)
|
||||
return True
|
||||
|
||||
class Sventagliata(Bang):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, number)
|
||||
self.name = 'Sventagliata'
|
||||
self.icon = '🎏'
|
||||
self.alt_text = "💥💥" # spara al target e anche, a uno a distanza 1 dal target
|
||||
self.need_target = True
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if against != None:
|
||||
#TODO
|
||||
# super().play_card(player, against=against)
|
||||
# player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
class Salvo(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Salvo', number)
|
||||
self.icon = '😇️'
|
||||
self.alt_text = "👤😇️"
|
||||
self.need_target = True
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if against != None:
|
||||
#TODO
|
||||
# super().play_card(player, against=against)
|
||||
# player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
class Mira(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Mira', number)
|
||||
self.icon = '👌🏻'
|
||||
self.alt_text = "💥🃏💔💔"
|
||||
self.need_target = True
|
||||
self.need_with = True
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
if against != None:
|
||||
#TODO
|
||||
# super().play_card(player, against=against)
|
||||
# player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
class Bandidos(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Bandidos', number)
|
||||
self.icon = '🤠️'
|
||||
self.alt_text = "👤🃏🃏/💔"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
player.game.discard_others(player, card_name=self.name)
|
||||
return True
|
||||
|
||||
class Fuga(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Fuga', number)
|
||||
self.icon = '🏃🏻'
|
||||
self.alt_text = "❌"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
#TODO
|
||||
# super().play_card(player, against=against)
|
||||
# player.game.attack(player, against)
|
||||
return True
|
||||
|
||||
class Poker(Card):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, 'Poker', number)
|
||||
self.icon = '🃏'
|
||||
self.alt_text = "👤🃏 🃏🃏"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
#TODO
|
||||
# super().play_card(player, against=against)
|
||||
# player.game.attack(player, against)
|
||||
return True
|
||||
|
||||
class RitornoDiFiamma(Mancato):
|
||||
def __init__(self, suit, number):
|
||||
super().__init__(suit, number)
|
||||
self.name = 'RitornoDiFiamma'
|
||||
self.icon = '🔥'
|
||||
self.alt_text = "😅 | 💥"
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
return False
|
||||
|
||||
def use_card(self, player):
|
||||
player.notify_self()
|
||||
|
||||
def get_starting_deck() -> List[Card]:
|
||||
cards = [
|
||||
Fantasma(Suit.SPADES, 9),
|
||||
Fantasma(Suit.SPADES, 10),
|
||||
# Lemat(Suit.DIAMONDS, 4),
|
||||
SerpenteASonagli(Suit.HEARTS, 7),
|
||||
Shotgun(Suit.SPADES, 'K'),
|
||||
Taglia(Suit.CLUBS, 9),
|
||||
UltimoGiro(Suit.DIAMONDS, 8),
|
||||
Tomahawk(Suit.DIAMONDS, 'A'),
|
||||
# Sventagliata(Suit.SPADES, 2),
|
||||
# Salvo(Suit.HEARTS, 5),
|
||||
Bandidos(Suit.DIAMONDS,'Q'), # gli altri giocatori scelgono se scartare 2 carte o perdere 1 punto vita
|
||||
# Fuga(Suit.HEARTS, 3), # evita l'effetto di carte marroni (tipo panico cat balou) di cui sei bersaglio
|
||||
# Mira(Suit.CLUBS, 6),
|
||||
# Poker(Suit.HEARTS, 'J'), # tutti gli altri scartano 1 carta a scelta, se non ci sono assi allora pesca 2 dal mazzo
|
||||
RitornoDiFiamma(Suit.CLUBS, 'Q'), # un mancato che fa bang
|
||||
Tornado(Suit.CLUBS, "A"),
|
||||
]
|
||||
for c in cards:
|
||||
c.expansion_icon = '👻️'
|
||||
return cards
|
10
backend/bang/expansions/the_valley_of_shadows/characters.py
Normal file
10
backend/bang/expansions/the_valley_of_shadows/characters.py
Normal file
@ -0,0 +1,10 @@
|
||||
from typing import List
|
||||
from bang.characters import Character
|
||||
|
||||
def all_characters() -> List[Character]:
|
||||
cards = [
|
||||
|
||||
]
|
||||
for c in cards:
|
||||
c.expansion_icon = '👻️'
|
||||
return cards
|
@ -5,6 +5,7 @@ import socketio
|
||||
import eventlet
|
||||
|
||||
import bang.players as pl
|
||||
import bang.cards as cs
|
||||
import bang.characters as characters
|
||||
import bang.expansions.dodge_city.characters as chd
|
||||
from bang.deck import Deck
|
||||
@ -15,6 +16,34 @@ import bang.expansions.gold_rush.shop_cards as grc
|
||||
import bang.expansions.gold_rush.characters as grch
|
||||
from metrics import Metrics
|
||||
|
||||
debug_commands = [
|
||||
{'cmd':'/debug', 'help':'Toggles the debug mode'},
|
||||
{'cmd':'/set_chars', 'help':'Set how many characters to distribute - sample /set_chars 3'},
|
||||
{'cmd':'/suicide', 'help':'Kills you'},
|
||||
{'cmd':'/nextevent', 'help':'Flip the next event card'},
|
||||
{'cmd':'/notify', 'help':'Send a message to a player - sample /notify player hi!'},
|
||||
{'cmd':'/show_cards', 'help':'View the hand of another - sample /show_cards player'},
|
||||
{'cmd':'/ddc', 'help':'Destroy all cards - sample /ddc player'},
|
||||
{'cmd':'/dsh', 'help':'Set health - sample /dsh player'},
|
||||
# {'cmd':'/togglebot', 'help':''},
|
||||
{'cmd':'/cancelgame', 'help':'Stops the current game'},
|
||||
{'cmd':'/startgame', 'help':'Force starts the game'},
|
||||
{'cmd':'/setbotspeed', 'help':'Changes the bot response time - sample /setbotspeed 0.5'},
|
||||
# {'cmd':'/addex', 'help':''},
|
||||
{'cmd':'/setcharacter', 'help':'Changes your current character - sample /setcharacter Willy The Kid'},
|
||||
{'cmd':'/setevent', 'help':'Changes the event deck - sample /setevent 0 Manette'},
|
||||
{'cmd':'/removecard', 'help':'Remove a card from hand/equip - sample /removecard 0'},
|
||||
{'cmd':'/getcard', 'help':'Get a brand new card - sample /getcard Birra'},
|
||||
{'cmd':'/meinfo', 'help':'Get player data'},
|
||||
{'cmd':'/gameinfo', 'help':'Get game data'},
|
||||
{'cmd':'/playerinfo', 'help':'Get player data - sample /playerinfo player'},
|
||||
{'cmd':'/cardinfo', 'help':'Get card data - sample /cardinfo handindex'},
|
||||
{'cmd':'/mebot', 'help':'Toggles bot mode'},
|
||||
{'cmd':'/getnuggets', 'help':'Adds nuggets to yourself - sample /getnuggets 5'},
|
||||
{'cmd':'/startwithseed', 'help':'start the game with custom seed'},
|
||||
{'cmd':'/getset', 'help':'get extension set of cards sample - /get valley', 'admin':True},
|
||||
]
|
||||
|
||||
class Game:
|
||||
def __init__(self, name, sio:socketio):
|
||||
super().__init__()
|
||||
@ -52,6 +81,11 @@ class Game:
|
||||
self.rpc_log = []
|
||||
self.is_replay = False
|
||||
|
||||
def shuffle_players(self):
|
||||
if not self.started:
|
||||
random.shuffle(self.players)
|
||||
self.notify_room()
|
||||
|
||||
def reset(self):
|
||||
for p in self.players:
|
||||
if (p.sid == p.name):
|
||||
@ -137,7 +171,7 @@ class Game:
|
||||
# chat_message(None, cmd[2], player)
|
||||
if i == fast_forward:
|
||||
self.replay_speed = 1.0
|
||||
|
||||
self.notify_room()
|
||||
eventlet.sleep(max(self.replay_speed, 0.1))
|
||||
eventlet.sleep(6)
|
||||
if self.is_replay:
|
||||
@ -145,11 +179,11 @@ class Game:
|
||||
|
||||
|
||||
def notify_room(self, sid=None):
|
||||
if len([p for p in self.players if p.character == None]) != 0 or sid:
|
||||
if any((p.character == None for p in self.players)) or sid:
|
||||
self.sio.emit('room', room=self.name if not sid else sid, data={
|
||||
'name': self.name,
|
||||
'started': self.started,
|
||||
'players': [{'name':p.name, 'ready': p.character != None, 'is_bot': p.is_bot} for p in self.players],
|
||||
'players': [{'name':p.name, 'ready': p.character != None, 'is_bot': p.is_bot, 'avatar': p.avatar} for p in self.players],
|
||||
'password': self.password,
|
||||
'is_competitive': self.is_competitive,
|
||||
'disconnect_bot': self.disconnect_bot,
|
||||
@ -158,30 +192,7 @@ class Game:
|
||||
})
|
||||
self.sio.emit('debug', room=self.name, data=self.debug)
|
||||
if self.debug:
|
||||
commands = [
|
||||
{'cmd':'/debug', 'help':'Toggles the debug mode'},
|
||||
{'cmd':'/set_chars', 'help':'Set how many characters to distribute - sample /set_chars 3'},
|
||||
{'cmd':'/suicide', 'help':'Kills you'},
|
||||
{'cmd':'/nextevent', 'help':'Flip the next event card'},
|
||||
{'cmd':'/notify', 'help':'Send a message to a player - sample /notify player hi!'},
|
||||
{'cmd':'/show_cards', 'help':'View the hand of another - sample /show_cards player'},
|
||||
{'cmd':'/ddc', 'help':'Destroy all cards - sample /ddc player'},
|
||||
{'cmd':'/dsh', 'help':'Set health - sample /dsh player'},
|
||||
# {'cmd':'/togglebot', 'help':''},
|
||||
{'cmd':'/cancelgame', 'help':'Stops the current game'},
|
||||
{'cmd':'/startgame', 'help':'Force starts the game'},
|
||||
{'cmd':'/setbotspeed', 'help':'Changes the bot response time - sample /setbotspeed 0.5'},
|
||||
# {'cmd':'/addex', 'help':''},
|
||||
{'cmd':'/setcharacter', 'help':'Changes your current character - sample /setcharacter Willy The Kid'},
|
||||
{'cmd':'/setevent', 'help':'Changes the event deck - sample /setevent 0 Manette'},
|
||||
{'cmd':'/removecard', 'help':'Remove a card from hand/equip - sample /removecard 0'},
|
||||
{'cmd':'/getcard', 'help':'Get a brand new card - sample /getcard Birra'},
|
||||
{'cmd':'/meinfo', 'help':'Get player data'},
|
||||
{'cmd':'/gameinfo', 'help':'Get game data'},
|
||||
{'cmd':'/mebot', 'help':'Toggles bot mode'},
|
||||
{'cmd':'/getnuggets', 'help':'Adds nuggets to yourself - sample /getnuggets 5'},
|
||||
{'cmd':'/startwithseed', 'help':'start the game with custom seed'}]
|
||||
self.sio.emit('commands', room=self.name, data=commands)
|
||||
self.sio.emit('commands', room=self.name, data=[x for x in debug_commands if 'admin' not in x])
|
||||
else:
|
||||
self.sio.emit('commands', room=self.name, data=[{'cmd':'/debug', 'help':'Toggles the debug mode'}])
|
||||
self.sio.emit('spectators', room=self.name, data=len(self.spectators))
|
||||
@ -203,6 +214,11 @@ class Game:
|
||||
self.disconnect_bot = not self.disconnect_bot
|
||||
self.notify_room()
|
||||
|
||||
def feature_flags(self):
|
||||
if 'the_valley_of_shadows' not in self.expansions:
|
||||
self.available_expansions.append('the_valley_of_shadows')
|
||||
self.notify_room()
|
||||
|
||||
def add_player(self, player: pl.Player):
|
||||
if player.is_bot and len(self.players) >= 8:
|
||||
return
|
||||
@ -212,6 +228,8 @@ class Game:
|
||||
if 'dodge_city' not in self.expansions:
|
||||
self.expansions.append('dodge_city')
|
||||
player.join_game(self)
|
||||
if player.is_admin():
|
||||
self.feature_flags()
|
||||
self.players.append(player)
|
||||
print(f'{self.name}: Added player {player.name} to game')
|
||||
self.notify_room()
|
||||
@ -231,7 +249,7 @@ class Game:
|
||||
|
||||
def notify_character_selection(self):
|
||||
self.notify_room()
|
||||
if len([p for p in self.players if p.character == None]) == 0:
|
||||
if not any((p.character == None for p in self.players)):
|
||||
for i in range(len(self.players)):
|
||||
print(self.name, self.players[i].name, self.players[i].character)
|
||||
self.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}')
|
||||
@ -303,6 +321,22 @@ class Game:
|
||||
self.players[i].notify_self()
|
||||
self.notify_event_card()
|
||||
|
||||
def discard_others(self, attacker: pl.Player, card_name:str=None):
|
||||
self.attack_in_progress = True
|
||||
attacker.pending_action = pl.PendingAction.WAIT
|
||||
attacker.notify_self()
|
||||
self.waiting_for = 0
|
||||
self.ready_count = 0
|
||||
for p in self.get_alive_players():
|
||||
if len(p.hand) > 0 and (p != attacker or card_name == 'Tornado'):
|
||||
if p.get_discarded(attacker=attacker, card_name=card_name):
|
||||
self.waiting_for += 1
|
||||
p.notify_self()
|
||||
if self.waiting_for == 0:
|
||||
attacker.pending_action = pl.PendingAction.PLAY
|
||||
attacker.notify_self()
|
||||
self.attack_in_progress = False
|
||||
|
||||
def attack_others(self, attacker: pl.Player, card_name:str=None):
|
||||
self.attack_in_progress = True
|
||||
attacker.pending_action = pl.PendingAction.WAIT
|
||||
@ -339,6 +373,11 @@ class Game:
|
||||
if self.pending_winners and not self.someone_won:
|
||||
return self.announces_winners()
|
||||
|
||||
def can_card_reach(self, card: cs.Card, player: pl.Player, target:str):
|
||||
if card and card.range != 0 and card.range < 99:
|
||||
return not any((True for p in self.get_visible_players(player) if p['name'] == target and p['dist'] > card.range))
|
||||
return True
|
||||
|
||||
def attack(self, attacker: pl.Player, target_username:str, double:bool=False, card_name:str=None):
|
||||
if self.get_player_named(target_username).get_banged(attacker=attacker, double=double, card_name=card_name):
|
||||
self.ready_count = 0
|
||||
@ -426,11 +465,11 @@ class Game:
|
||||
print(f'{self.name}: stop roulette')
|
||||
target_pl.lives -= 1
|
||||
target_pl.heal_if_needed()
|
||||
if len([c for c in target_pl.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in target_pl.gold_rush_equipment)):
|
||||
target_pl.gold_nuggets += 1
|
||||
if target_pl.character.check(self, grch.SimeonPicos):
|
||||
target_pl.gold_nuggets += 1
|
||||
if len([c for c in target_pl.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in target_pl.gold_rush_equipment)):
|
||||
target_pl.hand.append(self.deck.draw(True))
|
||||
target_pl.notify_self()
|
||||
self.is_russian_roulette_on = False
|
||||
@ -503,14 +542,14 @@ class Game:
|
||||
pl.hand.append(self.deck.draw())
|
||||
pl.hand.append(self.deck.draw())
|
||||
pl.notify_self()
|
||||
elif self.check_event(ceh.CittaFantasma):
|
||||
elif self.check_event(ceh.CittaFantasma) or self.players[self.turn].is_ghost:
|
||||
print(f'{self.name}: {self.players[self.turn]} is dead, event ghost')
|
||||
self.players[self.turn].is_ghost = True
|
||||
else:
|
||||
print(f'{self.name}: {self.players[self.turn]} is dead, next turn')
|
||||
return self.next_turn()
|
||||
self.player_bangs = 0
|
||||
if isinstance(self.players[self.turn].role, roles.Sheriff) or ((self.initial_players == 3 and isinstance(self.players[self.turn].role, roles.Vice) and not self.players[self.turn].is_ghost) or (self.initial_players == 3 and any([p for p in self.players if p.is_dead and p.role.name == 'Vice']) and isinstance(self.players[self.turn].role, roles.Renegade))):
|
||||
if isinstance(self.players[self.turn].role, roles.Sheriff) or ((self.initial_players == 3 and isinstance(self.players[self.turn].role, roles.Vice) and not self.players[self.turn].is_ghost) or (self.initial_players == 3 and any((p for p in self.players if p.is_dead and p.role.name == 'Vice')) and isinstance(self.players[self.turn].role, roles.Renegade))):
|
||||
self.deck.flip_event()
|
||||
if len(self.deck.event_cards) > 0 and self.deck.event_cards[0] != None:
|
||||
print(f'{self.name}: flip new event {self.deck.event_cards[0].name}')
|
||||
@ -600,9 +639,9 @@ class Game:
|
||||
player.game = None
|
||||
if self.disconnect_bot and self.started:
|
||||
player.is_bot = True
|
||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||
if not any((not p.is_bot for p in self.players)):
|
||||
eventlet.sleep(5)
|
||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||
if not any((not p.is_bot for p in self.players)):
|
||||
print(f'{self.name}: no players left in game, shutting down')
|
||||
self.shutting_down = True
|
||||
self.players = []
|
||||
@ -620,7 +659,7 @@ class Game:
|
||||
# else:
|
||||
# player.lives = 0
|
||||
# self.players.remove(player)
|
||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||
if not any((not p.is_bot for p in self.players)):
|
||||
print(f'{self.name}: no players left in game, shutting down')
|
||||
self.shutting_down = True
|
||||
self.players = []
|
||||
@ -637,7 +676,7 @@ class Game:
|
||||
if player.character and player.role:
|
||||
if not self.is_replay:
|
||||
Metrics.send_metric('player_death', points=[1], tags=[f"char:{player.character.name}", f"role:{player.role.name}"])
|
||||
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Ricercato)]) > 0 and player.attacker and player.attacker in self.players:
|
||||
if any((isinstance(c, grc.Ricercato) for c in player.gold_rush_equipment)) and player.attacker and player.attacker in self.players:
|
||||
player.attacker.gold_nuggets += 1
|
||||
player.attacker.hand.append(self.deck.draw(True))
|
||||
player.attacker.hand.append(self.deck.draw(True))
|
||||
@ -763,14 +802,15 @@ class Game:
|
||||
'is_ghost': pls[j].is_ghost,
|
||||
'is_bot': pls[j].is_bot,
|
||||
'icon': pls[j].role.icon if (pls[j].role is not None) else '🤠',
|
||||
'avatar': pls[j].avatar,
|
||||
'role': pls[j].role,
|
||||
} for j in range(len(pls)) if i != j]
|
||||
|
||||
def get_alive_players(self):
|
||||
return [p for p in self.players if not p.is_dead or p.is_ghost]
|
||||
|
||||
def get_dead_players(self):
|
||||
return [p for p in self.players if p.is_dead]
|
||||
def get_dead_players(self, include_ghosts=True):
|
||||
return [p for p in self.players if p.is_dead and (include_ghosts or not p.is_ghost)]
|
||||
|
||||
def notify_all(self):
|
||||
if self.started:
|
||||
@ -788,6 +828,7 @@ class Game:
|
||||
'character': p.character.__dict__ if p.character else None,
|
||||
'real_character': p.real_character.__dict__ if p.real_character else None,
|
||||
'icon': p.role.icon if self.initial_players == 3 and p.role else '🤠',
|
||||
'avatar': p.avatar,
|
||||
'is_ghost': p.is_ghost,
|
||||
'is_bot': p.is_bot,
|
||||
} for p in self.get_alive_players()]
|
||||
|
@ -12,10 +12,23 @@ import bang.expansions.fistful_of_cards.card_events as ce
|
||||
import bang.expansions.high_noon.card_events as ceh
|
||||
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 eventlet
|
||||
from typing import List
|
||||
from metrics import Metrics
|
||||
|
||||
robot_pictures = [
|
||||
'https://i.imgur.com/40rAFIb.jpg',
|
||||
'https://i.imgur.com/gG77VRR.jpg',
|
||||
'https://i.imgur.com/l2DTQeH.jpg',
|
||||
'https://i.imgur.com/aPM2gix.jpg',
|
||||
'https://i.imgur.com/ep5EB8c.jpg',
|
||||
'https://i.imgur.com/qsOWIsf.jpg',
|
||||
'https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/apple/325/robot_1f916.png',
|
||||
'https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/openmoji/338/robot_1f916.png',
|
||||
'https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/microsoft/319/robot_1f916.png',
|
||||
]
|
||||
|
||||
class PendingAction(IntEnum):
|
||||
PICK = 0
|
||||
DRAW = 1
|
||||
@ -26,13 +39,46 @@ class PendingAction(IntEnum):
|
||||
|
||||
class Player:
|
||||
|
||||
def __init__(self, name, sid, sio, bot=False):
|
||||
def is_admin(self):
|
||||
return self.discord_id in {'244893980960096266'}
|
||||
|
||||
def get_avatar(self):
|
||||
import requests
|
||||
headers = {
|
||||
'Authorization': 'Bearer ' + self.discord_token,
|
||||
}
|
||||
r = requests.get('https://discordapp.com/api/users/@me', headers=headers)
|
||||
if r.status_code == 200:
|
||||
res = r.json()
|
||||
self.avatar = f'https://cdn.discordapp.com/avatars/{res["id"]}/{res["avatar"]}.png'
|
||||
if self.game:
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_change_username|{self.name}|{res["username"]}')
|
||||
self.name = res['username']
|
||||
self.discord_id = res['id']
|
||||
if self.is_admin():
|
||||
if self.game: self.game.feature_flags()
|
||||
self.sio.emit('chat_message', room=self.sid, data={'color':'green', 'text':'(you are admin)'})
|
||||
if self.game:
|
||||
self.game.notify_room()
|
||||
self.sio.emit('me', data=self.name, room=self.sid)
|
||||
else:
|
||||
print('error getting avatar', r.status_code, r.text)
|
||||
print(r)
|
||||
|
||||
def __init__(self, name, sid, sio, bot=False, discord_token=None):
|
||||
import bang.game as g
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.sid = sid
|
||||
self.sio = sio
|
||||
self.is_bot = bot
|
||||
self.discord_token = discord_token
|
||||
self.discord_id = None
|
||||
self.avatar = ''
|
||||
if self.is_bot:
|
||||
self.avatar = robot_pictures[randrange(len(robot_pictures))]
|
||||
if self.discord_token:
|
||||
sio.start_background_task(self.get_avatar)
|
||||
self.game: g = None
|
||||
self.reset()
|
||||
|
||||
@ -163,6 +209,10 @@ class Player:
|
||||
self.sio.emit('notify_card', room=self.sid, data=mess)
|
||||
|
||||
def notify_self(self):
|
||||
if any((True for c in self.equipment if isinstance(c, tvosc.Fantasma))):
|
||||
self.is_ghost = True
|
||||
elif self.is_ghost and not self.game.check_event(ceh.CittaFantasma):
|
||||
self.is_ghost = False
|
||||
if self.is_ghost: self.lives = 0
|
||||
if self.pending_action == PendingAction.DRAW and self.game.check_event(ce.Peyote):
|
||||
self.available_cards = [{
|
||||
@ -187,7 +237,7 @@ class Player:
|
||||
self.hand.append(self.game.deck.draw(True))
|
||||
if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
|
||||
print('dying, attacker', self.attacker)
|
||||
if self.gold_nuggets >= 2 and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Zaino)]) > 0:
|
||||
if self.gold_nuggets >= 2 and any((isinstance(c, grc.Zaino) for c in self.gold_rush_equipment)):
|
||||
for i in range(len(self.gold_rush_equipment)):
|
||||
if isinstance(self.gold_rush_equipment[i], grc.Zaino):
|
||||
self.gold_rush_equipment[i].play_card(self, None)
|
||||
@ -202,11 +252,13 @@ class Player:
|
||||
self.choose_text = 'choose_sid_scrap'
|
||||
self.available_cards = self.hand
|
||||
self.lives += 1
|
||||
|
||||
ser = self.__dict__.copy()
|
||||
ser.pop('game')
|
||||
ser.pop('sio')
|
||||
ser.pop('sid')
|
||||
ser.pop('on_pick_cb')
|
||||
ser.pop('discord_token')
|
||||
ser.pop('on_failed_response_cb')
|
||||
ser.pop('attacker')
|
||||
ser.pop('rissa_targets')
|
||||
@ -214,7 +266,7 @@ class Player:
|
||||
ser['attacker'] = self.attacker.name
|
||||
ser['sight'] = self.get_sight()
|
||||
ser['sight_extra'] = self.get_sight(False) -1
|
||||
ser['can_gold_rush_discard'] = len([p for p in self.game.get_alive_players() if p != self and len([e for e in p.gold_rush_equipment if e.number <= self.gold_nuggets + 1]) > 0]) > 0
|
||||
ser['can_gold_rush_discard'] = any((p != self and any((e.number <= self.gold_nuggets + 1 for e in p.gold_rush_equipment)) for p in self.game.get_alive_players()))
|
||||
if self.character:
|
||||
ser['gold_rush_discount'] = 1 if self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1 else 0
|
||||
ser['lives'] = max(ser['lives'], 0)
|
||||
@ -223,14 +275,12 @@ class Player:
|
||||
self.pending_action = PendingAction.WAIT
|
||||
ser['hand'] = []
|
||||
ser['equipment'] = []
|
||||
self.sio.emit('self', room=self.sid, data=json.dumps(
|
||||
ser, default=lambda o: o.__dict__))
|
||||
self.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__))
|
||||
self.game.player_death(self)
|
||||
if self.game and self.game.started: # falso quando un bot viene eliminato dalla partita
|
||||
self.sio.emit('self_vis', room=self.sid, data=json.dumps(self.game.get_visible_players(self), default=lambda o: o.__dict__))
|
||||
self.game.notify_all()
|
||||
self.sio.emit('self', room=self.sid, data=json.dumps(
|
||||
ser, default=lambda o: o.__dict__))
|
||||
self.sio.emit('self', room=self.sid, data=json.dumps(ser, default=lambda o: o.__dict__))
|
||||
|
||||
def bot_spin(self):
|
||||
while self.is_bot and self.game != None and not self.game.shutting_down:
|
||||
@ -253,10 +303,10 @@ class Player:
|
||||
self.draw('')
|
||||
elif self.pending_action == PendingAction.PLAY:
|
||||
non_blocked_cards = [card for card in self.hand if (not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette)]
|
||||
equippables = [c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not any([type(c) == type(x) and not (c.is_weapon and c.must_be_used) for x in self.equipment])]
|
||||
equippables = [c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not c.need_target and not any((type(c) == type(x) and not (c.is_weapon and c.must_be_used) for x in self.equipment))]
|
||||
misc = [c for c in non_blocked_cards if not c.need_target and (isinstance(c, cs.WellsFargo) or isinstance(c, cs.Indiani) or isinstance(c, cs.Gatling) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or ((isinstance(c, cs.Birra) and self.lives < self.max_lives or c.must_be_used) and not self.game.check_event(ceh.IlReverendo)) or (c.need_with and len(self.hand) > 1 and not (isinstance(c, csd.Whisky) and self.lives == self.max_lives)))
|
||||
and not (not c.can_be_used_now and self.game.check_event(ce.IlGiudice)) and not c.is_equipment]
|
||||
need_target = [c for c in non_blocked_cards if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (type(c) == type(cs.Bang) and (self.game.check_event(ceh.Sermone) or (self.has_played_bang and (not any([isinstance(c, cs.Volcanic) for c in self.equipment]) or self.game.check_event(ce.Lazo))))) and not (isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice)) or isinstance(c, cs.Duello) or isinstance(c, cs.CatBalou) or isinstance(c, csd.Pugno)]
|
||||
need_target = [c for c in non_blocked_cards if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (type(c) == type(cs.Bang) and (self.game.check_event(ceh.Sermone) or (self.has_played_bang and (not any((isinstance(c, cs.Volcanic) for c in self.equipment)) or self.game.check_event(ce.Lazo))))) and not (isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice)) or isinstance(c, cs.Duello) or isinstance(c, cs.CatBalou) or isinstance(c, csd.Pugno)]
|
||||
green_cards = [c for c in self.equipment if not self.game.check_event(ce.Lazo) and not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now]
|
||||
if self.game.debug:
|
||||
print(f'hand: {self.hand}')
|
||||
@ -265,7 +315,7 @@ class Player:
|
||||
print(f'misc: {misc}')
|
||||
print(f'need_target: {need_target}')
|
||||
print(f'green_cards: {green_cards}')
|
||||
if self.gold_nuggets > 0 and any([c.number <= self.gold_nuggets for c in self.game.deck.shop_cards]):
|
||||
if self.gold_nuggets > 0 and any((c.number <= self.gold_nuggets for c in self.game.deck.shop_cards)):
|
||||
for i in range(len(self.game.deck.shop_cards)):
|
||||
if self.game.deck.shop_cards[i].number <= self.gold_nuggets:
|
||||
self.game.rpc_log.append(f'{self.name};buy_gold_rush_card;{i}')
|
||||
@ -273,6 +323,8 @@ class Player:
|
||||
return
|
||||
if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice):
|
||||
for c in equippables:
|
||||
if isinstance(c, tvosc.Fantasma) and len(self.game.get_dead_players(include_ghosts=False)) == 0:
|
||||
continue
|
||||
if self.play_card(self.hand.index(c)):
|
||||
return
|
||||
elif len(misc) > 0:
|
||||
@ -318,7 +370,7 @@ class Player:
|
||||
return
|
||||
break
|
||||
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
||||
if maxcards == self.lives and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Cinturone)]) > 0:
|
||||
if maxcards == self.lives and any((isinstance(c, grc.Cinturone) for c in self.gold_rush_equipment)):
|
||||
maxcards = 8
|
||||
if len(self.hand) > maxcards:
|
||||
self.game.rpc_log.append(f'{self.name};scrap;{0}')
|
||||
@ -376,19 +428,19 @@ class Player:
|
||||
data=f'_turn|{self.name}')
|
||||
print(f'{self.name}: I was notified that it is my turn')
|
||||
self.was_shot = False
|
||||
self.attacker = None
|
||||
self.is_my_turn = True
|
||||
self.is_waiting_for_action = True
|
||||
self.has_played_bang = False
|
||||
self.special_use_count = 0
|
||||
self.bang_used = 0
|
||||
if self.game.check_event(ceh.MezzogiornoDiFuoco):
|
||||
self.attacker = None
|
||||
self.lives -= 1
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)):
|
||||
self.gold_nuggets += 1
|
||||
if self.character.check(self.game, grch.SimeonPicos):
|
||||
self.gold_nuggets += 1
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw(True))
|
||||
if self.character.check(self.game, chars.BartCassidy) and self.lives > 0:
|
||||
self.hand.append(self.game.deck.draw(True))
|
||||
@ -398,11 +450,12 @@ class Player:
|
||||
return self.notify_self()
|
||||
|
||||
#non è un elif perchè vera custer deve fare questo poi cambiare personaggio
|
||||
if self.game.check_event(ce.FratelliDiSangue) and self.lives > 1 and not self.is_giving_life and len([p for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]):
|
||||
if self.game.check_event(ce.FratelliDiSangue) and self.lives > 1 and not self.is_giving_life and sum(p != self and p.lives < p.max_lives for p in self.game.get_alive_players()):
|
||||
self.available_cards = [{
|
||||
'name': p.name,
|
||||
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
||||
'avatar': p.avatar,
|
||||
'is_character': True,
|
||||
'noDesc': True
|
||||
} for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]
|
||||
@ -414,7 +467,7 @@ class Player:
|
||||
self.available_cards = [self.character, self.not_chosen_character]
|
||||
self.choose_text = 'choose_nuova_identita'
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
elif not self.game.check_event(ce.Lazo) and any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
elif not self.game.check_event(ce.Lazo) and any((isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) or isinstance(c, tvosc.SerpenteASonagli) for c in self.equipment)):
|
||||
self.is_giving_life = False
|
||||
self.pending_action = PendingAction.PICK
|
||||
else:
|
||||
@ -426,7 +479,7 @@ class Player:
|
||||
self.notify_self()
|
||||
|
||||
def draw(self, pile):
|
||||
if self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == 'event' and self.game.check_event(ce.Cecchino) and len([c for c in self.hand if c.name == cs.Bang(0,0).name]) >= 2:
|
||||
if self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == 'event' and self.game.check_event(ce.Cecchino) and sum((c.name == cs.Bang(0,0).name for c in self.hand)) >= 2:
|
||||
self.is_using_checchino = True
|
||||
self.available_cards = [{
|
||||
'name': p['name'],
|
||||
@ -439,11 +492,12 @@ class Player:
|
||||
self.choose_text = 'choose_cecchino'
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
self.notify_self()
|
||||
elif self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == 'event' and self.game.check_event(ce.Rimbalzo) and len([c for c in self.hand if c.name == cs.Bang(0,0).name]) > 0:
|
||||
elif self.is_my_turn and self.pending_action == PendingAction.PLAY and pile == 'event' and self.game.check_event(ce.Rimbalzo) and any((c.name == cs.Bang(0,0).name for c in self.hand)):
|
||||
self.available_cards = [{
|
||||
'name': p.name,
|
||||
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||
'is_character': True,
|
||||
'avatar': p.avatar,
|
||||
'noDesc': True
|
||||
} for p in self.game.get_alive_players() if len(p.equipment) > 0 and p != self]
|
||||
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||
@ -480,7 +534,7 @@ class Player:
|
||||
self.pending_action = PendingAction.PLAY
|
||||
num = 2 if not self.character.check(self.game, chd.BillNoface) else self.max_lives-self.lives+1
|
||||
if self.character.check(self.game, chd.PixiePete): num += 1
|
||||
if (len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0): num += 1
|
||||
if (any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment))): num += 1
|
||||
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.check_event(ceh.CittaFantasma)): num += 1
|
||||
elif self.game.check_event(ceh.Sete): num -= 1
|
||||
for i in range(num):
|
||||
@ -522,9 +576,9 @@ class Player:
|
||||
if self.pending_action != PendingAction.PICK:
|
||||
return
|
||||
pickable_cards = 1 + self.character.pick_mod
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||
if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)):
|
||||
pickable_cards += 1
|
||||
if self.is_my_turn:
|
||||
if self.is_my_turn and self.attacker == None:
|
||||
for i in range(len(self.equipment)):
|
||||
if i < len(self.equipment) and isinstance(self.equipment[i], cs.Dinamite):
|
||||
while pickable_cards > 0:
|
||||
@ -535,11 +589,11 @@ class Player:
|
||||
data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
||||
if picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0:
|
||||
self.lives -= 3
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)):
|
||||
self.gold_nuggets += 3
|
||||
if self.character.check(self.game, grch.SimeonPicos):
|
||||
self.gold_nuggets += 3
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.hand.append(self.game.deck.draw())
|
||||
@ -557,7 +611,7 @@ class Player:
|
||||
self.game.next_player().equipment.append(self.equipment.pop(i))
|
||||
self.game.next_player().notify_self()
|
||||
break
|
||||
if any([isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
if any((isinstance(c, cs.Dinamite) or isinstance(c, cs.Prigione) or isinstance(c, tvosc.SerpenteASonagli) for c in self.equipment)):
|
||||
self.notify_self()
|
||||
return
|
||||
for i in range(len(self.equipment)):
|
||||
@ -578,7 +632,21 @@ class Player:
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_prison_free|{self.name}')
|
||||
break
|
||||
break
|
||||
if any([isinstance(c, cs.Prigione) for c in self.equipment]):
|
||||
for i in range(len(self.equipment)):
|
||||
if isinstance(self.equipment[i], tvosc.SerpenteASonagli):
|
||||
while pickable_cards > 0:
|
||||
pickable_cards -= 1
|
||||
picked: cs.Card = self.game.deck.pick_and_scrap()
|
||||
print(f'Did pick {picked}')
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
||||
if not picked.check_suit(self.game, [cs.Suit.SPADES]):
|
||||
break
|
||||
elif pickable_cards == 0:
|
||||
self.lives -= 1
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_snake_bit|{self.name}')
|
||||
break
|
||||
if any((isinstance(c, cs.Prigione) for c in self.equipment)):
|
||||
self.notify_self()
|
||||
return
|
||||
if isinstance(self.real_character, chd.VeraCuster):
|
||||
@ -594,7 +662,7 @@ class Player:
|
||||
playable_cards = []
|
||||
for i in range(len(self.hand)):
|
||||
card = self.hand[i]
|
||||
if isinstance(card, cs.Bang) and self.has_played_bang and not any([isinstance(c, cs.Volcanic) for c in self.equipment]):
|
||||
if isinstance(card, cs.Bang) and self.has_played_bang and not any((isinstance(c, cs.Volcanic) for c in self.equipment)):
|
||||
continue
|
||||
elif isinstance(card, cs.Birra) and self.lives >= self.max_lives:
|
||||
continue
|
||||
@ -627,9 +695,9 @@ class Player:
|
||||
withCard = self.hand.pop(_with) if hand_index > _with else self.hand.pop(_with - 1)
|
||||
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
|
||||
did_play_card = False
|
||||
event_blocks_card = (self.game.check_event(ce.IlGiudice) and (card.is_equipment or (card.usable_next_turn and not card.can_be_used_now))) or (self.game.check_event(ce.Lazo) and card.usable_next_turn and card.can_be_used_now) or (self.game.check_event(ceh.Manette) and card.suit != self.committed_suit_manette and not (card.usable_next_turn and card.can_be_used_now))
|
||||
if not(against != None and (self.game.get_player_named(against).character.check(self.game, chd.ApacheKid) or len([c for c in self.game.get_player_named(against).gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0) and card.check_suit(self.game, [cs.Suit.DIAMONDS])) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) and not event_blocks_card:
|
||||
if against == self.name and not isinstance(card, csd.Tequila) and not isinstance(card, cs.Panico) and not isinstance(card, cs.CatBalou):
|
||||
event_blocks_card = (self.game.check_event(ce.IlGiudice) and (card.is_equipment or (card.usable_next_turn and not card.can_be_used_now))) or (self.game.check_event(ce.Lazo) and card.usable_next_turn and card.can_be_used_now) or ((self.game.check_event(ceh.Manette) and card.suit != self.committed_suit_manette) and not (card.usable_next_turn and card.can_be_used_now))
|
||||
if not(against != None and (self.game.get_player_named(against).character.check(self.game, chd.ApacheKid) or any((isinstance(c, grc.Calumet) for c in self.game.get_player_named(against).gold_rush_equipment))) and card.check_suit(self.game, [cs.Suit.DIAMONDS])) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) and not event_blocks_card:
|
||||
if (against == self.name and not isinstance(card, csd.Tequila) and not isinstance(card, cs.Panico) and not isinstance(card, cs.CatBalou)) or event_blocks_card:
|
||||
did_play_card = False
|
||||
else:
|
||||
did_play_card = card.play_card(self, against, withCard)
|
||||
@ -665,7 +733,7 @@ class Player:
|
||||
target = self.game.get_player_named(self.target_p)
|
||||
card = None
|
||||
if (target.name == self.name):
|
||||
card = self.equipment.pop(card_index)
|
||||
card = self.equipment.pop(card_index if card_index < len(target.hand) else card_index - len(target.hand))
|
||||
elif card_index >= len(target.hand):
|
||||
card = target.equipment.pop(card_index - len(target.hand))
|
||||
else:
|
||||
@ -688,7 +756,7 @@ class Player:
|
||||
elif self.choose_text == 'choose_ricercato':
|
||||
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||
player.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Ricercato|{player.name}')
|
||||
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Ricercato)]) > 0:
|
||||
if any((isinstance(c, grc.Ricercato) for c in player.gold_rush_equipment)):
|
||||
self.game.deck.shop_deck.append(grc.Ricercato())
|
||||
else:
|
||||
player.gold_rush_equipment.append(grc.Ricercato())
|
||||
@ -753,6 +821,34 @@ class Player:
|
||||
player.notify_self()
|
||||
self.pending_action = PendingAction.PLAY
|
||||
self.notify_self()
|
||||
elif 'choose_fantasma' in self.choose_text:
|
||||
if card_index <= len(self.available_cards):
|
||||
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||
player.equipment.append(self.game.deck.scrap_pile.pop(-1))
|
||||
player.notify_self()
|
||||
self.game.notify_all()
|
||||
self.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{player.name}|Fantasma|{player.name}')
|
||||
self.pending_action = PendingAction.PLAY
|
||||
self.notify_self()
|
||||
elif 'choose_tornado' in self.choose_text:
|
||||
if card_index <= len(self.available_cards):
|
||||
self.game.deck.scrap_pile.append(self.hand.pop(card_index))
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.pending_action = PendingAction.WAIT
|
||||
self.game.responders_did_respond_resume_turn()
|
||||
self.notify_self()
|
||||
elif 'choose_bandidos' in self.choose_text:
|
||||
if card_index <= len(self.available_cards):
|
||||
self.available_cards.pop(card_index)
|
||||
self.game.deck.scrap_pile.append(self.hand.pop(card_index))
|
||||
self.mancato_needed -= 1
|
||||
else:
|
||||
self.lives -= 1
|
||||
if self.mancato_needed <= 0:
|
||||
self.pending_action = PendingAction.WAIT
|
||||
self.game.responders_did_respond_resume_turn()
|
||||
self.notify_self()
|
||||
elif self.game.check_event(ceh.NuovaIdentita) and self.choose_text == 'choose_nuova_identita':
|
||||
if card_index == 1: # the other character
|
||||
self.character = self.not_chosen_character
|
||||
@ -771,11 +867,11 @@ class Player:
|
||||
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||
player.lives += 1
|
||||
self.lives -= 1
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)):
|
||||
self.gold_nuggets += 1
|
||||
if self.character.check(self.game, grch.SimeonPicos):
|
||||
self.gold_nuggets += 1
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
player.notify_self()
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_fratelli_sangue|{self.name}|{player.name}')
|
||||
@ -855,7 +951,7 @@ class Player:
|
||||
self.hand.append(card)
|
||||
pickable_stop = 1
|
||||
if self.game.check_event(ceh.Sete): pickable_stop += 1
|
||||
if self.game.check_event(ceh.IlTreno) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||
if self.game.check_event(ceh.IlTreno) or any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)):
|
||||
pickable_stop -= 1
|
||||
if len(self.available_cards) == pickable_stop:
|
||||
if len(self.available_cards) > 0: #la carta non scelta la rimettiamo in cima al mazzo
|
||||
@ -863,7 +959,7 @@ class Player:
|
||||
if len(self.available_cards) > 0: #se sono rimaste carte le scartiamo
|
||||
self.game.deck.scrap(self.available_cards.pop())
|
||||
#se c'è sia treno che piccone pesco un'altra carta
|
||||
if self.game.check_event(ceh.IlTreno) and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||
if self.game.check_event(ceh.IlTreno) and any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.is_drawing = False
|
||||
self.pending_action = PendingAction.PLAY
|
||||
@ -879,7 +975,7 @@ class Player:
|
||||
#legge del west non si applica perchè la seconda carta viene scartata
|
||||
if self.game.check_event(ceh.IlTreno):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||
if any((isinstance(c, grc.Piccone) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw())
|
||||
self.gold_nuggets += 1
|
||||
self.is_drawing = False
|
||||
@ -903,9 +999,9 @@ class Player:
|
||||
|
||||
def barrel_pick(self):
|
||||
pickable_cards = 1 + self.character.pick_mod
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||
if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)):
|
||||
pickable_cards += 1
|
||||
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
||||
if any((isinstance(c, cs.Barile) for c in self.equipment)) and self.character.check(self.game, chars.Jourdonnais):
|
||||
pickable_cards = 2
|
||||
while pickable_cards > 0:
|
||||
pickable_cards -= 1
|
||||
@ -919,8 +1015,8 @@ class Player:
|
||||
if self.mancato_needed <= 0:
|
||||
self.game.responders_did_respond_resume_turn(did_lose=False)
|
||||
return
|
||||
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Mancato) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente)]) == 0\
|
||||
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
|
||||
if not self.game.is_competitive and not any((isinstance(c, cs.Mancato) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente) for c in self.hand))\
|
||||
and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)):
|
||||
self.take_damage_response()
|
||||
self.game.responders_did_respond_resume_turn(did_lose=True)
|
||||
else:
|
||||
@ -933,9 +1029,9 @@ class Player:
|
||||
|
||||
def barrel_pick_no_dmg(self):
|
||||
pickable_cards = 1 + self.character.pick_mod
|
||||
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
||||
if any((isinstance(c, cs.Barile) for c in self.equipment)) and self.character.check(self.game, chars.Jourdonnais):
|
||||
pickable_cards = 2
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||
if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)):
|
||||
pickable_cards += 1
|
||||
while pickable_cards > 0:
|
||||
pickable_cards -= 1
|
||||
@ -949,8 +1045,8 @@ class Player:
|
||||
if self.mancato_needed <= 0:
|
||||
self.game.responders_did_respond_resume_turn(did_lose=False)
|
||||
return
|
||||
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Mancato) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente)]) == 0\
|
||||
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
|
||||
if not self.game.is_competitive and not any((isinstance(c, cs.Mancato) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente) for c in self.hand))\
|
||||
and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)):
|
||||
self.take_no_damage_response()
|
||||
self.game.responders_did_respond_resume_turn(did_lose=True)
|
||||
else:
|
||||
@ -961,6 +1057,17 @@ class Player:
|
||||
self.on_failed_response_cb = self.take_no_damage_response
|
||||
self.notify_self()
|
||||
|
||||
def get_discarded(self, attacker=None, card_name=None):
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
self.available_cards = self.hand.copy()
|
||||
if card_name == 'Tornado':
|
||||
self.choose_text = 'choose_tornado'
|
||||
if card_name == 'Bandidos':
|
||||
self.choose_text = 'choose_bandidos'
|
||||
self.mancato_needed = min(2, len(self.hand))
|
||||
self.available_cards.append({'name': '-1hp', 'icon': '💔', 'noDesc': True})
|
||||
return True
|
||||
|
||||
def get_banged(self, attacker, double=False, no_dmg=False, card_index=None, card_name=None):
|
||||
self.attacker = attacker
|
||||
self.attacking_card = card_name
|
||||
@ -973,9 +1080,9 @@ class Player:
|
||||
for i in range(len(self.equipment)):
|
||||
if self.equipment[i].can_be_used_now:
|
||||
print('usable', self.equipment[i])
|
||||
if not self.game.is_competitive and len([c for c in self.equipment if isinstance(c, cs.Barile)]) == 0 and not self.character.check(self.game, chars.Jourdonnais)\
|
||||
and len([c for c in self.hand if (isinstance(c, cs.Mancato) and c.can_be_used_now) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente)]) == 0\
|
||||
and len([c for c in self.equipment if c.can_be_used_now and isinstance(c, cs.Mancato)]) == 0:
|
||||
if not self.game.is_competitive and not any((isinstance(c, cs.Barile) for c in self.equipment)) and not self.character.check(self.game, chars.Jourdonnais)\
|
||||
and not any(((isinstance(c, cs.Mancato) and c.can_be_used_now) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente) for c in self.hand))\
|
||||
and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)):
|
||||
print('Cant defend')
|
||||
if not no_dmg:
|
||||
self.take_damage_response()
|
||||
@ -983,7 +1090,7 @@ class Player:
|
||||
self.take_no_damage_response()
|
||||
return False
|
||||
else:
|
||||
if ((not self.game.check_event(ce.Lazo) and len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0) \
|
||||
if ((not self.game.check_event(ce.Lazo) and any((isinstance(c, cs.Barile) for c in self.equipment))) \
|
||||
and not (self.game.players[self.game.turn].character.check(self.game, chd.BelleStar) and isinstance(attacker, Player))) \
|
||||
or self.character.check(self.game, chars.Jourdonnais): #se ho un barile e non c'è lazo e non mi sta attaccando Belle Star o se sono Jourdonnais
|
||||
print('has barrel')
|
||||
@ -1019,8 +1126,8 @@ class Player:
|
||||
def get_indians(self, attacker):
|
||||
self.attacker = attacker
|
||||
self.attacking_card = "Indiani!"
|
||||
if self.character.check(self.game, chd.ApacheKid) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0: return False
|
||||
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0:
|
||||
if self.character.check(self.game, chd.ApacheKid) or any((isinstance(c, grc.Calumet) for c in self.gold_rush_equipment)): return False
|
||||
if not self.game.is_competitive and not any((isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand)):
|
||||
print('Cant defend')
|
||||
self.take_damage_response()
|
||||
return False
|
||||
@ -1037,7 +1144,7 @@ class Player:
|
||||
def get_dueled(self, attacker):
|
||||
self.attacker = attacker
|
||||
self.attacking_card = "Duello"
|
||||
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0):
|
||||
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and not any((isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato)) for c in self.hand))):
|
||||
print('Cant defend')
|
||||
self.take_damage_response()
|
||||
self.game.responders_did_respond_resume_turn(did_lose=True)
|
||||
@ -1052,7 +1159,7 @@ class Player:
|
||||
return True
|
||||
|
||||
def heal_if_needed(self):
|
||||
while self.lives <= 0 and len(self.game.get_alive_players()) > 2 and len([c for c in self.hand if isinstance(c, cs.Birra)]) > 0 and not self.game.check_event(ceh.IlReverendo):
|
||||
while self.lives <= 0 and len(self.game.get_alive_players()) > 2 and any((isinstance(c, cs.Birra) for c in self.hand)) and not self.game.check_event(ceh.IlReverendo):
|
||||
for i in range(len(self.hand)):
|
||||
if isinstance(self.hand[i], cs.Birra):
|
||||
if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn:
|
||||
@ -1066,6 +1173,7 @@ class Player:
|
||||
|
||||
def take_damage_response(self):
|
||||
self.lives -= 1
|
||||
self.sio.emit('hurt', room=self.sid, data=f'')
|
||||
if self.lives > 0:
|
||||
if self.character.check(self.game, chars.BartCassidy):
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
@ -1077,15 +1185,24 @@ class Player:
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_special_el_gringo|{self.name}|{self.attacker.name}')
|
||||
self.attacker.notify_self()
|
||||
if self.attacker and any((isinstance(c, tvosc.Taglia) for c in self.equipment)):
|
||||
self.attacker.hand.append(self.game.deck.draw(True))
|
||||
self.sio.emit('chat_message', room=self.game.name,
|
||||
data=f'_taglia_reward|{self.name}|{self.attacker.name}')
|
||||
self.attacker.notify_self()
|
||||
if self.attacker and len(self.hand) > 0 and any((isinstance(c, tvosc.Shotgun) for c in self.attacker.equipment)):
|
||||
c = self.hand.pop(randrange(0, len(self.hand)))
|
||||
self.game.deck.scrap(c, True)
|
||||
self.sio.emit('chat_message', room=self.game.name, data=f'_shotgun_scrap|{self.name}|{c.name}')
|
||||
if self.attacker and 'gold_rush' in self.game.expansions:
|
||||
if (isinstance(self.attacker, Player)):
|
||||
self.attacker.gold_nuggets += 1
|
||||
self.attacker.notify_self()
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||
if any((isinstance(c, grc.Talismano) for c in self.gold_rush_equipment)):
|
||||
self.gold_nuggets += 1
|
||||
if self.character.check(self.game, grch.SimeonPicos):
|
||||
self.gold_nuggets += 1
|
||||
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||
if any((isinstance(c, grc.Stivali) for c in self.gold_rush_equipment)):
|
||||
self.hand.append(self.game.deck.draw(True))
|
||||
self.heal_if_needed()
|
||||
self.mancato_needed = 0
|
||||
@ -1137,7 +1254,15 @@ class Player:
|
||||
self.molly_discarded_cards = 0
|
||||
self.notify_self()
|
||||
self.game.responders_did_respond_resume_turn(did_lose=False)
|
||||
if isinstance(card, tvosc.RitornoDiFiamma):
|
||||
self.game.attack(self, self.attacker.name, card_name=card.name)
|
||||
self.event_type = ''
|
||||
elif not any(((isinstance(c, cs.Mancato) and c.can_be_used_now) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Bang)) or self.character.check(self.game, chd.ElenaFuente) for c in self.hand)) and not any((c.can_be_used_now and isinstance(c, cs.Mancato) for c in self.equipment)):
|
||||
self.on_failed_response_cb()
|
||||
if self.game:
|
||||
self.game.responders_did_respond_resume_turn(did_lose=True)
|
||||
if isinstance(card, tvosc.RitornoDiFiamma) and self.attacker:
|
||||
self.game.attack(self, self.attacker.name, card_name=card.name)
|
||||
else:
|
||||
self.pending_action = PendingAction.RESPOND
|
||||
self.notify_self()
|
||||
@ -1205,9 +1330,10 @@ class Player:
|
||||
'name': p.name,
|
||||
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||
'is_character': True,
|
||||
'avatar': p.avatar,
|
||||
'alt_text': ''.join(['🎴️'] * len(p.gold_rush_equipment)),
|
||||
'noDesc': True
|
||||
} for p in self.game.get_alive_players() if p != self and len([e for e in p.gold_rush_equipment if e.number + 1 <= self.gold_nuggets]) > 0]
|
||||
} for p in self.game.get_alive_players() if p != self and any((e.number + 1 <= self.gold_nuggets for e in p.gold_rush_equipment))]
|
||||
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||
self.choose_text = 'gold_rush_discard'
|
||||
self.pending_action = PendingAction.CHOOSE
|
||||
@ -1234,23 +1360,23 @@ class Player:
|
||||
if self.game.check_event(ce.LeggeDelWest) and len(must_be_used_cards) > 0:
|
||||
card = must_be_used_cards[0]
|
||||
print(f'Legge del west card: {card.name}')
|
||||
print(self.has_played_bang and not (any([isinstance(c, cs.Volcanic) for c in self.equipment]) and type(card) == type(cs.Bang)))
|
||||
if card.suit == cs.Suit.DIAMONDS and card.need_target and len([p for p in self.game.get_alive_players() if (not p.character.check(self.game, chd.ApacheKid) and not any([isinstance(c, grc.Calumet) for c in p.gold_rush_equipment]))]) == 0:
|
||||
print(self.has_played_bang and not (any((isinstance(c, cs.Volcanic) for c in self.equipment)) and type(card) == type(cs.Bang)))
|
||||
if card.suit == cs.Suit.DIAMONDS and card.need_target and not any(((not p.character.check(self.game, chd.ApacheKid) and not any((isinstance(c, grc.Calumet) for c in p.gold_rush_equipment))) for p in self.game.get_alive_players())):
|
||||
if isinstance(card, cs.Bang):
|
||||
return True
|
||||
else:
|
||||
return len(self.equipment) == 0 # se non ho carte equipaggiamento
|
||||
elif (isinstance(card, cs.Bang) or (isinstance(card, cs.Mancato) and self.character.check(self.game, chars.CalamityJanet))) and self.has_played_bang and not any([isinstance(c, cs.Volcanic) for c in self.equipment]) or len([p for p in self.game.get_visible_players(self) if self.get_sight() >= p['dist']]) == 0:
|
||||
elif (isinstance(card, cs.Bang) or (isinstance(card, cs.Mancato) and self.character.check(self.game, chars.CalamityJanet))) and self.has_played_bang and not any((isinstance(c, cs.Volcanic) for c in self.equipment)) or not any((self.get_sight() >= p['dist'] for p in self.game.get_visible_players(self))):
|
||||
return True
|
||||
elif isinstance(card, cs.Mancato) or (card.need_with and len(self.hand) < 2):
|
||||
return True
|
||||
elif isinstance(card, cs.Panico) and len([p for p in self.game.get_visible_players(self) if self.get_sight(False) >= p['dist']]) == 0 and len(self.equipment) == 0:
|
||||
elif isinstance(card, cs.Panico) and not any((self.get_sight(False) >= p['dist'] for p in self.game.get_visible_players(self))) and len(self.equipment) == 0:
|
||||
return True
|
||||
elif isinstance(card, csd.Pugno) and len([p for p in self.game.get_visible_players(self) if self.get_sight(False) >= p['dist']]) == 0:
|
||||
elif isinstance(card, csd.Pugno) and not any((self.get_sight(False) >= p['dist'] for p in self.game.get_visible_players(self))):
|
||||
return True
|
||||
elif isinstance(card, cs.Prigione) and len([p for p in self.game.get_visible_players(self) if not p['is_sheriff']]) == 0:
|
||||
elif isinstance(card, cs.Prigione) and not any((not p['is_sheriff'] for p in self.game.get_visible_players(self))):
|
||||
return True
|
||||
elif not card.is_weapon and len([c for c in self.equipment if c.name == card.name]) > 0:
|
||||
elif not card.is_weapon and any((c.name == card.name for c in self.equipment)):
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
@ -1260,7 +1386,7 @@ class Player:
|
||||
if not self.is_my_turn and not forced:
|
||||
return
|
||||
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
||||
if maxcards == self.lives and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Cinturone)]) > 0:
|
||||
if maxcards == self.lives and any((isinstance(c, grc.Cinturone) for c in self.gold_rush_equipment)):
|
||||
maxcards = 8
|
||||
if len(self.hand) > maxcards and not forced:
|
||||
print(f"{self.name}: I have to many cards in my hand and I can't end the turn")
|
||||
@ -1290,7 +1416,7 @@ class Player:
|
||||
self.play_turn(can_play_vendetta=False)
|
||||
return
|
||||
##Ghost##
|
||||
if self.is_dead and self.is_ghost and self.game.check_event(ceh.CittaFantasma):
|
||||
if self.is_dead and self.is_ghost and self.game.check_event(ceh.CittaFantasma) and not any((True for c in self.equipment if isinstance(c, tvosc.Fantasma))):
|
||||
self.is_ghost = False
|
||||
for i in range(len(self.hand)):
|
||||
self.game.deck.scrap(self.hand.pop(), True)
|
||||
|
@ -21,7 +21,7 @@ class Sheriff(Role):
|
||||
def on_player_death(self, alive_players: list, initial_players: int, dead_role=None, attacker_role=None):
|
||||
if initial_players == 3 and len(alive_players) == 1:
|
||||
return True
|
||||
elif initial_players != 3 and not any([isinstance(p.role, Outlaw) or isinstance(p.role, Renegade) for p in alive_players]):
|
||||
elif initial_players != 3 and not any((isinstance(p.role, Outlaw) or isinstance(p.role, Renegade) for p in alive_players)):
|
||||
print("The Sheriff won!")
|
||||
return True
|
||||
return False
|
||||
@ -42,7 +42,7 @@ class Vice(Role):
|
||||
return True
|
||||
elif initial_players == 3 and attacker_role != None:
|
||||
return isinstance(dead_role, Renegade) and isinstance(attacker_role, Vice)
|
||||
elif initial_players != 3 and not any([isinstance(p.role, Outlaw) or isinstance(p.role, Renegade) for p in alive_players]):
|
||||
elif initial_players != 3 and not any((isinstance(p.role, Outlaw) or isinstance(p.role, Renegade) for p in alive_players)):
|
||||
print("The Vice won!")
|
||||
return True
|
||||
return False
|
||||
@ -62,9 +62,9 @@ class Outlaw(Role):
|
||||
return True
|
||||
elif initial_players == 3 and attacker_role != None:
|
||||
return isinstance(dead_role, Vice) and isinstance(attacker_role, Outlaw)
|
||||
elif (initial_players != 3 and (not any([isinstance(p.role, Sheriff) for p in alive_players]))
|
||||
and (any([isinstance(p.role, Outlaw) for p in alive_players])
|
||||
or any([isinstance(p.role, Renegade) for p in alive_players]) and len(alive_players) > 1)):
|
||||
elif (initial_players != 3 and (not any((isinstance(p.role, Sheriff) for p in alive_players)))
|
||||
and (any((isinstance(p.role, Outlaw) for p in alive_players))
|
||||
or any((isinstance(p.role, Renegade) for p in alive_players)) and len(alive_players) > 1)):
|
||||
print("The Outlaw won!")
|
||||
return True
|
||||
return False
|
||||
|
@ -14,11 +14,16 @@ from discord_webhook import DiscordWebhook
|
||||
from metrics import Metrics
|
||||
from ddtrace import tracer
|
||||
|
||||
Metrics.init()
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
sys.setrecursionlimit(10**6) # this should prevents bots from stopping
|
||||
|
||||
import logging
|
||||
logging.basicConfig(filename='out.log', level='ERROR')
|
||||
from functools import wraps
|
||||
|
||||
Metrics.init()
|
||||
|
||||
sio = socketio.Server(cors_allowed_origins="*")
|
||||
|
||||
static_files={
|
||||
@ -45,13 +50,30 @@ games: List[Game] = []
|
||||
online_players = 0
|
||||
blacklist: List[str] = []
|
||||
|
||||
def send_to_debug(error):
|
||||
for g in games:
|
||||
if g.debug or any((p.is_admin() for p in g.players)):
|
||||
sio.emit('chat_message', room=g.name, data={'color': f'red','text':json.dumps({'ERROR':error}), 'type':'json'})
|
||||
|
||||
def bang_handler(func):
|
||||
@wraps(func)
|
||||
def wrapper_func(*args, **kwargs):
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
print(traceback.format_exc())
|
||||
send_to_debug(traceback.format_exc())
|
||||
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])
|
||||
Metrics.send_metric('lobbies', points=[len([g for g in games if not g.is_replay])])
|
||||
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])
|
||||
Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)])
|
||||
Metrics.send_metric('online_players', points=[online_players])
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def connect(sid, environ):
|
||||
global online_players
|
||||
online_players += 1
|
||||
@ -61,40 +83,46 @@ def connect(sid, environ):
|
||||
Metrics.send_metric('online_players', points=[online_players])
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_online_players(sid):
|
||||
global online_players
|
||||
sio.emit('players', room='lobby', data=online_players)
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def report(sid, text):
|
||||
print(f'New report from {sid}: {text}')
|
||||
ses: Player = sio.get_session(sid)
|
||||
data=''
|
||||
if hasattr(ses, 'game'):
|
||||
data = "\n".join(ses.game.rpc_log[:-1]).strip()
|
||||
data = data +"\n@@@\n" +text
|
||||
#print(data)
|
||||
response = requests.post("https://www.toptal.com/developers/hastebin/documents", data)
|
||||
response = requests.post("https://hastebin.com/documents", data.encode('utf-8'))
|
||||
key = json.loads(response.text).get('key')
|
||||
if "DISCORD_WEBHOOK" in os.environ and len(os.environ['DISCORD_WEBHOOK']) > 0:
|
||||
webhook = DiscordWebhook(url=os.environ['DISCORD_WEBHOOK'], content=f'New bug report, replay at https://www.toptal.com/developers/hastebin/{key}')
|
||||
webhook = DiscordWebhook(url=os.environ['DISCORD_WEBHOOK'], content=f'New bug report, replay at https://bang.xamin.it/game?replay={key}')
|
||||
response = webhook.execute()
|
||||
sio.emit('chat_message', room=sid, data={'color': f'green','text':f'Report OK'})
|
||||
else:
|
||||
print("WARNING: DISCORD_WEBHOOK not found")
|
||||
Metrics.send_event('BUG_REPORT', event_data=text)
|
||||
print(f'New bug report, replay at https://www.toptal.com/developers/hastebin/{key}')
|
||||
print(f'New bug report, replay at https://bang.xamin.it/game?replay={key}')
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def set_username(sid, username):
|
||||
ses = sio.get_session(sid)
|
||||
if not isinstance(ses, Player):
|
||||
sio.save_session(sid, Player(username, sid, sio))
|
||||
dt = username["discord_token"] if 'discord_token' in username else None
|
||||
sio.save_session(sid, Player(username["name"], sid, sio, discord_token=dt))
|
||||
print(f'{sid} is now {username}')
|
||||
advertise_lobbies()
|
||||
elif ses.game == None or not ses.game.started:
|
||||
username = username["name"]
|
||||
print(f'{sid} changed username to {username}')
|
||||
prev = ses.name
|
||||
if len([p for p in ses.game.players if p.name == username]) > 0:
|
||||
if ses.game and any((p.name == username for p in ses.game.players)):
|
||||
ses.name = f"{username}_{random.randint(0,100)}"
|
||||
else:
|
||||
ses.name = username
|
||||
@ -104,19 +132,34 @@ def set_username(sid, username):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_me(sid, room):
|
||||
if isinstance(sio.get_session(sid), Player):
|
||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||
if sio.get_session(sid).game:
|
||||
sio.get_session(sid).game.notify_room()
|
||||
else:
|
||||
sio.save_session(sid, Player('player', sid, sio))
|
||||
dt = room["discord_token"] if 'discord_token' in room else None
|
||||
sio.save_session(sid, Player('player', sid, sio, discord_token=dt))
|
||||
if 'replay' in room and room['replay'] != None:
|
||||
create_room(sid, room['replay'])
|
||||
sid = sio.get_session(sid)
|
||||
sid.game.is_hidden = True
|
||||
eventlet.sleep(0.5)
|
||||
response = requests.get(f"https://hastebin.com/raw/{room['replay']}")
|
||||
if response.status_code != 200:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'green','text':f'Invalid replay code'})
|
||||
return
|
||||
log = response.text.splitlines()
|
||||
sid.game.spectators.append(sid)
|
||||
sid.game.replay(log)
|
||||
return
|
||||
de_games = [g for g in games if g.name == room['name']]
|
||||
if len(de_games) == 1 and not de_games[0].started:
|
||||
join_room(sid, room)
|
||||
elif len(de_games) == 1 and de_games[0].started:
|
||||
print('room exists')
|
||||
if room['username'] != None and any([p.name == room['username'] for p in de_games[0].players if p.is_bot]):
|
||||
if room['username'] != None and any((p.name == room['username'] for p in de_games[0].players if (p.is_bot or (dt != None and p.discord_token == dt)))):
|
||||
print('getting inside the bot')
|
||||
bot = [p for p in de_games[0].players if p.is_bot and p.name == room['username'] ][0]
|
||||
bot.sid = sid
|
||||
@ -126,6 +169,7 @@ def get_me(sid, room):
|
||||
de_games[0].notify_room(sid)
|
||||
eventlet.sleep(0.1)
|
||||
de_games[0].notify_all()
|
||||
de_games[0].notify_scrap_pile(sid)
|
||||
sio.emit('role', room=sid, data=json.dumps(bot.role, default=lambda o: o.__dict__))
|
||||
bot.notify_self()
|
||||
if len(bot.available_characters) > 0:
|
||||
@ -135,6 +179,8 @@ def get_me(sid, room):
|
||||
sio.get_session(sid).game = de_games[0]
|
||||
sio.enter_room(sid, de_games[0].name)
|
||||
de_games[0].notify_room(sid)
|
||||
eventlet.sleep(0.1)
|
||||
|
||||
de_games[0].notify_event_card(sid)
|
||||
de_games[0].notify_scrap_pile(sid)
|
||||
de_games[0].notify_all()
|
||||
@ -146,7 +192,7 @@ def get_me(sid, room):
|
||||
sio.emit('me', data={'error':'Wrong password/Cannot connect'}, room=sid)
|
||||
else:
|
||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||
if room['username'] == None or any([p.name == room['username'] for p in sio.get_session(sid).game.players]):
|
||||
if room['username'] == None or any((p.name == room['username'] for p in sio.get_session(sid).game.players)):
|
||||
sio.emit('change_username', room=sid)
|
||||
else:
|
||||
sio.emit('chat_message', room=sio.get_session(sid).game.name, data=f"_change_username|{sio.get_session(sid).name}|{room['username']}")
|
||||
@ -157,6 +203,7 @@ def get_me(sid, room):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def disconnect(sid):
|
||||
global online_players
|
||||
online_players -= 1
|
||||
@ -164,6 +211,7 @@ def disconnect(sid):
|
||||
sio.emit('players', room='lobby', data=online_players)
|
||||
if sio.get_session(sid).game and sio.get_session(sid).disconnect():
|
||||
sio.close_room(sio.get_session(sid).game.name)
|
||||
if sio.get_session(sid).game in games:
|
||||
games.pop(games.index(sio.get_session(sid).game))
|
||||
print('disconnect ', sid)
|
||||
advertise_lobbies()
|
||||
@ -171,9 +219,10 @@ def disconnect(sid):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def create_room(sid, room_name):
|
||||
if sio.get_session(sid).game == None:
|
||||
while len([g for g in games if g.name == room_name]):
|
||||
while any((g.name == room_name for g in games)):
|
||||
room_name += f'_{random.randint(0,100)}'
|
||||
sio.leave_room(sid, 'lobby')
|
||||
sio.enter_room(sid, room_name)
|
||||
@ -186,26 +235,31 @@ def create_room(sid, room_name):
|
||||
advertise_lobbies()
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def private(sid):
|
||||
g = sio.get_session(sid).game
|
||||
g.set_private()
|
||||
advertise_lobbies()
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def toggle_expansion(sid, expansion_name):
|
||||
g = sio.get_session(sid).game
|
||||
g.toggle_expansion(expansion_name)
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def toggle_comp(sid):
|
||||
sio.get_session(sid).game.toggle_competitive()
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def toggle_replace_with_bot(sid):
|
||||
sio.get_session(sid).game.toggle_disconnect_bot()
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def join_room(sid, room):
|
||||
room_name = room['name']
|
||||
i = [g.name for g in games].index(room_name)
|
||||
@ -215,7 +269,7 @@ def join_room(sid, room):
|
||||
print(f'{sid} joined a room named {room_name}')
|
||||
sio.leave_room(sid, 'lobby')
|
||||
sio.enter_room(sid, room_name)
|
||||
while len([p for p in games[i].players if p.name == sio.get_session(sid).name]):
|
||||
while any((p.name == sio.get_session(sid).name and not p.is_bot for p in games[i].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))
|
||||
@ -236,8 +290,9 @@ Sockets for the status page
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_all_rooms(sid, deploy_key):
|
||||
if 'DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']:
|
||||
if ('DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin():
|
||||
sio.emit('all_rooms', room=sid, data=[{
|
||||
'name': g.name,
|
||||
'hidden': g.is_hidden,
|
||||
@ -252,13 +307,15 @@ def get_all_rooms(sid, deploy_key):
|
||||
} for g in games])
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def kick(sid, data):
|
||||
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
||||
if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin():
|
||||
sio.emit('kicked', room=data['sid'])
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def hide_toogle(sid, data):
|
||||
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
||||
if ('DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']) or sio.get_session(sid).is_admin():
|
||||
game = [g for g in games if g.name==data['room']]
|
||||
if len(games) > 0:
|
||||
game[0].is_hidden = not game[0].is_hidden
|
||||
@ -274,6 +331,7 @@ Sockets for the game
|
||||
"""
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def start_game(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.start_game()
|
||||
@ -281,6 +339,13 @@ def start_game(sid):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def shuffle_players(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.shuffle_players()
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def set_character(sid, name):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};set_character;{name}')
|
||||
@ -289,12 +354,14 @@ def set_character(sid, name):
|
||||
ses.set_character(name)
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def refresh(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.notify_self()
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def draw(sid, pile):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};draw;{pile}')
|
||||
@ -302,6 +369,7 @@ def draw(sid, pile):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def pick(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};pick')
|
||||
@ -309,6 +377,7 @@ def pick(sid):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def end_turn(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};end_turn')
|
||||
@ -316,6 +385,7 @@ def end_turn(sid):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def play_card(sid, data):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};play_card;{json.dumps(data)}')
|
||||
@ -323,6 +393,7 @@ def play_card(sid, data):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def respond(sid, card_index):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};respond;{card_index}')
|
||||
@ -330,6 +401,7 @@ def respond(sid, card_index):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def choose(sid, card_index):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};choose;{card_index}')
|
||||
@ -337,6 +409,7 @@ def choose(sid, card_index):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def scrap(sid, card_index):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};scrap;{card_index}')
|
||||
@ -351,6 +424,7 @@ def special(sid, data):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def gold_rush_discard(sid):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};gold_rush_discard;')
|
||||
@ -358,6 +432,7 @@ def gold_rush_discard(sid):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def buy_gold_rush_card(sid, data:int):
|
||||
ses: Player = sio.get_session(sid)
|
||||
ses.game.rpc_log.append(f'{ses.name};buy_gold_rush_card;{data}')
|
||||
@ -365,6 +440,7 @@ def buy_gold_rush_card(sid, data:int):
|
||||
|
||||
@tracer.wrap
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def chat_message(sid, msg, pl=None):
|
||||
ses: Player = sio.get_session(sid) if pl is None else pl
|
||||
ses.game.rpc_log.append(f'{ses.name};chat_message;{msg}')
|
||||
@ -379,7 +455,7 @@ def chat_message(sid, msg, pl=None):
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'Only 1 bot at the time'})
|
||||
else:
|
||||
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
||||
while any([p for p in ses.game.players if p.name == bot.name]):
|
||||
while any((p for p in ses.game.players if p.name == bot.name)):
|
||||
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
||||
ses.game.add_player(bot)
|
||||
bot.bot_spin()
|
||||
@ -388,7 +464,7 @@ def chat_message(sid, msg, pl=None):
|
||||
_cmd = msg.split()
|
||||
if len(_cmd) >= 2:
|
||||
replay_id = _cmd[1]
|
||||
response = requests.get(f"https://www.toptal.com/developers/hastebin/raw/{replay_id}")
|
||||
response = requests.get(f"https://hastebin.com/raw/{replay_id}")
|
||||
log = response.text.splitlines()
|
||||
ses.game.spectators.append(ses)
|
||||
if len(_cmd) == 2:
|
||||
@ -407,7 +483,7 @@ def chat_message(sid, msg, pl=None):
|
||||
ses.game.start_game(int(msg.split()[1]))
|
||||
return
|
||||
elif '/removebot' in msg and not ses.game.started:
|
||||
if any([p.is_bot for p in ses.game.players]):
|
||||
if any((p.is_bot for p in ses.game.players)):
|
||||
[p for p in ses.game.players if p.is_bot][-1].disconnect()
|
||||
return
|
||||
elif '/togglecomp' in msg and ses.game:
|
||||
@ -418,13 +494,13 @@ def chat_message(sid, msg, pl=None):
|
||||
if len(cmd) == 2 and 'DEPLOY_KEY' in os.environ and cmd[1] == os.environ['DEPLOY_KEY']: # solo chi ha la deploy key può attivare la modalità debug
|
||||
ses.game.debug = not ses.game.debug
|
||||
ses.game.notify_room()
|
||||
elif ses == ses.game.players[0]: # solo l'owner può attivare la modalità debug
|
||||
elif ses == ses.game.players[0] or ses.is_admin(): # solo l'owner può attivare la modalità debug
|
||||
ses.game.debug = not ses.game.debug
|
||||
ses.game.notify_room()
|
||||
if ses.game.debug:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'red','text':f'debug mode is now active, only the owner of the room can disable it with /debug'})
|
||||
return
|
||||
if not ses.game.debug:
|
||||
if not ses.game.debug and not ses.is_admin():
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'debug mode is not active, only the owner of the room can enable it with /debug'})
|
||||
elif '/set_chars' in msg and not ses.game.started:
|
||||
cmd = msg.split()
|
||||
@ -544,6 +620,27 @@ def chat_message(sid, msg, pl=None):
|
||||
for cn in card_names:
|
||||
ses.hand.append([c for c in cards if c.name.lower() == cn.lower() or c.name[0:-1].lower() == cn.lower()][0])
|
||||
ses.notify_self()
|
||||
elif '/equipcard' in msg:
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'})
|
||||
import bang.cards as cs
|
||||
cmd = msg.split()
|
||||
if len(cmd) >= 2:
|
||||
cards = cs.get_starting_deck(ses.game.expansions)
|
||||
card_names = ' '.join(cmd[1:]).split(',')
|
||||
for cn in card_names:
|
||||
ses.equipment.append([c for c in cards if c.name.lower() == cn.lower() or c.name[0:-1].lower() == cn.lower()][0])
|
||||
ses.notify_self()
|
||||
elif '/getset' in msg:
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'})
|
||||
cmd = msg.split()
|
||||
if len(cmd) >= 2:
|
||||
from bang.expansions import DodgeCity, TheValleyOfShadows
|
||||
if cmd[1] == 'dodgecity':
|
||||
ses.hand = DodgeCity.get_cards()
|
||||
ses.notify_self()
|
||||
elif 'valley' in cmd[1].lower():
|
||||
ses.hand = TheValleyOfShadows.get_cards()
|
||||
ses.notify_self()
|
||||
elif '/getnuggets' in msg:
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got nuggets'})
|
||||
import bang.cards as cs
|
||||
@ -552,16 +649,26 @@ def chat_message(sid, msg, pl=None):
|
||||
ses.gold_nuggets += int(cmd[1])
|
||||
ses.notify_self()
|
||||
elif '/gameinfo' in msg:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {dict(filter(lambda x:x[0] != "rpc_log",ses.game.__dict__.items()))}'})
|
||||
sio.emit('chat_message', room=sid, data={'color': f'', 'text':json.dumps(ses.game.__dict__, default=lambda o: f'<{o.__class__.__name__}() not serializable>'), 'type': 'json'})
|
||||
elif '/status' in msg and ses.is_admin():
|
||||
sio.emit('mount_status', room=sid)
|
||||
elif '/meinfo' in msg:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'})
|
||||
sio.emit('chat_message', room=sid, data={'color': f'', 'text':json.dumps(ses.__dict__, default=lambda o: f'<{o.__class__.__name__}() not serializable>'), 'type': 'json'})
|
||||
elif '/playerinfo' in msg:
|
||||
cmd = msg.split()
|
||||
if len(cmd) == 2:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'', 'text':json.dumps(ses.game.get_player_named(cmd[1]).__dict__, default=lambda o: f'<{o.__class__.__name__}() not serializable>'), 'type': 'json'})
|
||||
elif '/cardinfo' in msg:
|
||||
cmd = msg.split()
|
||||
if len(cmd) == 2:
|
||||
sio.emit('chat_message', room=sid, data={'color': f'', 'text':json.dumps(ses.hand[int(cmd[1])].__dict__, default=lambda o: f'<{o.__class__.__name__}() not serializable>'), 'type': 'json'})
|
||||
elif '/mebot' in msg:
|
||||
ses.is_bot = not ses.is_bot
|
||||
if (ses.is_bot):
|
||||
ses.was_player = True
|
||||
ses.bot_spin()
|
||||
elif '/arcadekick' in msg and ses.game.started:
|
||||
if len([p for p in ses.game.players if p.pending_action != PendingAction.WAIT]) == 0:
|
||||
if not any((p.pending_action != PendingAction.WAIT for p in ses.game.players)):
|
||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'','text':f'KICKING THE ARCADE CABINET'})
|
||||
ses.game.next_turn()
|
||||
else:
|
||||
@ -582,6 +689,7 @@ Sockets for the help screen
|
||||
"""
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_cards(sid):
|
||||
import bang.cards as c
|
||||
cards = c.get_starting_deck(['dodge_city'])
|
||||
@ -594,12 +702,14 @@ def get_cards(sid):
|
||||
Metrics.send_metric('help_screen_viewed', points=[1])
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_characters(sid):
|
||||
import bang.characters as ch
|
||||
cards = ch.all_characters(['dodge_city', 'gold_rush'])
|
||||
sio.emit('characters_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_highnooncards(sid):
|
||||
import bang.expansions.high_noon.card_events as ceh
|
||||
chs = []
|
||||
@ -608,6 +718,7 @@ def get_highnooncards(sid):
|
||||
sio.emit('highnooncards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_foccards(sid):
|
||||
import bang.expansions.fistful_of_cards.card_events as ce
|
||||
chs = []
|
||||
@ -616,6 +727,7 @@ def get_foccards(sid):
|
||||
sio.emit('foccards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_goldrushcards(sid):
|
||||
import bang.expansions.gold_rush.shop_cards as grc
|
||||
cards = grc.get_cards()
|
||||
@ -626,9 +738,34 @@ def get_goldrushcards(sid):
|
||||
cards = [cards_dict[i] for i in cards_dict]
|
||||
sio.emit('goldrushcards_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def get_valleyofshadowscards(sid):
|
||||
import bang.expansions.the_valley_of_shadows.cards as tvos
|
||||
cards = tvos.get_starting_deck()
|
||||
cards_dict = {}
|
||||
for ca in cards:
|
||||
if ca.name not in cards_dict:
|
||||
cards_dict[ca.name] = ca
|
||||
cards = [cards_dict[i] for i in cards_dict]
|
||||
sio.emit('valleyofshadows_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||
|
||||
@sio.event
|
||||
@bang_handler
|
||||
def discord_auth(sid, data):
|
||||
res = requests.post('https://discord.com/api/oauth2/token', data={
|
||||
'client_id': '1059452581027532880',
|
||||
'client_secret': 'Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu',
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': data['origin'],
|
||||
'code': data['code'],
|
||||
})
|
||||
if res.status_code == 200:
|
||||
sio.emit('discord_auth_succ', room=sid, data=res.json())
|
||||
|
||||
def pool_metrics():
|
||||
sio.sleep(60)
|
||||
Metrics.send_metric('lobbies', points=[len([g for g in games if not g.is_replay])])
|
||||
Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games)])
|
||||
Metrics.send_metric('online_players', points=[online_players])
|
||||
pool_metrics()
|
||||
|
||||
@ -650,6 +787,10 @@ class CustomProxyFix(object):
|
||||
return ['']
|
||||
return self.app(environ, start_response)
|
||||
|
||||
|
||||
discord_ci = '1059452581027532880'
|
||||
discord_cs = 'Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu'
|
||||
|
||||
if __name__ == '__main__':
|
||||
sio.start_background_task(pool_metrics)
|
||||
eventlet.wsgi.server(eventlet.listen(('', 5001)), CustomProxyFix(app))
|
||||
|
@ -297,8 +297,6 @@ def test_SlabTheKiller():
|
||||
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||
g.players[(g.turn+1)%2].respond(0)
|
||||
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||
g.players[(g.turn+1)%2].respond(-1)
|
||||
assert g.players[(g.turn+1)%2].pending_action == PendingAction.WAIT
|
||||
assert g.players[(g.turn+1)%2].lives == 3
|
||||
|
||||
|
142
backend/tests/valley_of_shadows_test.py
Normal file
142
backend/tests/valley_of_shadows_test.py
Normal file
@ -0,0 +1,142 @@
|
||||
from random import randint
|
||||
from bang.characters import Character
|
||||
from bang.expansions.the_valley_of_shadows.cards import *
|
||||
from tests.dummy_socket import DummySocket
|
||||
from bang.deck import Deck
|
||||
from bang.game import Game
|
||||
from bang.players import Player, PendingAction
|
||||
import bang.cards as cs
|
||||
|
||||
# test UltimoGiro
|
||||
def test_ultimo_giro():
|
||||
sio = DummySocket()
|
||||
g = Game('test', sio)
|
||||
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||
for p in ps:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
for p in ps:
|
||||
p.available_characters = [Character('test_char', 4)]
|
||||
p.set_character(p.available_characters[0].name)
|
||||
ultimo_giro_guy = g.players[g.turn]
|
||||
ultimo_giro_guy.draw('')
|
||||
ultimo_giro_guy.lives = 3
|
||||
ultimo_giro_guy.hand = [UltimoGiro(0,0)]
|
||||
assert ultimo_giro_guy.lives == 3
|
||||
ultimo_giro_guy.play_card(0)
|
||||
assert ultimo_giro_guy.lives == 4
|
||||
|
||||
# test Tomahawk
|
||||
def test_tomahawk():
|
||||
sio = DummySocket()
|
||||
g = Game('test', sio)
|
||||
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(6)]
|
||||
for p in ps:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
for p in ps:
|
||||
p.available_characters = [Character('test_char', 4)]
|
||||
p.set_character(p.available_characters[0].name)
|
||||
tomahawk_guy = g.players[g.turn]
|
||||
tomahawk_guy.draw('')
|
||||
tomahawk_guy.hand = [Tomahawk(0,0)]
|
||||
assert len(tomahawk_guy.hand) == 1
|
||||
tomahawk_guy.play_card(0, g.players[(g.turn+3)%6].name)
|
||||
assert len(tomahawk_guy.hand) == 1
|
||||
tomahawk_guy.play_card(0, g.players[(g.turn+1)%6].name)
|
||||
assert len(tomahawk_guy.hand) == 0
|
||||
|
||||
# test Fantasma
|
||||
def test_fantasma():
|
||||
sio = DummySocket()
|
||||
g = Game('test', sio)
|
||||
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||
for p in ps:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
|
||||
for p in ps:
|
||||
p.available_characters = [Character('test_char', 4)]
|
||||
p.set_character(p.available_characters[0].name)
|
||||
fantasma_guy = g.players[g.turn]
|
||||
fantasma_guy.lives = 0
|
||||
fantasma_guy.notify_self()
|
||||
pl = g.players[g.turn]
|
||||
pl.draw('')
|
||||
pl.hand = [Fantasma(0,0)]
|
||||
pl.play_card(0)
|
||||
assert pl.pending_action == PendingAction.CHOOSE
|
||||
assert pl.available_cards[0]['name'] == fantasma_guy.name
|
||||
pl.choose(0)
|
||||
assert pl.pending_action == PendingAction.PLAY
|
||||
assert len(fantasma_guy.equipment) == 1 and isinstance(fantasma_guy.equipment[0], Fantasma)
|
||||
|
||||
# test SerpenteASonagli
|
||||
def test_serpente_a_sonagli():
|
||||
sio = DummySocket()
|
||||
g = Game('test', sio)
|
||||
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||
for p in ps:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
for p in ps:
|
||||
p.available_characters = [Character('test_char', 4)]
|
||||
p.set_character(p.available_characters[0].name)
|
||||
p = g.players[g.turn]
|
||||
serp = g.players[(g.turn+1)%3]
|
||||
p.draw('')
|
||||
p.hand = [SerpenteASonagli(0,0)]
|
||||
assert len(p.hand) == 1
|
||||
p.play_card(0, serp.name)
|
||||
assert len(p.hand) == 0
|
||||
assert len(serp.equipment) == 1 and isinstance(serp.equipment[0], SerpenteASonagli)
|
||||
p.end_turn()
|
||||
assert serp.pending_action == PendingAction.PICK
|
||||
g.deck.cards[0] = Bang(Suit.SPADES, 5)
|
||||
serp.pick()
|
||||
assert serp.lives == 3
|
||||
serp.draw('')
|
||||
serp.hand = [SerpenteASonagli(0,0)]
|
||||
serp.play_card(0, g.players[(g.turn+1)%3].name)
|
||||
assert len(serp.hand) == 0
|
||||
serp.end_turn()
|
||||
assert g.players[g.turn].pending_action == PendingAction.PICK
|
||||
g.deck.cards[0] = Bang(Suit.HEARTS, 5)
|
||||
g.players[g.turn].pick()
|
||||
assert g.players[g.turn].lives == 4
|
||||
|
||||
# test RitornoDiFiamma
|
||||
def test_ritorno_di_fiamma():
|
||||
sio = DummySocket()
|
||||
g = Game('test', sio)
|
||||
g.expansions = ['the_valley_of_shadows']
|
||||
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||
for p in ps:
|
||||
g.add_player(p)
|
||||
g.start_game()
|
||||
for p in ps:
|
||||
p.available_characters = [Character('test_char', 4)]
|
||||
p.set_character(p.available_characters[0].name)
|
||||
p = g.players[g.turn]
|
||||
p1 = g.players[(g.turn+1)%3]
|
||||
p.draw('')
|
||||
p.hand = [Bang(1, 1)]
|
||||
p1.hand = [RitornoDiFiamma(0,0)]
|
||||
p.play_card(0, p1.name)
|
||||
assert len(p.hand) == 0
|
||||
assert len(p1.hand) == 1
|
||||
p1.respond(0)
|
||||
assert len(p1.hand) == 0
|
||||
assert p.lives == 3
|
||||
p.end_turn()
|
||||
p1.draw('')
|
||||
p1.hand = [Bang(1, 1)]
|
||||
p.equipment = [cs.Barile(0,0)]
|
||||
p.hand = [RitornoDiFiamma(0,0)]
|
||||
p1.play_card(0, p.name)
|
||||
assert p.pending_action == PendingAction.PICK
|
||||
g.deck.cards[0] = Bang(Suit.SPADES, 5)
|
||||
p.pick()
|
||||
assert p.pending_action == PendingAction.RESPOND
|
||||
p.respond(0)
|
||||
assert p1.lives == 3
|
@ -17,7 +17,8 @@
|
||||
"vue": "^2.6.14",
|
||||
"vue-clipboard2": "^0.3.3",
|
||||
"vue-i18n": "^8.27.1",
|
||||
"vue-router": "^3.5.4",
|
||||
"vue-json-viewer": "^2.2.22",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue-socket.io": "^3.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -135,6 +135,7 @@ export default {
|
||||
this.$i18n.locale = userLang.split('-')[0]
|
||||
}
|
||||
this.detectColorScheme()
|
||||
if (window.location.origin.indexOf('localhost') !== -1) return;
|
||||
datadogRum.init({
|
||||
applicationId: '076b1a5e-16a9-44eb-b320-27afd32c57a5',
|
||||
clientToken: 'pub1cc4d0d6ea0a7235aa1eab86e7a192d4',
|
||||
|
@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div :class="{ card: true, equipment: card.is_equipment, character:card.is_character, back:card.is_back, 'usable-next-turn':card.usable_next_turn, 'must-be-used':card.must_be_used, 'gold-rush': card.expansion === 'gold_rush', 'brown':card.kind === 0, 'black':card.kind === 1,}">
|
||||
<div :class="{ card: true, avatarred:card.avatar, equipment: card.is_equipment, character:card.is_character, back:card.is_back, 'usable-next-turn':card.usable_next_turn, 'must-be-used':card.must_be_used, 'gold-rush': card.expansion === 'gold_rush', 'brown':card.kind === 0, 'black':card.kind === 1,}">
|
||||
<h4>{{cardName}}</h4>
|
||||
<div class="emoji">{{emoji}}</div>
|
||||
<div v-if="card.avatar" class="avatar" :style="`background-image: url(${card.avatar});`"></div>
|
||||
<div :class="{emoji:true, bottomed:card.avatar}">{{emoji}}</div>
|
||||
<div class="alt_text">{{card.alt_text}}</div>
|
||||
<div class="suit">{{number}}<span :style="`${(card.suit !== undefined && card.suit%2 === 0)? 'color:red':''}`">{{suit}}</span></div>
|
||||
<div class="expansion" v-if="card.expansion_icon">{{card.expansion_icon}}</div>
|
||||
@ -72,6 +73,12 @@ export default {
|
||||
word-wrap: normal;
|
||||
/* word-wrap: break-word; */
|
||||
}
|
||||
.avatarred {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.card.back{
|
||||
color:white;
|
||||
background: repeating-linear-gradient(
|
||||
@ -117,6 +124,17 @@ export default {
|
||||
);
|
||||
border: 2pt solid rgb(50 122 172);
|
||||
}
|
||||
.avatar {
|
||||
position: absolute;
|
||||
width: 36pt;
|
||||
margin: auto;
|
||||
top: 25%;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 36pt;
|
||||
height: 36pt;
|
||||
}
|
||||
.card.brown.gold-rush {
|
||||
box-shadow: 0 0 0pt 4pt var(--bg-color), 0 0 5pt 4pt #aaa;
|
||||
border: 2pt dotted #9C7340;
|
||||
@ -142,6 +160,10 @@ export default {
|
||||
font-size:26pt;
|
||||
top: 35%;
|
||||
}
|
||||
.emoji.bottomed {
|
||||
top: 45%;
|
||||
left: 8pt;
|
||||
}
|
||||
.card.must-be-used {
|
||||
filter: drop-shadow(0 0 5px red);
|
||||
}
|
||||
@ -155,7 +177,7 @@ export default {
|
||||
left:3pt;
|
||||
}
|
||||
.card.character .suit {
|
||||
font-size: x-small;
|
||||
font-size: small;
|
||||
right: 3pt;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -9,7 +9,10 @@
|
||||
</div>
|
||||
<div class="cont">
|
||||
<transition-group name="message" tag="div" id="chatbox" :style="`${collapsed?'display:none':''}`">
|
||||
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color};background:${msg.bgcolor}${msg.bgcolor?';border-left: medium solid '+msg.color+';padding-left:2pt;':''}`">{{msg.text}}</p>
|
||||
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color};background:${msg.bgcolor}${msg.bgcolor?';border-left: medium solid '+msg.color+';padding-left:2pt;':''}`">
|
||||
<JsonViewer v-if="msg.type == 'json'" :value="msg.json"/>
|
||||
<span v-else>{{msg.text}}</span>
|
||||
</p>
|
||||
<p class="end" key="end" style="color:#0000">.</p>
|
||||
</transition-group>
|
||||
<div v-if="commandSuggestion.length > 0">
|
||||
@ -35,11 +38,16 @@ import prison_sfx from '@/assets/sounds/prison.mp3'
|
||||
import turn_sfx from '@/assets/sounds/turn.mp3'
|
||||
import death_sfx from '@/assets/sounds/death.mp3'
|
||||
import cash_sfx from '@/assets/sounds/cash.mp3'
|
||||
import JsonViewer from 'vue-json-viewer'
|
||||
|
||||
export default {
|
||||
name: 'Chat',
|
||||
props: {
|
||||
username: String
|
||||
},
|
||||
components: {
|
||||
JsonViewer
|
||||
},
|
||||
data: () => ({
|
||||
messages: [],
|
||||
toasts: [],
|
||||
@ -59,7 +67,6 @@ export default {
|
||||
},
|
||||
sockets: {
|
||||
chat_message(msg) {
|
||||
// console.log(msg)
|
||||
if ((typeof msg === "string" && msg.indexOf('_') === 0) || (msg.color != null && msg.text.indexOf('_') === 0)) {
|
||||
let t_color = null
|
||||
let bg_color = null
|
||||
@ -107,6 +114,9 @@ export default {
|
||||
} else { // a chat message
|
||||
(new Audio(message_sfx)).play();
|
||||
this.messages.push(msg);
|
||||
if (msg.type && msg.type === 'json') {
|
||||
msg.json = JSON.parse(msg.text);
|
||||
}
|
||||
if (this.collapsed || window.innerWidth < 1000) {
|
||||
this.toasts.push(msg);
|
||||
setTimeout(() => this.toasts.shift(), 5000);
|
||||
|
@ -3,11 +3,12 @@
|
||||
<h1>{{text}}</h1>
|
||||
<div>
|
||||
<transition-group name="list" tag="div">
|
||||
<Card v-for="(c, i) in cards" v-bind:key="c.name ? (c.name+c.number) : i" :card="c" @click.native="select(c)" @pointerenter.native="showDesc(c)" @pointerleave.native="desc=''"/>
|
||||
<Card v-for="(c, i) in cards" v-bind:key="c.name ? (c.name+c.number) : i" :card="c" @click.native="internalSelect(c)" @pointerenter.native="showDesc(c)" @pointerleave.native="desc=''"/>
|
||||
</transition-group>
|
||||
</div>
|
||||
<p v-if="hintText">{{hintText}}</p>
|
||||
<div style="margin-top:6pt;" class="button center-stuff" v-if="showCancelBtn" @click="cancel"><span>{{realCancelText}}</span></div>
|
||||
<h2 v-if="timer > 0 && remainingTime > 0 && !showCancelBtn">{{remainingTime}}</h2>
|
||||
<div style="margin-top:6pt;" class="button center-stuff" v-if="showCancelBtn" @click="internalCancel"><span>{{realCancelText}}</span> <span v-if="timer > 0 && remainingTime > 0"> ({{remainingTime}})</span></div>
|
||||
<p v-if="desc" style="bottom:10pt;right:0;left:0;position:absolute;margin:16pt;font-size:18pt">{{desc}}</p>
|
||||
</div>
|
||||
</template>
|
||||
@ -29,13 +30,19 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
timer: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
text: String,
|
||||
hintText: String,
|
||||
playAudio: Boolean,
|
||||
},
|
||||
data: () => ({
|
||||
desc: '',
|
||||
realCancelText: ''
|
||||
realCancelText: '',
|
||||
remainingTime: 0,
|
||||
intervalID: '',
|
||||
}),
|
||||
computed: {
|
||||
showCancelBtn() {
|
||||
@ -49,16 +56,30 @@ export default {
|
||||
//console.log(card)
|
||||
if (card.noDesc || card.name == null || card.name == "PewPew!")
|
||||
this.desc = ""
|
||||
else if (card.is_character)
|
||||
this.desc = card.name
|
||||
else if (card.goal)
|
||||
this.desc = this.$t(`cards.${card.name}.name`)
|
||||
else if (card.desc)
|
||||
this.desc = (this.$i18n.locale=='it'?card.desc:card.desc_eng)
|
||||
else
|
||||
this.desc = this.$t(`cards.${card.name}.desc`)
|
||||
},
|
||||
countDown() {
|
||||
if (this.remainingTime > 0) {
|
||||
this.remainingTime--;
|
||||
} else {
|
||||
this.select(this.cards[0]);
|
||||
window.clearInterval(this.intervalID);
|
||||
}
|
||||
},
|
||||
internalCancel() {
|
||||
if (this.intervalID) window.clearInterval(this.intervalID);
|
||||
this.cancel();
|
||||
},
|
||||
internalSelect(card) {
|
||||
if (this.intervalID) window.clearInterval(this.intervalID);
|
||||
this.select(card);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.realCancelText = this.cancelText
|
||||
if (this.realCancelText == '') {
|
||||
@ -70,6 +91,11 @@ export default {
|
||||
if (this.playAudio) {
|
||||
(new Audio(show_sfx)).play();
|
||||
}
|
||||
this.remainingTime = this.timer;
|
||||
if (this.timer != 0 && this.remainingTime == this.timer) {
|
||||
if (this.intervalID) window.clearInterval(this.intervalID);
|
||||
this.intervalID = window.setInterval(this.countDown, 1000);
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -108,6 +108,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="valleyofshadowscards">{{$t('help.valleyofshadowscards')}}</h2>
|
||||
<div class="flexy-cards-wrapper">
|
||||
<div v-for="(c, i) in valleyofshadowscards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||
<Card :card="c" class="valley-of-shadows" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||
<div style="margin-left:6pt;">
|
||||
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -127,6 +136,7 @@ export default {
|
||||
highnooncards: [],
|
||||
foccards: [],
|
||||
goldrushcards: [],
|
||||
valleyofshadowscards: [],
|
||||
}),
|
||||
computed: {
|
||||
endTurnCard() {
|
||||
@ -161,6 +171,11 @@ export default {
|
||||
...x,
|
||||
}))
|
||||
},
|
||||
valleyofshadows_info(cardsJson) {
|
||||
this.valleyofshadowscards = JSON.parse(cardsJson).map(x=>({
|
||||
...x,
|
||||
}))
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$socket.emit('get_cards')
|
||||
@ -168,6 +183,7 @@ export default {
|
||||
this.$socket.emit('get_highnooncards')
|
||||
this.$socket.emit('get_foccards')
|
||||
this.$socket.emit('get_goldrushcards')
|
||||
this.$socket.emit('get_valleyofshadowscards')
|
||||
document.getElementById('help').scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@
|
||||
<div v-if="showTurnFlow" id="turn-indicator" :class="{reversed:turnReversed}"/>
|
||||
<transition-group name="list" tag="div" class="players-table">
|
||||
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;" class="player-in-table">
|
||||
<transition-group v-if="p.gold_nuggets && p.gold_nuggets > 0" name="list" tag="div" style="position: absolute;top: -10pt; font-size:9pt;">
|
||||
<span v-for="(n, i) in p.gold_nuggets" v-bind:key="i" :alt="i">💵️</span>
|
||||
</transition-group>
|
||||
@ -70,6 +70,7 @@
|
||||
<span>🤖</span>
|
||||
</div>
|
||||
</div>
|
||||
<Card v-if="startGameCard" key="_shuffle_players_" :donotlocalize="true" :card="shufflePlayersCard" @click.native="shufflePlayers" class="fistful-of-cards"/>
|
||||
</transition-group>
|
||||
</div>
|
||||
<div v-if="started">
|
||||
@ -85,6 +86,12 @@
|
||||
<transition name="bounce">
|
||||
<full-screen-input v-if="!started && hasToSetUsername" :defaultValue="storedUsername" :text="$t('choose_username')" :val="username" :send="setUsername" :sendText="$t('ok')"/>
|
||||
</transition>
|
||||
<transition name="bounce">
|
||||
<div v-if="displayAdminStatus" id="admin-status">
|
||||
<input type="button" @click="displayAdminStatus = false" value="close"/>
|
||||
<Status deploy_key="ok"/>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -98,6 +105,7 @@ import Player from './Player.vue'
|
||||
import Deck from './Deck.vue'
|
||||
import TinyHand from './TinyHand.vue'
|
||||
import FullScreenInput from './FullScreenInput.vue'
|
||||
import Status from './Status.vue'
|
||||
|
||||
export default {
|
||||
name: 'Lobby',
|
||||
@ -109,7 +117,8 @@ export default {
|
||||
Deck,
|
||||
TinyHand,
|
||||
PrettyCheck,
|
||||
FullScreenInput
|
||||
FullScreenInput,
|
||||
Status
|
||||
},
|
||||
data: () => ({
|
||||
username: '',
|
||||
@ -134,6 +143,7 @@ export default {
|
||||
debug_mode: false,
|
||||
showTurnFlow: false,
|
||||
turnReversed: false,
|
||||
displayAdminStatus: false,
|
||||
turn: -1,
|
||||
}),
|
||||
sockets: {
|
||||
@ -156,6 +166,7 @@ export default {
|
||||
name: x.name,
|
||||
ready: x.ready,
|
||||
is_bot: x.is_bot,
|
||||
avatar: x.avatar,
|
||||
ncards: 0,
|
||||
}
|
||||
})
|
||||
@ -179,6 +190,9 @@ export default {
|
||||
this.username = username
|
||||
// this.$socket.emit('get_cards', 'dodge_city')
|
||||
},
|
||||
mount_status() {
|
||||
this.displayAdminStatus = true
|
||||
},
|
||||
// cards_info(data) {
|
||||
// data = JSON.parse(data)
|
||||
// let bigthing = {}
|
||||
@ -227,7 +241,7 @@ export default {
|
||||
return ''
|
||||
},
|
||||
isRoomOwner() {
|
||||
return this.players.length > 0 && this.players[0].name == this.username
|
||||
return this.players.length > 0 && this.players.filter(x => !x.is_bot)[0].name == this.username
|
||||
},
|
||||
startGameCard() {
|
||||
if (!this.started && this.players.length > 2 && this.isRoomOwner) {
|
||||
@ -240,6 +254,16 @@ export default {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
shufflePlayersCard() {
|
||||
if (!this.started && this.players.length > 2 && this.isRoomOwner) {
|
||||
return {
|
||||
name: this.$t('shuffle_players'),
|
||||
icon: '🔀',
|
||||
is_equipment: true,
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
playersTable() {
|
||||
if (Vue.config.devtools)
|
||||
console.log('update players')
|
||||
@ -289,19 +313,25 @@ export default {
|
||||
},
|
||||
getPlayerCard(player) {
|
||||
let icon = ''
|
||||
let owner = this.players.filter(x => !x.is_bot)[0];
|
||||
if (!this.started) icon = '🤠'
|
||||
else icon = player.ready !== undefined ? ((player.ready)?'👍': '🤔') : (player.is_sheriff ? '⭐' : player.icon)
|
||||
return {
|
||||
name: player.name,
|
||||
number: ((this.username == player.name) ? this.$t('you') : (this.players[0].name == player.name) ? this.$t('owner') :'') + (player.dist ? `${player.dist}⛰` : ''),
|
||||
number: ((this.username == player.name) ? this.$t('you') : (owner.name == player.name) ? this.$t('owner') :'') + (player.dist ? `${player.dist}⛰` : ''),
|
||||
icon: icon,
|
||||
is_character: true,
|
||||
avatar: player.avatar,
|
||||
}
|
||||
},
|
||||
startGame() {
|
||||
this.started = true;
|
||||
this.$socket.emit('start_game')
|
||||
},
|
||||
shufflePlayers() {
|
||||
this.started = true;
|
||||
this.$socket.emit('shuffle_players')
|
||||
},
|
||||
choose(player_name) {
|
||||
if (Vue.config.devtools)
|
||||
console.log('choose from' + player_name)
|
||||
@ -345,7 +375,7 @@ export default {
|
||||
if (name.trim().length > 0){
|
||||
localStorage.setItem('username', name)
|
||||
this.hasToSetUsername = false
|
||||
this.$socket.emit('set_username', name)
|
||||
this.$socket.emit('set_username', {name:name})
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -369,9 +399,9 @@ export default {
|
||||
mounted() {
|
||||
if (Vue.config.devtools)
|
||||
console.log('mounted lobby')
|
||||
if (!this.$route.query.code)
|
||||
if (!this.$route.query.code && !this.$route.query.replay)
|
||||
return this.$router.push('/')
|
||||
this.$socket.emit('get_me', {name:this.$route.query.code, password:this.$route.query.pwd, username: localStorage.getItem('username')})
|
||||
this.$socket.emit('get_me', {name:this.$route.query.code, password:this.$route.query.pwd, username: localStorage.getItem('username'), discord_token: localStorage.getItem('discord_token'), replay: this.$route.query.replay})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -426,6 +456,9 @@ export default {
|
||||
justify-content: space-evenly;
|
||||
margin-bottom: 12pt;
|
||||
}
|
||||
#admin-status {
|
||||
position:absolute;width:100%;height:100%;overflow:auto;background:var(--bg-color); opacity: 0.8;
|
||||
}
|
||||
#turn-indicator{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@ -471,5 +504,11 @@ background-position-x: 80px;
|
||||
min-width: 25vw;
|
||||
max-width: 25vw;
|
||||
}
|
||||
.player-in-table {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
.player-in-table:hover {
|
||||
transform: translateY(-5px) scale(1.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -18,6 +18,7 @@
|
||||
<input id="username" v-model="username" />
|
||||
<input type="submit" class="btn" :value="$t('submit')"/>
|
||||
</form>
|
||||
<a class="btn" :href="redirectUrl">Login with Discord</a>
|
||||
<p v-if="onlinePlayers > 0">{{$t("online_players")}}{{onlinePlayers}}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
@ -71,8 +72,12 @@ export default {
|
||||
isInLobby: false,
|
||||
onlinePlayers: 0,
|
||||
randomTip: '',
|
||||
discordPic: '',
|
||||
}),
|
||||
computed: {
|
||||
redirectUrl() {
|
||||
return 'https://discordapp.com/api/oauth2/authorize?client_id=1059452581027532880&response_type=code&scope=identify&redirect_uri=' + window.location.origin;
|
||||
},
|
||||
noLobbyAvailable() {
|
||||
return this.openLobbies && this.openLobbies.length == 0
|
||||
},
|
||||
@ -85,6 +90,7 @@ export default {
|
||||
number: this.$t('you'),
|
||||
icon: '🤠',
|
||||
is_character: true,
|
||||
avatar: this.discordPic,
|
||||
}
|
||||
},
|
||||
version() {
|
||||
@ -105,14 +111,20 @@ export default {
|
||||
players(num) {
|
||||
this.onlinePlayers = num;
|
||||
// console.log('PLAYERS:' + num)
|
||||
},
|
||||
discord_auth_succ(data) {
|
||||
if (data.access_token) {
|
||||
localStorage.setItem('discord_token', data.access_token)
|
||||
this.login()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setUsername(e){
|
||||
if (this.username.trim().length > 0){
|
||||
this.didSetUsername = true
|
||||
localStorage.setItem('username', this.username)
|
||||
this.$socket.emit('set_username', this.username)
|
||||
this.$socket.emit('set_username', {name:this.username})
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
@ -145,8 +157,34 @@ export default {
|
||||
init() {
|
||||
location.reload();
|
||||
},
|
||||
login() {
|
||||
fetch('https://discordapp.com/api/users/@me', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('discord_token')
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
this.username = data.username
|
||||
this.didSetUsername = true
|
||||
this.discordPic = `https://cdn.discordapp.com/avatars/${data.id}/${data.avatar}.png`
|
||||
localStorage.setItem('username', this.username)
|
||||
this.$socket.emit('set_username', {name: this.username, discord_token: localStorage.getItem('discord_token')})
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
localStorage.removeItem('discord_token')
|
||||
this.$router.replace({query: []})
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (localStorage.getItem('discord_token')) {
|
||||
this.login()
|
||||
} else if (this.$route.query.code) {
|
||||
this.$socket.emit('discord_auth', {code:this.$route.query.code, origin:window.location.origin})
|
||||
this.$router.replace({query: []})
|
||||
}
|
||||
this.randomTip = `tip_${1+Math.floor(Math.random() * 8)}`
|
||||
if (localStorage.getItem('username'))
|
||||
this.username = localStorage.getItem('username')
|
||||
|
@ -37,6 +37,7 @@
|
||||
</div>
|
||||
<div v-if="lives > 0 || is_ghost" style="position:relative">
|
||||
<span id="hand_text">{{$t('hand')}}</span>
|
||||
<span id="hand_text" style="bottom:40pt;">{{hand.length}}/{{maxHandLength()}}</span>
|
||||
<transition-group name="list" tag="div" :class="{hand:true, 'play-cards':pending_action===2}">
|
||||
<Card v-for="card in handComputed" v-bind:key="card.name+card.number+card.suit" :card="card"
|
||||
@click.native="play_card(card, false)"
|
||||
@ -49,15 +50,15 @@
|
||||
</transition>
|
||||
<Chooser v-if="is_my_turn && pending_action == 4 && (lives > 0 || is_ghost) && !(emporioCards && emporioCards.cards && emporioCards.cards.length > 0)" :text="$t('wait')" :cards="[]"/>
|
||||
<Chooser v-if="card_against" :text="$t('card_against')" :hint-text="visiblePlayers.length === 0 ? $t('no_players_in_range'):''" :cards="visiblePlayers" :select="selectAgainst" :cancel="card_against.number !== 42 ? cancelCardAgainst : null"/>
|
||||
<Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond" :playAudio="true"/>
|
||||
<Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose" :playAudio="true"/>
|
||||
<Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond" :playAudio="true" :timer="30"/>
|
||||
<Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose" :playAudio="true" :timer="30"/>
|
||||
<Chooser v-if="lives <= 0 && max_lives > 0 && !is_ghost && !spectator" :text="$t('you_died')" :cancelText="$t('spectate')" :cancel="()=>{max_lives = 0; spectator = true}"/>
|
||||
<Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" />
|
||||
<Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')"/>
|
||||
<Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ${$t('did_pick_as')}:`" :cards="[notifycard.card]" :hintText="$t(notifycard.message)" class="turn-notify-4s"/>
|
||||
<Chooser v-if="cantplaycard" :key="cantplaycard" :text="`${$t('cantplaycard')}`" class="turn-notify-4s"/>
|
||||
<Chooser v-if="!show_role && is_my_turn && pending_action < 2" :text="$t('play_your_turn')" :key="is_my_turn" class="turn-notify" />
|
||||
<Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/>
|
||||
<Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter" :timer="45"/>
|
||||
<Chooser v-if="hasToPickResponse" :playAudio="true" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" />
|
||||
<Chooser v-if="!card_against && card_with" :text="`${$t('choose_scarp_card_to')} ${card_with.name.toUpperCase()}`" :cards="handComputed.filter(x => x !== card_with)" :select="selectWith" :cancel="()=>{card_with = null}"/>
|
||||
<Chooser v-if="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${maxHandLength()}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/>
|
||||
@ -69,6 +70,7 @@
|
||||
:cards="notScrappedHand" :select="holydayScrapAdd" :cancel="() => {holydayScrap = false;scrapHand=[]}"/>
|
||||
<Chooser v-if="holydayScrap && scrapHand.length == 2" :text="$t('card_against')" :cards="visiblePlayers" :select="holydayScrapBang" :cancel="() => {holydayScrap = false;scrapHand=[]}"/>
|
||||
<Chooser style="filter: grayscale(1);" v-if="emporioCards && emporioCards.cards && emporioCards.cards.length > 0 && (pending_action === 4 || pending_action === null)" :text="$t('emporio_others', [emporioCards.name])" :cards="emporioCards.cards"/>
|
||||
<div style="position: fixed;width: 100%;height: 100%;background: #ff000070;top: 0;left: 0;" v-if="hurt" class="hurt-notify"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -134,6 +136,7 @@ export default {
|
||||
committed_suit_manette: null,
|
||||
gold_nuggets: 0,
|
||||
cantplaycard: false,
|
||||
hurt: false,
|
||||
}),
|
||||
sockets: {
|
||||
role(role) {
|
||||
@ -205,6 +208,12 @@ export default {
|
||||
this.notifycard = null
|
||||
}.bind(this), 4000)
|
||||
},
|
||||
hurt() {
|
||||
this.hurt = true
|
||||
setTimeout(function(){
|
||||
this.hurt = false
|
||||
}.bind(this), 500)
|
||||
},
|
||||
cant_play_card() {
|
||||
this.cantplaycard = true
|
||||
setTimeout(function(){
|
||||
@ -234,6 +243,7 @@ export default {
|
||||
name: player.name,
|
||||
number: player.dist !== undefined ? `${player.dist}⛰` : '',
|
||||
icon: this.noStar ? player.icon : player.is_sheriff ? '⭐' : '🤠',
|
||||
avatar: player.avatar,
|
||||
is_character: true,
|
||||
}})
|
||||
return vis
|
||||
@ -255,6 +265,7 @@ export default {
|
||||
number: player.dist !== undefined ? `${player.dist}⛰` : '',
|
||||
icon: this.noStar ? player.icon : player.is_sheriff ? '⭐' : '🤠',
|
||||
alt_text: Array(player.lives+1).join('❤️')+Array(player.max_lives-player.lives+1).join('💀'),
|
||||
avatar: player.avatar,
|
||||
is_character: true,
|
||||
}})
|
||||
if (this.card_against && this.card_against.can_target_self) {
|
||||
@ -510,6 +521,10 @@ export default {
|
||||
margin: 10pt 0pt;
|
||||
overflow:auto;
|
||||
}
|
||||
.hurt-notify {
|
||||
pointer-events: none;
|
||||
animation: disappear 0.5s ease-in forwards;
|
||||
}
|
||||
.turn-notify {
|
||||
pointer-events: none;
|
||||
animation: disappear 2s ease-in forwards;
|
||||
|
@ -31,9 +31,15 @@
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Help',
|
||||
name: 'Status',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
onpage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data:()=>({
|
||||
rooms: [],
|
||||
deploy_key: ''
|
||||
@ -46,7 +52,7 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.deploy_key == "")
|
||||
if (this.deploy_key == "" && this.onpage)
|
||||
this.deploy_key = prompt('Write the key');
|
||||
this.refresh();
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"tip_7": "If you want to help us translate the game in your language, ping us on discord!",
|
||||
"tip_8": "If you disconnect during in an ongoing game you will be replaced by a bot (while you are gone)!",
|
||||
"online_players": "Online players: ",
|
||||
"shuffle_players": "Shuffle Players",
|
||||
"choose_username": "Pick an username:",
|
||||
"report_bug": "Write what the bug consists of",
|
||||
"report": "Report a bug",
|
||||
@ -56,6 +57,9 @@
|
||||
"choose_ranch": "Choose the cards to replace",
|
||||
"choose_dalton": "Choose which equipment to discard",
|
||||
"choose_fratelli_di_sangue": "Choose who you want to donate one of your lives",
|
||||
"choose_fantasma": "Choose who to bring back to life",
|
||||
"choose_tornado": "Choose a card to discard for the tornado",
|
||||
"choose_bandidos": "Choose between discarding 2 cards or losing a life (1 card if you only have 1)",
|
||||
"choose_cecchino": "Choose who to shoot",
|
||||
"choose_rimbalzo_player": "Choose the target of the bang",
|
||||
"choose_rimbalzo_card": "Choose the card to discard the bang to",
|
||||
@ -132,7 +136,10 @@
|
||||
"UnionPacific": "{0} played Union Pacific and draws 4 cards from the deck",
|
||||
"use_special": "{0} used the special ability of their character ({1})",
|
||||
"gold_rush_pay_discard": "{0} discarded {2} from {1}.",
|
||||
"choose_emporio": "{0} has chosen {1} from General Store."
|
||||
"choose_emporio": "{0} has chosen {1} from General Store.",
|
||||
"shotgun_scrap": "When the shotgun hit {0} a card flew away from his hand ({1})",
|
||||
"taglia_reward": "{1} got a card from the bounty on {0}",
|
||||
"snake_bit": "{0} was bitten by the Rattle Snake."
|
||||
},
|
||||
"foc": {
|
||||
"leggedelwest": "He must play this card on this turn if possible."
|
||||
@ -674,6 +681,66 @@
|
||||
"Simeon Picos": {
|
||||
"name": "Simeon Picos",
|
||||
"desc": "He gets 1 gold nugget every time he loses 1 hp."
|
||||
},
|
||||
"Fantasma": {
|
||||
"name": "Ghost",
|
||||
"desc": "Play on any eliminated player: That player is back in the game, but can't gain nor lose life points."
|
||||
},
|
||||
"Lemat": {
|
||||
"name": "Lemat",
|
||||
"desc": "During your turn you can use any card as BANG!."
|
||||
},
|
||||
"SerpenteASonagli": {
|
||||
"name": "Serpente a Sonagli",
|
||||
"desc": "Play on any player. At the beginning of his turn. if that player draws on Spades, he loses I life point."
|
||||
},
|
||||
"Shotgun": {
|
||||
"name": "Shotgun",
|
||||
"desc": "Every time you wound a player, they must discard a card from their hand."
|
||||
},
|
||||
"Taglia": {
|
||||
"name": "Bounty",
|
||||
"desc": "Play on anyone. If that player is hit by BANG!, the person who shot gets a card from the deck."
|
||||
},
|
||||
"Mira": {
|
||||
"name": "Aim",
|
||||
"desc": "Play this card along with a BANG! card. If the target is hit, they lose 2 life points."
|
||||
},
|
||||
"RitornoDiFiamma": {
|
||||
"name": "Backfire",
|
||||
"desc": "Counts as a Missed! card. The player who shot is the target of a BANG!."
|
||||
},
|
||||
"Bandidos": {
|
||||
"name": "Bandidos",
|
||||
"desc": "Each player chooses to discard 2 cards from their hand (or 1 if they only have 1) or lose 1 life point."
|
||||
},
|
||||
"Fuga": {
|
||||
"name": "Escape",
|
||||
"desc": "Can be played out of turn. Avoids the effect of a brown (not BANG!) card you are a target of."
|
||||
},
|
||||
"Sventagliata": {
|
||||
"name": "Fanning",
|
||||
"desc": "Counts as the only BANG! of the turn. A player of your choice at distance 1 from the target (if any, excluding yourself) is also a target of a BANG.."
|
||||
},
|
||||
"UltimoGiro": {
|
||||
"name": "Last Call",
|
||||
"desc": "Recovers 1 life point"
|
||||
},
|
||||
"Poker": {
|
||||
"name": "Poker",
|
||||
"desc": "All other players discard a card from their hand, at the same time. If there are no Aces, draw up to 2 of those cards."
|
||||
},
|
||||
"Salvo": {
|
||||
"name": "Saved!",
|
||||
"desc": "Can be played out of turn. Prevents another player from losing 1 life point. If they survive, they draw 2 cards from their hand or deck (your choice)."
|
||||
},
|
||||
"Tomahawk": {
|
||||
"name": "Tomahawk",
|
||||
"desc": "Attacks a player up to distance 2."
|
||||
},
|
||||
"Tornado": {
|
||||
"name": "Tornado",
|
||||
"desc": "Everyone discards a card from their hand (if possible), then draws 2 cards from the deck."
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
@ -724,7 +791,8 @@
|
||||
"gotogoldrush": "Jump to Gold Rush",
|
||||
"highnooncards": "High Noon - Event Cards",
|
||||
"foccards": "Fistful of Cards - Event Cards",
|
||||
"goldrushcards": "Gold Rush Cards"
|
||||
"goldrushcards": "Gold Rush Cards",
|
||||
"valleyofshadowscards": "The Valley of Shadows Cards"
|
||||
},
|
||||
"theme": {
|
||||
"sepia": "Sepia",
|
||||
|
@ -9,6 +9,7 @@
|
||||
"tip_7": "Se vuoi aiutarci a tradurre il gioco nella tua lingua scrivicelo su discord!",
|
||||
"tip_8": "Se ti disconnetti durante una partita verrai sostituito da un bot (durante la tua assenza)!",
|
||||
"online_players": "Giocatori online: ",
|
||||
"shuffle_players": "Riordina Giocatori",
|
||||
"choose_username": "Scegli un username:",
|
||||
"report_bug":"Scrivi in cosa consiste il bug",
|
||||
"report":"Segnala un bug",
|
||||
@ -56,6 +57,9 @@
|
||||
"choose_ranch": "Scegli le carte da sostituire",
|
||||
"choose_dalton": "Scegli che equipaggiamento scartare",
|
||||
"choose_fratelli_di_sangue": "Scegli a chi donare una delle tue vite",
|
||||
"choose_fantasma": "Scegli chi riportare in vita",
|
||||
"choose_tornado": "Scegli una carta da scartare per il tornado",
|
||||
"choose_bandidos": "Scegli tra scartare 2 carte o perdere una vita (1 carta se ne hai solo 1)",
|
||||
"choose_cecchino": "Scegli contro chi sparare",
|
||||
"choose_rimbalzo_player": "Scegli contro chi scartare il bang",
|
||||
"choose_rimbalzo_card": "Scegli contro che carta scartare il bang",
|
||||
@ -132,7 +136,10 @@
|
||||
"UnionPacific": "{0} ha giocato Union Pacific e ha pescato 4 carte.",
|
||||
"use_special": "{0} ha usato l'abilità speciale del suo personaggio ({1})",
|
||||
"gold_rush_pay_discard": "{0} ha fatto scartare {2} a {1}.",
|
||||
"choose_emporio": "{0} ha scelto {1} da Emporio."
|
||||
"choose_emporio": "{0} ha scelto {1} da Emporio.",
|
||||
"shotgun_scrap": "Quando lo shotgun ha colpito {0} gli ha tolto una carta ({1})",
|
||||
"taglia_reward": "{1} ha ottenuto ricompensa dalla taglia su {0}",
|
||||
"snake_bit": "{0} è stato morso dal Serpente a Sonagli."
|
||||
},
|
||||
"foc": {
|
||||
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"
|
||||
@ -674,6 +681,66 @@
|
||||
"Simeon Picos": {
|
||||
"name": "Simeon Picos",
|
||||
"desc": "Ogni volta che viene ferito, prende una pepita"
|
||||
},
|
||||
"Fantasma": {
|
||||
"name": "Fantasma",
|
||||
"desc": "Gioca su un giocatore eliminato. Quel giocatore torna in gioco, ma non può guadagnare né perdere punti vita."
|
||||
},
|
||||
"Lemat": {
|
||||
"name": "Lemat",
|
||||
"desc": "Puoi usare ogni carta in mano come BANG!."
|
||||
},
|
||||
"SerpenteASonagli": {
|
||||
"name": "Serpente a Sonagli",
|
||||
"desc": "Gioca su chiunque. All'inizio del turno, quel giocatore estrae una carta, e se è Picche, perde 1 punto vita."
|
||||
},
|
||||
"Shotgun": {
|
||||
"name": "Shotgun",
|
||||
"desc": "Ogni volta che ferisci un giocatore, deve scartare una carta a dalla sua mano."
|
||||
},
|
||||
"Taglia": {
|
||||
"name": "Taglia",
|
||||
"desc": "Gioca su chiunque. Se quel giocatore è colpito da BANG!, chi ha sparato ottiene una carta dal mazzo."
|
||||
},
|
||||
"Mira": {
|
||||
"name": "Mira",
|
||||
"desc": "Gioca questa carta assieme a una carta BANG!. Se il bersaglio viene colpito, perde 2 punti vita."
|
||||
},
|
||||
"RitornoDiFiamma": {
|
||||
"name": "Ritorno di Fiamma",
|
||||
"desc": "Vale una carta Mancato! Il giocatore che ha sparato è bersaglio di un BANG!."
|
||||
},
|
||||
"Bandidos": {
|
||||
"name": "Bandidos",
|
||||
"desc": "Ogni giocatore sceglie se scartare 2 carte dalla mano (o 1 se ne ha solo 1) o perdere 1 punto vita."
|
||||
},
|
||||
"Fuga": {
|
||||
"name": "Fuga",
|
||||
"desc": "Può essere giocata fuori turno. Evita l'effetto di una carta marrone (non BANG!) di cui sei uno dei bersagli."
|
||||
},
|
||||
"Sventagliata": {
|
||||
"name": "Sventagliata",
|
||||
"desc": "Conta come l'unico BANG! del turno. Anche un giocatore a tua scelta a distanza 1 dal bersaglio (se ce, te escluso) è bersaglio di un BANG."
|
||||
},
|
||||
"UltimoGiro": {
|
||||
"name": "Ultimo Giro",
|
||||
"desc": "Recupera 1 vita"
|
||||
},
|
||||
"Poker": {
|
||||
"name": "Poker",
|
||||
"desc": "Tutti gli altri scartano una carta dalla mano, allo stesso tempo. Se non c'è alcun Asso, pesca fino a 2 di quelle carte."
|
||||
},
|
||||
"Salvo": {
|
||||
"name": "Salvo!",
|
||||
"desc": "Può essere giocata fuori turno. Previeni la perdita di 1 punto vita di un altro giocatore. Se sopravvive, pesca 2 carte dalla sua mano o dal mazzo (scegli)."
|
||||
},
|
||||
"Tomahawk": {
|
||||
"name": "Tomahawk",
|
||||
"desc": "Attacca un giocatore fino a distanza 2."
|
||||
},
|
||||
"Tornado": {
|
||||
"name": "Tornado",
|
||||
"desc": "Tutti scartano una carta dalla mano (se possibile), poi ottengono 2 carte dal mazzo."
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
@ -724,7 +791,8 @@
|
||||
"allcharacters": "Tutti i personaggi",
|
||||
"highnooncards": "Carte Evento High Noon",
|
||||
"foccards": "Carte Evento Fistful of Cards",
|
||||
"goldrushcards": "Carte Gold Rush"
|
||||
"goldrushcards": "Carte Gold Rush",
|
||||
"valleyofshadowscards": "Carte The Valley of Shadows"
|
||||
},
|
||||
"theme": {
|
||||
"sepia": "Seppia",
|
||||
|
13911
frontend/yarn.lock
13911
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user