Compare commits

..

2 Commits

Author SHA1 Message Date
dependabot[bot]
a757531077
Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 04:48:22 +00:00
Alberto Xamin
9d9cd8fa75
mouse-pos-test 2023-04-04 11:45:18 +01:00
58 changed files with 2428 additions and 8136 deletions

View File

@ -4,68 +4,69 @@ on:
branches: main
jobs:
build-platform:
buildx:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [linux/amd64, linux/arm/v7, linux/arm64/v8]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
-
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: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Prepare Platform Tag
id: platform_tag
run: echo "platform_tag=$(echo ${{ matrix.platform }} | sed 's|/|-|g')" >> $GITHUB_ENV
- name: Build and push platform-specific image
uses: docker/build-push-action@v5
with:
context: .
platforms: ${{ matrix.platform }}
push: true
tags: albertoxamin/bang:${{ env.platform_tag }}
cache-from: type=registry,ref=albertoxamin/bang:${{ env.platform_tag }}
cache-to: type=inline
create-manifest:
runs-on: ubuntu-latest
needs: build-platform
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Create and push multi-arch manifest
-
name: Docker Buildx (build amd64 arm64)
run: |
docker buildx imagetools create \
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache" \
--platform linux/amd64 \
--output "type=image,push=false" \
--tag albertoxamin/bang:latest \
albertoxamin/bang:linux-amd64 \
albertoxamin/bang:linux-arm-v7 \
albertoxamin/bang:linux-arm64-v8
--file ./Dockerfile ./
-
name: Docker Buildx (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:latest \
--file ./Dockerfile ./
-
name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Docker Buildx (push all)
run: |
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--platform linux/amd64,linux/arm/v7 \
--output "type=image,push=true" \
--tag albertoxamin/bang:latest \
--file ./Dockerfile ./
-
name: Inspect image
run: |
docker buildx imagetools inspect albertoxamin/bang:latest
- name: Notify discord
uses: th0th/notify-discord@v0.4.1
if: ${{ always() }}
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_JOB_NAME: "Docker image main :latest"
GITHUB_JOB_STATUS: ${{ job.status }}

View File

@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -6,9 +6,3 @@ BANG! is a trademark owned by DVGiochi.
[Frontend Readme](./frontend/README.md)
[Backend Readme](./backend/Readme.md)
<img width="1326" alt="bang-lobby" src="https://github.com/albertoxamin/bang/assets/6067659/853a4182-aace-4f5d-92c3-ad63f309c37a">
<img width="1316" alt="bang-in-game" src="https://github.com/albertoxamin/bang/assets/6067659/fa4ad9f3-2012-4a38-b3eb-97fd0b4b8f2b">

View File

@ -20,7 +20,6 @@ class Suit(IntEnum):
HEARTS = 2 # ♥
SPADES = 3 # ♠
GOLD = 4 # 🤑
TRAIN = 5 # 🚂
class Card(ABC):
@ -67,7 +66,7 @@ class Card(ABC):
def __str__(self) -> str:
if str(self.suit).isnumeric():
char = ["♦️", "♣️", "♥️", "♠️", "🤑", "🚋"][int(self.suit)]
char = ["♦️", "♣️", "♥️", "♠️", "🤑"][int(self.suit)]
else:
char = self.suit
return f"{self.name} {char}{self.number}"
@ -125,13 +124,13 @@ class Card(ABC):
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_against{'_with' if _with else ''}|{player.name}|{self.name}|{against}|{_with.name if _with else ''}",
data=f"_play_card_against|{player.name}|{self.name}|{against}",
)
else:
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card{'_with' if _with else ''}|{player.name}|{self.name}|{_with.name if _with else ''}",
data=f"_play_card|{player.name}|{self.name}",
)
return True
@ -219,10 +218,8 @@ class Prigione(Card):
) -> bool:
if player.game.check_event(ce.IlGiudice):
return False
if (
against is not None
and not isinstance(player.game.get_player_named(against).role, r.Sheriff)
and not self.is_duplicate_card(player.game.get_player_named(against))
if against is not None and not isinstance(
player.game.get_player_named(against).role, r.Sheriff
):
self.can_be_used_now = False
G.sio.emit(
@ -371,18 +368,14 @@ class Birra(Card):
player.game.deck.draw(True, player=p)
p.notify_self()
if "gold_rush" in player.game.expansions and self.number != 42:
player.set_choose_action(
"choose_birra_function",
[
{
"name": "Pepita",
"icon": "💵️",
"alt_text": "1",
"noDesc": True,
},
from bang.players import PendingAction
player.available_cards = [
{"name": "Pepita", "icon": "💵️", "alt_text": "1", "noDesc": True},
self,
],
)
]
player.choose_text = "choose_birra_function"
player.pending_action = PendingAction.CHOOSE
player.notify_self()
return True
if (

View File

@ -1,17 +1,15 @@
from typing import List, Set, Dict, Tuple, Optional, TYPE_CHECKING
import random
import bang.cards as cs
import bang.expansions.fistful_of_cards.card_events as ce
import bang.expansions.high_noon.card_events as ceh
import bang.expansions.wild_west_show.card_events as cew
import bang.expansions.wild_west_show.characters as chw
import bang.expansions.gold_rush.shop_cards as grc
import bang.expansions.train_robbery.stations as trs
import bang.expansions.train_robbery.trains as trt
from globals import G
if TYPE_CHECKING:
from bang.game import Game
from bang.players import Player
class Deck:
@ -37,9 +35,6 @@ class Deck:
self.game = game
self.event_cards: List[ce.CardEvent] = []
self.event_cards_wildwestshow: List[ce.CardEvent] = []
self.stations: List[trs.StationCard] = []
self.train_pile: List[trt.TrainCard] = []
self.current_train: List[trt.TrainCard] = []
endgame_cards: List[ce.CardEvent] = []
if "fistful_of_cards" in game.expansions:
self.event_cards.extend(ce.get_all_events(game.rng))
@ -52,12 +47,6 @@ class Deck:
game.rng.shuffle(self.event_cards_wildwestshow)
self.event_cards_wildwestshow.insert(0, None)
self.event_cards_wildwestshow.append(cew.get_endgame_card())
if "train_robbery" in game.expansions:
self.stations = game.rng.sample(trs.get_all_stations(), len(game.players))
self.train_pile = trt.get_all_cards(game.rng)
self.current_train = [trt.get_locomotives(game.rng)[0]] + self.train_pile[
:3
]
if len(self.event_cards) > 0:
game.rng.shuffle(self.event_cards)
self.event_cards = self.event_cards[:12]
@ -117,21 +106,12 @@ class Deck:
self.shop_cards[i].reset_card()
self.game.notify_gold_rush_shop()
def move_train_forward(self):
if len(self.stations) == 0:
return
if len(self.current_train) == len(self.stations) + 4:
return
if len(self.current_train) > 0:
self.current_train.append(None)
self.game.notify_stations()
def peek(self, n_cards: int) -> list:
return self.cards[:n_cards]
def peek_scrap_pile(self, n_cards: int=1) -> List[cs.Card]:
def peek_scrap_pile(self) -> cs.Card:
if len(self.scrap_pile) > 0:
return self.scrap_pile[-n_cards:]
return self.scrap_pile[-1]
else:
return None
@ -161,15 +141,7 @@ class Deck:
and len(self.scrap_pile) > 0
and not ignore_event
):
card = self.draw_from_scrap_pile()
if player is not None and self.game.replay_speed > 0:
G.sio.emit(
"card_drawn",
room=self.game.name,
data={"player": player.name, "pile": "scrap"},
)
player.hand.append(card)
return card
return self.draw_from_scrap_pile()
card = self.cards.pop(0)
if len(self.cards) == 0:
self.reshuffle()
@ -196,7 +168,7 @@ class Deck:
else:
return self.draw()
def scrap(self, card: cs.Card, ignore_event:bool=False, player:'Player'=None):
def scrap(self, card: cs.Card, ignore_event=False, player=None):
if card.number == 42:
return
card.reset_card()

View File

@ -9,61 +9,11 @@ class DodgeCity():
from bang.expansions.dodge_city import cards
return cards.get_starting_deck()
def get_expansion_info(self):
return {
"id": "dodge_city",
"name": "Dodge City",
"cards": [
{"type": "characters", "cards": DodgeCity.get_characters()},
{"type": "cards", "cards": DodgeCity.get_cards()}
]
}
class HighNoon():
def get_events():
from bang.expansions.high_noon import card_events
return card_events.get_all_events() + [card_events.get_endgame_card()]
def get_expansion_info(self):
return {
"id": "high_noon",
"name": "High Noon",
"cards": [
{"type": "events", "cards": HighNoon.get_events()}
]
}
class FistfulOfCards():
def get_events():
from bang.expansions.fistful_of_cards import card_events
return card_events.get_all_events() + [card_events.get_endgame_card()]
def get_expansion_info(self):
return {
"id": "fistful_of_cards",
"name": "Fistful of Cards",
"cards": [
{"type": "events", "cards": FistfulOfCards.get_events()}
]
}
class GoldRush():
def get_characters():
from bang.expansions.gold_rush import characters
return characters.all_characters()
def get_shop_cards():
from bang.expansions.gold_rush import shop_cards
return shop_cards.get_cards()
def get_expansion_info(self):
return {
"id": "gold_rush",
"name": "Gold Rush",
"cards": [
{"type": "characters", "cards": GoldRush.get_characters()},
{"type": "cards", "cards": GoldRush.get_shop_cards()}
]
}
class TheValleyOfShadows():
def get_characters():
from bang.expansions.the_valley_of_shadows import characters
@ -73,74 +23,7 @@ class TheValleyOfShadows():
from bang.expansions.the_valley_of_shadows import cards
return cards.get_starting_deck()
def get_expansion_info(self):
return {
"id": "the_valley_of_shadows",
"name": "The Valley of Shadows",
"cards": [
{"type": "characters", "cards": TheValleyOfShadows.get_characters()},
{"type": "cards", "cards": TheValleyOfShadows.get_cards()}
]
}
class WildWestShow():
def get_characters():
from bang.expansions.wild_west_show import characters
return characters.all_characters()
def get_events():
from bang.expansions.wild_west_show import card_events
return card_events.get_all_events() + [card_events.get_endgame_card()]
def get_expansion_info(self):
return {
"id": "wild_west_show",
"name": "Wild West Show",
"cards": [
{"type": "characters", "cards": WildWestShow.get_characters()},
{"type": "events", "cards": WildWestShow.get_events()}
]
}
class TrainRobbery():
def get_stations():
from bang.expansions.train_robbery import stations
return stations.get_all_stations()
def get_trains():
from bang.expansions.train_robbery import trains
return trains.get_all_cards() + trains.get_locomotives()
def get_expansion_info(self):
return {
"id": "train_robbery",
"name": "Train Robbery",
"cards": [
{"type": "stations", "cards": TrainRobbery.get_stations()},
{"type": "trains", "cards": TrainRobbery.get_trains()}
]
}
def get_expansion_info(expansion_id):
expansion_map = {
"dodge_city": DodgeCity(),
"high_noon": HighNoon(),
"fistful_of_cards": FistfulOfCards(),
"gold_rush": GoldRush(),
"the_valley_of_shadows": TheValleyOfShadows(),
"wild_west_show": WildWestShow(),
"train_robbery": TrainRobbery()
}
expansion_info = expansion_map[expansion_id].get_expansion_info()
for section in expansion_info["cards"]:
unique_cards = []
seen_card = set()
for card in section["cards"]:
if card.name not in seen_card:
unique_cards.append(card)
seen_card.add(card.name)
section["cards"] = unique_cards
return expansion_info

View File

@ -2,25 +2,22 @@ from bang.cards import *
import bang.expansions.fistful_of_cards.card_events as ce
from globals import G
class Binocolo(Mirino):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Binocolo"
self.icon = "🔍"
self.name = 'Binocolo'
self.icon = '🔍'
class Riparo(Mustang):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Riparo"
self.icon = ""
self.name = 'Riparo'
self.icon = ''
class Pugno(Card):
def __init__(self, suit, number):
super().__init__(suit, "Pugno!", number, range=1)
self.icon = "👊"
super().__init__(suit, 'Pugno!', number, range=1)
self.icon = '👊'
self.alt_text = "1🔎 💥"
# self.desc = "Spara a un giocatore a distanza 1"
# self.desc_eng = "Shoot a player at distance 1"
@ -33,12 +30,11 @@ class Pugno(Card):
return True
return False
class Schivata(Mancato):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Schivata"
self.icon = "🙅‍♂️"
self.name = 'Schivata'
self.icon = '🙅‍♂️'
# self.desc += " e poi pesca una carta"
# self.desc_eng += " and then draw a card."
self.alt_text = "😅 | 🎴"
@ -50,44 +46,37 @@ class Schivata(Mancato):
player.game.deck.draw(True, player=player)
player.notify_self()
class RagTime(Panico):
def __init__(self, suit, number):
Card.__init__(self, suit, "Rag Time", number)
self.icon = "🎹"
Card.__init__(self, suit, 'Rag Time', number)
self.icon = '🎹'
# self.desc = "Ruba 1 carta da un giocatore a prescindere dalla distanza"
# self.desc_eng = "Steal a card from another player at any distance"
self.need_target = True
self.need_with = True
self.alt_text = "2🃏 | 👤😱"
self.alt_text = '2🃏 | 👤😱'
def play_card(self, player, against, _with):
if against is not None and _with is not None:
player.game.deck.scrap(_with)
super().play_card(player, against=against, _with=_with)
super().play_card(player, against=against)
return True
return False
class Rissa(CatBalou):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Rissa"
self.icon = "🥊"
self.name = 'Rissa'
self.icon = '🥊'
# self.desc = "Fai scartare una carta a tutti gli altri giocatori, scegli a caso dalla mano, oppure fra quelle che hanno in gioco"
# self.desc_eng = "Choose a card to discard from the hand/equipment of all the other players"
self.need_with = True
self.need_target = False
self.alt_text = "2🃏 | 👤💃"
self.alt_text = '2🃏 | 👤💃'
def play_card(self, player, against, _with):
if _with is not None:
if not any(
(
p != player and (len(p.hand) + len(p.equipment)) > 0
for p in player.game.players
)
):
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 = []
@ -97,43 +86,35 @@ class Rissa(CatBalou):
player.rissa_targets.append(target)
target = player.game.get_player_named(target.name, next=True)
player.game.deck.scrap(_with)
player.event_type = "rissa"
print(f"rissa targets: {player.rissa_targets}")
super().play_card(
player, against=player.rissa_targets.pop(0).name, _with=_with
)
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card|{player.name}|{self.name}",
)
player.event_type = 'rissa'
print(f'rissa targets: {player.rissa_targets}')
super().play_card(player, against=player.rissa_targets.pop(0).name)
G.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}')
return True
return False
class SpringField(Card):
def __init__(self, suit, number):
super().__init__(suit, "Springfield", number)
self.icon = "🌵"
super().__init__(suit, 'Springfield', number)
self.icon = '🌵'
# self.desc = "Spara a un giocatore a prescindere dalla distanza"
# self.desc_eng = "Shoot a player at any distance"
self.need_target = True
self.need_with = True
self.alt_text = "2🃏 | 👤💥"
self.alt_text = '2🃏 | 👤💥'
def play_card(self, player, against, _with=None):
if against is not None and _with is not None:
player.game.deck.scrap(_with)
super().play_card(player, against=against, _with=_with)
super().play_card(player, against=against)
player.game.attack(player, against, card_name=self.name)
return True
return False
class Tequila(Card):
def __init__(self, suit, number):
super().__init__(suit, "Tequila", number)
self.icon = "🍹"
super().__init__(suit, 'Tequila', number)
self.icon = '🍹'
# self.desc = "Fai recuperare 1 vita a un giocatore a tua scelta, anche te stesso"
# self.desc_eng = "Heal 1 HP to a player of your choice (can be you)"
self.need_target = True
@ -143,45 +124,36 @@ class Tequila(Card):
def play_card(self, player, against, _with=None):
if against is not None and _with is not None:
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_for|{player.name}|{self.name}|{against}",
)
G.sio.emit('chat_message', room=player.game.name, data=f'_play_card_for|{player.name}|{self.name}|{against}')
player.game.deck.scrap(_with)
player.game.get_player_named(against).lives = min(
player.game.get_player_named(against).lives + 1,
player.game.get_player_named(against).max_lives,
)
player.game.get_player_named(against).lives = min(player.game.get_player_named(against).lives+1, player.game.get_player_named(against).max_lives)
player.game.get_player_named(against).notify_self()
return True
return False
class Whisky(Card):
def __init__(self, suit, number):
super().__init__(suit, "Whisky", number)
self.icon = "🥃"
super().__init__(suit, 'Whisky', number)
self.icon = '🥃'
# self.desc = "Gioca questa carta per recuperare fino a 2 punti vita"
# self.desc_eng = "Heal 2 HP"
self.need_with = True
self.alt_text = "2🃏 | 🍺🍺"
self.alt_text = '2🃏 | 🍺🍺'
def play_card(self, player, against, _with=None):
if _with is not None:
super().play_card(player, against=against, _with=_with)
super().play_card(player, against=against)
player.game.deck.scrap(_with)
player.lives = min(player.lives+2, player.max_lives)
player.notify_self()
return True
return False
class Bibbia(Schivata):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Bibbia"
self.icon = "📖"
self.name = 'Bibbia'
self.icon = '📖'
self.usable_next_turn = True
self.can_be_used_now = False
@ -190,26 +162,18 @@ class Bibbia(Schivata):
pass
return False
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class Cappello(Mancato):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Cappello"
self.icon = "🧢"
self.name = 'Cappello'
self.icon = '🧢'
self.usable_next_turn = True
self.can_be_used_now = False
self.alt_text = "😅"
@ -220,40 +184,30 @@ class Cappello(Mancato):
return False
else:
self.reset_card()
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class PlaccaDiFerro(Cappello):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Placca Di Ferro"
self.icon = "🛡"
self.name = 'Placca Di Ferro'
self.icon = '🛡'
class Sombrero(Cappello):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Sombrero"
self.icon = "👒"
self.name = 'Sombrero'
self.icon = '👒'
class Pugnale(Pugno):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Pugnale"
self.icon = "🗡"
self.name = 'Pugnale'
self.icon = '🗡'
self.usable_next_turn = True
self.can_be_used_now = False
@ -262,27 +216,19 @@ class Pugnale(Pugno):
return super().play_card(player, against=against)
else:
self.reset_card()
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class Derringer(Pugnale):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Derringer"
self.icon = "🚬"
self.alt_text += " 🎴"
self.name = 'Derringer'
self.icon = '🚬'
self.alt_text += ' 🎴'
# self.desc += ' e poi pesca una carta'
# self.desc_eng += ' and then draw a card.'
@ -291,15 +237,8 @@ class Derringer(Pugnale):
player.game.deck.draw(True, player=player)
return super().play_card(player, against=against)
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
@ -309,11 +248,10 @@ class Derringer(Pugnale):
player.game.deck.draw(True, player=player)
player.notify_self()
class Borraccia(Card):
def __init__(self, suit, number):
super().__init__(suit, "Borraccia", number)
self.icon = "🍼"
super().__init__(suit, 'Borraccia', number)
self.icon = '🍼'
# self.desc = 'Recupera 1 vita'
# self.desc_eng = 'Regain 1 HP'
self.alt_text = "🍺"
@ -327,26 +265,18 @@ class Borraccia(Card):
player.notify_self()
return True
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class PonyExpress(WellsFargo):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Pony Express"
self.icon = "🦄"
self.name = 'Pony Express'
self.icon = '🦄'
self.alt_text = "🎴🎴🎴"
self.usable_next_turn = True
self.can_be_used_now = False
@ -355,26 +285,18 @@ class PonyExpress(WellsFargo):
if self.can_be_used_now:
return super().play_card(player, against)
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class Howitzer(Gatling):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Howitzer"
self.icon = "📡"
self.name = 'Howitzer'
self.icon = '📡'
self.alt_text = "👥💥"
self.usable_next_turn = True
self.can_be_used_now = False
@ -383,21 +305,13 @@ class Howitzer(Gatling):
if self.can_be_used_now:
return super().play_card(player, against)
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class CanCan(CatBalou):
def __init__(self, suit, number):
super().__init__(suit, number)
@ -411,24 +325,16 @@ class CanCan(CatBalou):
if self.can_be_used_now:
return super().play_card(player, against)
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class Conestoga(Panico):
def __init__(self, suit, number):
Card.__init__(self, suit, "Conestoga", number)
Card.__init__(self, suit, 'Conestoga', number)
self.icon = "🏕"
# self.desc = "Ruba 1 carta da un giocatore a prescindere dalla distanza"
# self.desc_eng = "Steal a card from another player at any distance"
@ -441,26 +347,18 @@ class Conestoga(Panico):
if self.can_be_used_now:
return super().play_card(player, against)
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class Pepperbox(Bang):
def __init__(self, suit, number):
super().__init__(suit, number)
self.name = "Pepperbox"
self.icon = "🌶"
self.name = 'Pepperbox'
self.icon = '🌶'
self.alt_text = "💥"
self.usable_next_turn = True
self.can_be_used_now = False
@ -473,25 +371,17 @@ class Pepperbox(Bang):
return True
return False
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
class FucileDaCaccia(Card):
def __init__(self, suit, number):
super().__init__(suit, "Fucile Da Caccia", number)
self.icon = "🌂"
super().__init__(suit, 'Fucile Da Caccia', number)
self.icon = '🌂'
# self.desc = "Spara a un giocatore a prescindere dalla distanza"
self.alt_text = "👤💥"
self.need_target = True
@ -506,66 +396,58 @@ class FucileDaCaccia(Card):
return True
return False
else:
if not self.is_duplicate_card(player) and not player.game.check_event(
ce.IlGiudice
):
if not self.is_duplicate_card(player) and not player.game.check_event(ce.IlGiudice):
self.reset_card()
G.sio.emit(
"chat_message",
room=player.game.name,
data=f"_play_card_green|{player.name}|{self.name}",
)
player.equipment.append(self)
return True
else:
return False
# pylint: disable=function-redefined
def get_starting_deck() -> List[Card]:
cards = [
Barile(Suit.CLUBS, "A"),
Barile(Suit.CLUBS, 'A'),
Binocolo(Suit.DIAMONDS, 10),
Dinamite(Suit.CLUBS, 10),
Mustang(Suit.HEARTS, 5),
Remington(Suit.DIAMONDS, 6),
RevCarabine(Suit.SPADES, 5),
Riparo(Suit.DIAMONDS, "K"),
Riparo(Suit.DIAMONDS, 'K'),
Bang(Suit.SPADES, 8),
Bang(Suit.CLUBS, 5),
Bang(Suit.CLUBS, 6),
Bang(Suit.CLUBS, "K"),
Bang(Suit.CLUBS, 'K'),
Birra(Suit.HEARTS, 6),
Birra(Suit.SPADES, 6),
CatBalou(Suit.CLUBS, 8),
Emporio(Suit.SPADES, "A"),
Emporio(Suit.SPADES, 'A'),
Indiani(Suit.DIAMONDS, 5),
Mancato(Suit.DIAMONDS, 8),
Panico(Suit.HEARTS, "J"),
Panico(Suit.HEARTS, 'J'),
Pugno(Suit.SPADES, 10),
RagTime(Suit.HEARTS, 9),
Rissa(Suit.SPADES, "J"),
Rissa(Suit.SPADES, 'J'),
Schivata(Suit.DIAMONDS, 7),
Schivata(Suit.HEARTS, "K"),
SpringField(Suit.SPADES, "K"),
Schivata(Suit.HEARTS, 'K'),
SpringField(Suit.SPADES, 'K'),
Tequila(Suit.CLUBS, 9),
Whisky(Suit.HEARTS, "Q"),
Whisky(Suit.HEARTS, 'Q'),
Bibbia(Suit.HEARTS, 10),
Cappello(Suit.DIAMONDS, "J"),
PlaccaDiFerro(Suit.DIAMONDS, "A"),
PlaccaDiFerro(Suit.SPADES, "Q"),
Cappello(Suit.DIAMONDS, 'J'),
PlaccaDiFerro(Suit.DIAMONDS, 'A'),
PlaccaDiFerro(Suit.SPADES, 'Q'),
Sombrero(Suit.CLUBS, 7),
Pugnale(Suit.HEARTS, 8),
Derringer(Suit.SPADES, 7),
Borraccia(Suit.HEARTS, 7),
CanCan(Suit.CLUBS, "J"),
CanCan(Suit.CLUBS, 'J'),
Conestoga(Suit.DIAMONDS, 9),
FucileDaCaccia(Suit.CLUBS, "Q"),
PonyExpress(Suit.DIAMONDS, "Q"),
Pepperbox(Suit.HEARTS, "A"),
FucileDaCaccia(Suit.CLUBS, 'Q'),
PonyExpress(Suit.DIAMONDS, 'Q'),
Pepperbox(Suit.HEARTS, 'A'),
Howitzer(Suit.SPADES, 9),
]
for c in cards:
c.expansion_icon = "🐄️"
c.expansion = "dodge_city"
c.expansion_icon = '🐄️'
c.expansion = 'dodge_city'
return cards

View File

@ -1,6 +1,5 @@
from typing import List
from bang.characters import Character
from globals import PendingAction
class PixiePete(Character):
@ -166,6 +165,8 @@ class DocHolyday(Character):
def special(self, player, data):
if super().special(player, data):
from bang.players import PendingAction
if (
player.special_use_count < 1
and player.pending_action == PendingAction.PLAY

View File

@ -1,4 +1,3 @@
from typing import TYPE_CHECKING
from abc import ABC, abstractmethod
import random
import bang.players as players
@ -7,9 +6,6 @@ import bang.cards as cs
from globals import G
if TYPE_CHECKING:
from bang.game import Game
class CardEvent(ABC):
"""Base class for all event cards"""
@ -18,7 +14,7 @@ class CardEvent(ABC):
self.name = name
self.icon = icon
def on_flipped(self, game: "Game"):
def on_flipped(self, game):
"""Default on flipped event
Args:

View File

@ -1,26 +1,23 @@
from typing import List
from bang.characters import Character
class DonBell(Character):
def __init__(self):
super().__init__("Don Bell", max_lives=4)
# A fine turno estrae, ❤️ o ♦️ gioca di nuovo
self.icon = "🔔️"
self.icon = '🔔️'
class DutchWill(Character):
def __init__(self):
super().__init__("Dutch Will", max_lives=4)
# Pesca 2 ne scarta 1 e prende 1 pepita
self.icon = "🧐️"
self.icon = '🧐️'
class JackyMurieta(Character):
def __init__(self):
super().__init__("Jacky Murieta", max_lives=4)
# puo pagare 2 pepite per sparare 1 bang extra
self.icon = "💆‍♂️️"
self.icon = '💆‍♂️️'
def special(self, player, data):
if super().special(player, data):
@ -32,55 +29,45 @@ class JackyMurieta(Character):
return True
return False
class JoshMcCloud(Character):
def __init__(self):
super().__init__("Josh McCloud", max_lives=4)
# puo pagare 2 pepite per pescare il primo equipaggiamento dalla pila gold rush
self.icon = "⛅️"
self.icon = '⛅️'
def special(self, player, data):
if super().special(player, data):
if player.gold_nuggets >= 2 and player.is_my_turn:
player.gold_nuggets -= 2
card = player.game.deck.shop_deck.pop(0)
print(
f"{player.name} ha comprato usando la abilità speciale {card.name}"
)
print(f'{player.name} ha comprato usando la abilità speciale {card.name}')
if card.play_card(player):
player.game.deck.shop_deck.append(card)
player.notify_self()
return True
return False
class MadamYto(Character):
def __init__(self):
super().__init__("Madam Yto", max_lives=4)
# quando viene giocata 1 birra pesca 1 carta
self.icon = "💃️"
self.icon = '💃️'
class PrettyLuzena(Character):
def __init__(self):
super().__init__("Pretty Luzena", max_lives=4)
# una volta per turno ha 1 sconto di 1 pepita sugli equipaggiamenti
self.icon = "👛️"
self.icon = '👛️'
class RaddieSnake(Character):
def __init__(self):
super().__init__("Raddie Snake", max_lives=4)
# può scartare 1 pepita per pescare 1 carta (2 volte per turno)
self.icon = "🐍️"
self.icon = '🐍️'
def special(self, player, data):
if super().special(player, data):
if (
player.gold_nuggets >= 1
and player.is_my_turn
and player.special_use_count < 2
):
if player.gold_nuggets >= 1 and player.is_my_turn and player.special_use_count < 2:
player.gold_nuggets -= 1
player.special_use_count += 1
player.game.deck.draw(True, player=player)
@ -88,12 +75,11 @@ class RaddieSnake(Character):
return True
return False
class SimeonPicos(Character):
def __init__(self):
super().__init__("Simeon Picos", max_lives=4)
# ottiene 1 pepita ogni volta che perde 1 punto vita
self.icon = "🥲"
self.icon = '🥲'
def all_characters() -> List[Character]:
@ -108,6 +94,6 @@ def all_characters() -> List[Character]:
SimeonPicos(),
]
for c in cards:
c.expansion_icon = "🤑️"
c.expansion = "gold_rush"
c.expansion_icon = '🤑️'
c.expansion = 'gold_rush'
return cards

View File

@ -1,7 +1,7 @@
from bang.cards import *
import bang.roles as r
import bang.players as pl
from globals import G, PendingAction
from globals import G
class ShopCardKind(IntEnum):
BROWN = 0 # Se lequipaggiamento ha il bordo marrone, applicane subito leffetto e poi scartalo.
@ -47,7 +47,7 @@ class Bicchierino(ShopCard):
'is_player': True
} for p in player.game.get_alive_players()]
player.choose_text = 'choose_bicchierino'
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.notify_self()
return super().play_card(player, against, _with)
@ -64,7 +64,7 @@ class Bottiglia(ShopCard):
for i in range(len(player.available_cards)):
player.available_cards[i].must_be_used = True
player.choose_text = 'choose_bottiglia'
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.notify_self()
return super().play_card(player, against, _with)
@ -79,7 +79,7 @@ class Complice(ShopCard):
for i in range(len(player.available_cards)):
player.available_cards[i].must_be_used = True
player.choose_text = 'choose_complice'
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.notify_self()
return super().play_card(player, against, _with)
@ -175,7 +175,7 @@ class Ricercato(ShopCard):
} for p in player.game.get_alive_players() if p != player and not isinstance(p.role, r.Sheriff)]
player.available_cards.append({'name': player.name, 'number':0,'icon': 'you', 'is_character': True})
player.choose_text = 'choose_ricercato'
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.notify_self()
return True
# la giochi su un altro giocatore, ricompensa di 2 carte e 1 pepita a chi lo uccide

View File

@ -3,7 +3,7 @@ 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
from globals import G, PendingAction
from globals import G
class Fantasma(Card):
@ -15,7 +15,7 @@ class Fantasma(Card):
if (player.game.check_event(ce.IlGiudice)) or not self.can_be_used_now:
return False
if len(player.game.get_dead_players(include_ghosts=False)) > 0:
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.choose_text = "choose_fantasma"
player.available_cards = [
{
@ -60,7 +60,11 @@ class Lemat(Card):
for p in player.game.get_visible_players(player)
)
):
player.set_choose_action("choose_play_as_bang", player.hand.copy())
from bang.players import PendingAction
player.available_cards = player.hand.copy()
player.pending_action = PendingAction.CHOOSE
player.choose_text = "choose_play_as_bang"
player.notify_self()
return False
@ -181,7 +185,7 @@ class Sventagliata(
if p["name"] != player.name and p["name"] != t.name and p["dist"]
]
if len(player.available_cards) > 0:
player.pending_action = PendingAction.CHOOSE
player.pending_action = pl.PendingAction.CHOOSE
player.choose_text = "choose_sventagliata"
else:
player.available_cards = []

View File

@ -21,11 +21,12 @@ class BlackFlower(Character):
for p in player.game.get_visible_players(player)
)
) and super().special(player, data):
from bang.players import PendingAction
player.available_cards = [c for c in player.hand if c.suit == cs.Suit.CLUBS]
player.special_use_count += 1
player.set_choose_action(
"choose_play_as_bang",
[c for c in player.hand if c.suit == cs.Suit.CLUBS],
)
player.pending_action = PendingAction.CHOOSE
player.choose_text = "choose_play_as_bang"
player.notify_self()

View File

@ -1,315 +0,0 @@
from typing import TYPE_CHECKING
import bang.cards as cs
from globals import PendingAction
if TYPE_CHECKING:
from bang.players import Player
class StationCard:
def __init__(self, name: str):
self.name = name
self.expansion = "train_robbery"
self.price: list[dict] = []
self.attached_train = None
def discard_and_buy_train(self, player: "Player", card_index: int):
"""Discard the card and buy the train"""
if self.attached_train is None:
return
card = player.available_cards.pop(card_index)
for i, card in enumerate(player.hand):
if card == self:
player.hand.pop(i)
break
else:
player.lives -= 1
card = player.hand.pop(card_index)
player.game.deck.scrap(card, True, player=player)
player.equipment.append(self.attached_train)
self.attached_train = None
player.pending_action = PendingAction.PLAY
def check_price(self, player: "Player") -> bool:
"""Check if the card can be used to rob the train"""
return len(player.hand) > 0
class BoomTown(StationCard):
"""Discard a Bang! to rob the train"""
def __init__(self):
super().__init__("Boom Town")
self.price = [cs.Bang(0, 0).__dict__]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not isinstance(c, cs.Bang) for c in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if isinstance(c, cs.Bang)],
self.discard_and_buy_train,
)
return True
class Caticor(StationCard):
"""Discard a Cat Balou or Panico to rob the train"""
def __init__(self):
super().__init__("Caticor")
self.price = [cs.CatBalou(0, 0).__dict__, cs.Panico(0, 0).__dict__]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not (isinstance(card, cs.CatBalou) or isinstance(card, cs.Panico))
for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[
c
for c in player.hand
if isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico)
],
self.discard_and_buy_train,
)
return True
class CreepyCreek(StationCard):
"""Discard a card of spades to rob the train"""
def __init__(self):
super().__init__("Creepy Creek")
self.price = [{"icon": "♠️"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
card.suit != cs.Suit.SPADES for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.suit == cs.Suit.SPADES],
self.discard_and_buy_train,
)
return True
class CrownsHole(StationCard):
"""Discard a beer to rob the train"""
def __init__(self):
super().__init__("Crowns Hole")
self.price = [cs.Birra(0, 0).__dict__]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not isinstance(card, cs.Birra) for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if isinstance(c, cs.Birra)],
self.discard_and_buy_train,
)
return True
class Deadwood(StationCard):
"""Discard an equipment card to rob the train"""
def __init__(self):
super().__init__("Deadwood")
self.price = [{"is_equipment": True}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not card.is_equipment for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.is_equipment],
self.discard_and_buy_train,
)
return True
class Dodgeville(StationCard):
"""Discard a Missed! to rob the train"""
def __init__(self):
super().__init__("Dodgeville")
self.price = [cs.Mancato(0, 0).__dict__]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not isinstance(card, cs.Mancato) for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if isinstance(c, cs.Mancato)],
self.discard_and_buy_train,
)
return True
class FortWorth(StationCard):
"""Discard a card with number 10, J, Q, K, A to rob the train"""
def __init__(self):
super().__init__("Fort Worth")
self.price = [{"icon": "10\nJ\nQ\nK\nA"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
card.number not in {1, 10, 11, 12, 13} for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.number in {1, 10, 11, 12, 13}],
self.discard_and_buy_train,
)
return True
class Frisco(StationCard):
"""Discard a card of clubs to rob the train"""
def __init__(self):
super().__init__("Frisco")
self.price = [{"icon": "♣️"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
card.suit != cs.Suit.CLUBS for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.suit == cs.Suit.CLUBS],
self.discard_and_buy_train,
)
return True
class MinersOath(StationCard):
"""Discard a card of diamonds to rob the train"""
def __init__(self):
super().__init__("Miners Oath")
self.price = [{"icon": "♦️"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
card.suit != cs.Suit.DIAMONDS for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.suit == cs.Suit.DIAMONDS],
self.discard_and_buy_train,
)
return True
class SanTafe(StationCard):
"""Discard a card of hearts to rob the train"""
def __init__(self):
super().__init__("San Tafe")
self.price = [{"icon": "♥️"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
card.suit != cs.Suit.HEARTS for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if c.suit == cs.Suit.HEARTS],
self.discard_and_buy_train,
)
return True
class Tombrock(StationCard):
"""Lose 1 life point to rob the train"""
def __init__(self):
super().__init__("Tombrock")
self.price = [{"icon": "💔"}]
def check_price(self, player: "Player"):
if player.lives <= 1:
return False
player.set_choose_action(
"choose_buy_train",
[{"icon": "💔"}],
self.discard_and_buy_train,
)
return True
class Yooma(StationCard):
"""Discard a card with number between 2 and 9 to rob the train"""
def __init__(self):
super().__init__("Yooma")
self.price = [{"icon": "2-9"}]
def check_price(self, player: "Player"):
if super().check_price(player) and all(
not (2 <= card.number <= 9) for card in player.hand
):
return False
player.set_choose_action(
"choose_buy_train",
[c for c in player.hand if 2 <= c.number <= 9],
self.discard_and_buy_train,
)
return True
class VirginiaTown(StationCard):
"""Discard two cards to rob the train"""
def __init__(self):
super().__init__("Virginia Town")
self.price = [{}, {}]
def check_price(self, player: "Player"):
if super().check_price(player) and len(player.hand) < 2:
return False
player.set_choose_action(
"choose_buy_train",
player.hand.copy(),
self.discard_and_buy_train,
)
return True
def get_all_stations():
"""Return a list of all the station cards"""
return [
BoomTown(),
Caticor(),
CreepyCreek(),
CrownsHole(),
Deadwood(),
Dodgeville(),
FortWorth(),
Frisco(),
MinersOath(),
SanTafe(),
Tombrock(),
Yooma(),
VirginiaTown(),
]

View File

@ -1,406 +0,0 @@
import random
from bang.cards import Card, Bang, Panico, CatBalou, Mancato
from typing import TYPE_CHECKING
from globals import G, PendingAction
if TYPE_CHECKING:
from bang.players import Player
class TrainCard(Card):
def __init__(self, name: str, is_locomotive: bool = False):
super().__init__(suit=5, number=0, name=name)
self.expansion_icon = "🚂"
self.is_equipment = True
self.is_locomotive = is_locomotive
self.expansion = "train_robbery"
self.type = "train"
self.implemented = True
# Circus Wagon: gli altri giocatori
# scartano una carta, in senso orario, a
# partire dal giocatore alla tua sinistra.
# Express Car: non puoi svolgere
# un altro turno extra dopo quello
# ottenuto da questo effetto, anche se
# riesci a giocare di nuovo Express Car.
# Ghost Car: giocabile su un
# qualsiasi giocatore, anche se già
# eliminato, te compreso. Non può
# essere giocato sullo Sceriffo.
# Se quel giocatore è/viene eliminato,
# invece ritorna/resta in gioco, senza
# punti vita. Non può guadagnare né
# perdere punti vita, e viene considerato
# un personaggio in gioco per tutti gli
# effetti (condizioni di vittoria, distanza
# tra giocatori, abilità dei personaggi,
# ecc.). Non avendo punti vita, deve
# scartare la sua intera mano alla fine
# del turno, ma può tenere qualsiasi
# carta in gioco di fronte a sé, incluso
# Ghost Car. Tuttavia, è eliminato
# dal gioco non appena Ghost Car
# viene scartato: nessuna ricompensa
# viene assegnata in questo caso se il
# giocatore è un Fuorilegge, e le abilità
# dei personaggi (ad es. Vulture Sam)
# non si attivano
# Lounge Car: i vagoni che peschi
# non contano per il normale limite di
# acquisizione di 1 vagone per turno. Se
# sei lo Sceriffo e peschi Ghost Car, devi
# darlo a un altro giocatore.
# Lumber Flatcar: gioca su un
# qualsiasi giocatore (compreso te).
# Finché questa carta è in gioco, quel
# giocatore vede gli altri giocatori a
# distanza aumentata di 1.
# Private Car: questo effetto non
# ti protegge da Gatling, Knife Revolver,
# labilità di Evan Babbit, e così via.
# Sleeper Car: puoi anche usare
# leffetto una volta per turno con
# Indiani!, Duello, ecc.
class Ironhorse(TrainCard):
"""LOCOMOTIVA:
Ogni giocatore, incluso colui che ha attivato l'effetto, è bersaglio di un BANG!
Nessun giocatore è responsabile dell'eventuale perdita di punti vita.
Se tutti i giocatori vengono eliminati allo stesso tempo, i Fuorilegge vincono.
"""
def __init__(self):
super().__init__("Ironhorse", is_locomotive=True)
self.icon = "🚂"
def play_card(self, player, against=None, _with=None) -> bool:
player.game.attack(player, player.name, card_name=self.name)
player.game.attack_others(player, card_name=self.name)
return True
class Leland(TrainCard):
"""
LOCOMOTIVA: svolgi l'effetto dell'Emporio, cominciando dal giocatore di turno e procedendo in senso orario.
"""
def __init__(self):
super().__init__("Leland", is_locomotive=True)
self.icon = "🚂"
def play_card(self, player, against=None, _with=None) -> bool:
player.game.emporio(player)
return True
class BaggageCar(TrainCard):
"""Scartalo: ottieni l'effetto di un Mancato!, Panico!, Cat Balou o di un BANG! extra.
Discard this for a Missed! Panic!, Cat Balou, or an extra BANG!"""
def __init__(self):
super().__init__("Baggage Car")
self.icon = "🚋🛄"
def choose_callback(self, player: 'Player', card_index):
player.hand.append(player.available_cards[card_index])
player.pending_action = PendingAction.PLAY
def play_card(self, player, against=None, _with=None) -> bool:
player.set_choose_action(
"choose_baggage_car",
[Bang(4, 42), Mancato(4, 42), CatBalou(4, 42), Panico(4, 42)],
self.choose_callback,
)
return True
class Caboose(TrainCard):
"""Puoi scartare un altra tua carta bordo blu incuso un vagone come se fosse un Mancato!"""
def __init__(self):
super().__init__("Caboose")
self.icon = "🚋"
def play_card(self, player, against=None, _with=None) -> bool:
return False
class CattleTruck(TrainCard):
"""Scartalo: guarda le 3 carte in cima agli scarti e pescane I"""
def __init__(self):
super().__init__("Cattle Truck")
self.icon = "🚋🐄"
def choose_card_callback(self, player: 'Player', card_index):
chosen_card = player.available_cards.pop(card_index)
player.game.deck.scrap_pile.pop(-card_index)
player.hand.append(chosen_card)
player.pending_action = PendingAction.PLAY
player.notify_self()
def play_card(self, player, against=None, _with=None) -> bool:
drawn_cards = player.game.deck.peek_scrap_pile(n_cards=3)
player.set_choose_action(
"choose_cattle_truck",
drawn_cards,
self.choose_card_callback,
)
return True
class CircusWagon(TrainCard):
"""Scartalo: ogni altro giocatore deve scartare una carta che ha in gioco."""
def __init__(self):
super().__init__("Circus Wagon", is_locomotive=True)
self.icon = "🚋🎪"
def play_card(self, player, against=None, _with=None) -> bool:
player.game.discard_others(player, card_name=self.name)
return True
@classmethod
def choose_circus_wagon(cls, player: 'Player', card_index):
player.game.deck.scrap(player.hand.pop(card_index), player=player)
player.pending_action = PendingAction.WAIT
player.game.responders_did_respond_resume_turn()
player.notify_self()
class CoalHopper(TrainCard):
"""Scartalo: pesca una carta e scarta un vagone in gioco davanti a un giocatore a tua scelta."""
def __init__(self):
super().__init__("Coal Hopper")
self.icon = "🚋🔥"
self.need_target = True
def play_card(self, player, against=None, _with=None) -> bool:
if against is not None and len(player.game.get_player_named(against).equipment) > 0:
player.game.steal_discard(player, against, self)
return True
class DiningCar(TrainCard):
"""A inizio turno, "estrai!": se è Cuori, recuperi I punto vita."""
def __init__(self):
super().__init__("Dining Car")
self.icon = "🚋🍽"
def play_card(self, player, against=None, _with=None) -> bool:
return False
class ExpressCar(TrainCard):
"""Scarta tutte le carte in mano, poi gioca un altro turno"""
def __init__(self):
super().__init__("Express Car")
self.icon = "🚋⚡"
def play_card(self, player, against=None, _with=None) -> bool:
while len(player.hand) > 0:
player.game.deck.scrap(player.hand.pop(0), player=player)
player.notify_self()
player.play_turn()
return True
class GhostCar(TrainCard):
"""Giocalo su chiunque tranne lo Sceritfo. Se vieni eliminato, invece resta in gioco, ma non puo guadagnare ne perdere punti vita."""
def __init__(self):
super().__init__("Ghost Car")
self.icon = "🚋👻"
self.implemented = False
def play_card(self, player, against=None, _with=None) -> bool:
return False
class LoungeCar(TrainCard):
"""Scartalo: pesca 2 vagoni dal mazzo, mettine I in gioco di fronte a te e 1 di fronte a un altro giocatore."""
def __init__(self):
super().__init__("Lounge Car")
self.icon = "🚋🛋"
self.implemented = False
def play_card(self, player, against=None, _with=None) -> bool:
return True
class LumberFlatcar(TrainCard):
"""Giocalo su un qualsiasi giocatore (compreso te). Finché questa carta è in gioco, quel giocatore vede gli altri giocatori a distanza aumentata di 1."""
def __init__(self):
super().__init__("Lumber Flatcar")
self.icon = "🚋🪵"
self.sight_mod = -1
def play_card(self, player, against=None, _with=None) -> bool:
return False
class MailCar(TrainCard):
"""Scartalo: pesca 3 carte e dai 1 di esse a un altro giocatore a tua scelta."""
def __init__(self):
super().__init__("Mail Car")
self.icon = "🚋📮"
def choose_card_callback(self, player: 'Player', card_index):
chosen_card = player.available_cards.pop(card_index)
player.hand.extend(player.available_cards)
player.set_choose_action(
"choose_other_player",
player.game.get_other_players(player),
lambda p, other_player_index: self.choose_player_callback(p, other_player_index, chosen_card)
)
def choose_player_callback(self, player: 'Player', other_player_index, chosen_card):
pl_name = player.game.get_other_players(player)[other_player_index]["name"]
other_player = player.game.get_player_named(pl_name)
other_player.hand.append(chosen_card)
G.sio.emit(
"card_drawn",
room=player.game.name,
data={"player": pl_name, "pile": player.name},
)
other_player.notify_self()
player.pending_action = PendingAction.PLAY
def play_card(self, player, against=None, _with=None) -> bool:
drawn_cards = [player.game.deck.draw(player=player) for _ in range(3)]
player.set_choose_action(
"choose_mail_car",
drawn_cards,
self.choose_card_callback,
)
return True
class ObservationCar(TrainCard):
"""Tu vedi gli altri a distanza -1. Gli altri a vedono a distanza +1."""
def __init__(self):
super().__init__("Observation Car")
self.icon = "🚋👀"
self.sight_mod = 1
self.vis_mod = 1
def play_card(self, player, against=None, _with=None) -> bool:
return False
class PassengerCar(TrainCard):
"""Scartalo: pesca una carta (o in mano o in gioco) da un altro giocatore"""
def __init__(self):
super().__init__("Passenger Car")
self.icon = "🚋🚶"
self.range = 99
self.need_target = True
def play_card(self, player, against=None, _with=None) -> bool:
if (
against is not None
and (len(player.equipment) > 0 or len(player.equipment) > 0)
):
player.game.steal_discard(player, against, self)
return True
return False
class PrisonerCar(TrainCard):
"""Le carte Duello e Indiani! giocate dagli altri giocatori non hanno effetto su di te."""
def __init__(self):
super().__init__("Prisoner Car")
self.icon = "🚋👮🏻‍♂️"
def play_card(self, player, against=None, _with=None) -> bool:
return False
class PrivateCar(TrainCard):
"""Se non hai carte in mano, non puoi essere bersaglio di carte BANG"""
def __init__(self):
super().__init__("Private Car")
self.icon = "🚋💁🏻"
def play_card(self, player, against=None, _with=None) -> bool:
return False
class SleeperCar(TrainCard):
"""Una volta per turno, puoi scartare un'altra tua carta a bordo blu incluso."""
def __init__(self):
super().__init__("Sleeper Car")
self.icon = "🚋🛌"
def choose_card_callback(self, player: 'Player', card_index):
player.game.deck.scrap(player.equipment.pop(card_index), player=player)
player.pending_action = PendingAction.PLAY
self.usable_next_turn = True
self.can_be_used_now = False
player.notify_self()
def play_card(self, player, against=None, _with=None) -> bool:
if not self.can_be_used_now:
return False
player.set_choose_action(
"choose_sleeper_car",
player.equipment,
self.choose_card_callback,
)
return False
def get_all_cards(rng=random):
"""Return a list of all train cards in the expansion"""
cars = [
BaggageCar(),
Caboose(),
CattleTruck(),
CircusWagon(),
CoalHopper(),
DiningCar(),
ExpressCar(),
GhostCar(),
LoungeCar(),
LumberFlatcar(),
MailCar(),
ObservationCar(),
PassengerCar(),
PrisonerCar(),
PrivateCar(),
SleeperCar(),
]
cars = [c for c in cars if c.implemented]
rng.shuffle(cars)
return cars
def get_locomotives(rng=random):
"""Return a list of all locomotive cards in the expansion"""
locs = [
Ironhorse(),
Leland(),
]
rng.shuffle(locs)
return locs

View File

@ -7,16 +7,6 @@ from globals import G
from bang.expansions.fistful_of_cards.card_events import CardEvent
class WildWestShowCardEvent(CardEvent):
"""
Base class for all card events in the Wild West Show expansion
"""
def __init__(self, name, icon):
super().__init__(name, icon)
self.expansion = "wild-west-show"
# class Bavaglio(CardEvent):
# def __init__(self):
# super().__init__("Bavaglio", "🤐")
@ -24,7 +14,7 @@ class WildWestShowCardEvent(CardEvent):
# # NOT IMPLEMENTED
class Camposanto(WildWestShowCardEvent):
class Camposanto(CardEvent):
"""
All'inizio del proprio turno, ogni giocatore eliminato torna in gioco con 1 punto vita. Pesca il ruolo a caso fra quelli dei giocatori eliminati.
"""
@ -33,7 +23,7 @@ class Camposanto(WildWestShowCardEvent):
super().__init__("Camposanto", "")
class DarlingValentine(WildWestShowCardEvent):
class DarlingValentine(CardEvent):
"""
All'inizio del proprio turno, ogni giocatore scarta le carte in mano e ne pesca dal mazzo altrettante.
"""
@ -42,7 +32,7 @@ class DarlingValentine(WildWestShowCardEvent):
super().__init__("Darling Valentine", "💋")
class DorothyRage(WildWestShowCardEvent):
class DorothyRage(CardEvent):
"""
Nel proprio turno, ogni giocatore può obbligarne un altro a giocare una carta.
"""
@ -51,7 +41,7 @@ class DorothyRage(WildWestShowCardEvent):
super().__init__("Dorothy Rage", "👩‍⚖️")
class HelenaZontero(WildWestShowCardEvent):
class HelenaZontero(CardEvent):
"""
Quando Helena entra in gioco, "estrai!": se esce Cuori o Quadri, rimescola i ruoli attivi tranne lo Sceriffo, e ridistribuiscili a caso.
"""
@ -74,14 +64,14 @@ class HelenaZontero(WildWestShowCardEvent):
)
pls = [p for p in game.players if not isinstance(p.role, roles.Sheriff)]
newroles = [p.role for p in pls]
game.rng.shuffle(newroles)
random.shuffle(newroles)
for p in pls:
p.set_role(newroles.pop(game.rng.randint(0, len(newroles) - 1)))
p.set_role(newroles.pop(random.randint(0, len(newroles) - 1)))
return super().on_flipped(game)
class LadyRosaDelTexas(WildWestShowCardEvent):
class LadyRosaDelTexas(CardEvent):
"""
Nel proprio turno, ogni giocatore può scambiarsi di posto con quello alla sua destra, il quale salta il prossimo turno.
"""
@ -99,7 +89,7 @@ class LadyRosaDelTexas(WildWestShowCardEvent):
game.notify_all()
class MissSusanna(WildWestShowCardEvent):
class MissSusanna(CardEvent):
"""
Nel proprio turno ogni giocatore deve giocare almeno 3 carte. Se non lo fa, perde 1 punto vita.
"""
@ -108,7 +98,7 @@ class MissSusanna(WildWestShowCardEvent):
super().__init__("Miss Susanna", "👩‍🎤")
class RegolamentoDiConti(WildWestShowCardEvent):
class RegolamentoDiConti(CardEvent):
"""
Tutte le carte possono essere giocate come se fossero BANG!. Le carte BANG! come se fossero Mancato!
"""
@ -118,15 +108,12 @@ class RegolamentoDiConti(WildWestShowCardEvent):
def on_clicked(self, game, player):
super().on_clicked(game, player)
if (
len(player.hand) > 0
and not player.has_played_bang
and any(
if len(player.hand) > 0:
if not player.has_played_bang and any(
(
player.get_sight() >= p["dist"]
for p in game.get_visible_players(player)
)
)
):
player.available_cards = player.hand.copy()
player.pending_action = players.PendingAction.CHOOSE
@ -134,7 +121,7 @@ class RegolamentoDiConti(WildWestShowCardEvent):
player.notify_self()
class Sacagaway(WildWestShowCardEvent):
class Sacagaway(CardEvent):
"""
Tutti i giocatori giocano a carte scoperte (tranne il ruolo!).
"""
@ -143,7 +130,7 @@ class Sacagaway(WildWestShowCardEvent):
super().__init__("Sacagaway", "🌄")
class WildWestShow(WildWestShowCardEvent):
class WildWestShow(CardEvent):
"""
L'obiettivo di ogni giocatore diventa: "Rimani l'ultimo in gioco!"
"""
@ -158,12 +145,12 @@ class WildWestShow(WildWestShowCardEvent):
def get_endgame_card():
"""Return the endgame card for this expansion"""
return WildWestShow()
end_game = WildWestShow()
end_game.expansion = "wild-west-show"
return end_game
def get_all_events(rng=random):
"""Return all the events for this expansion shuffled, excluding the endgame card"""
cards = [
Camposanto(),
DarlingValentine(),
@ -175,4 +162,6 @@ def get_all_events(rng=random):
Sacagaway(),
]
rng.shuffle(cards)
for c in cards:
c.expansion = "wild-west-show"
return cards

View File

@ -123,22 +123,17 @@ class YoulGrinner(Character):
def all_characters() -> List[Character]:
"""
Returns a list of all characters in this expansion.
"""
cards = [
BigSpencer(),
FlintWestwood(),
GaryLooter(),
# GaryLooter(),
# GreygoryDeckard(),
JohnPain(),
LeeVanKliff(),
TerenKill(),
YoulGrinner(),
]
for card in cards:
card.expansion_icon = "🎪" # pylint: disable=attribute-defined-outside-init
card.expansion = ( # pylint: disable=attribute-defined-outside-init
"wild_west_show"
)
for c in cards:
c.expansion_icon = "🎪"
c.expansion = "wild_west_show"
return cards

View File

@ -8,7 +8,6 @@ import bang.players as pl
import bang.cards as cs
import bang.characters as characters
import bang.expansions.dodge_city.characters as chd
import bang.expansions.wild_west_show.characters as chwws
from bang.deck import Deck
import bang.roles as roles
import bang.expansions.fistful_of_cards.card_events as ce
@ -17,9 +16,8 @@ import bang.expansions.wild_west_show.card_events as cew
import bang.expansions.gold_rush.shop_cards as grc
import bang.expansions.gold_rush.characters as grch
import bang.expansions.the_valley_of_shadows.cards as tvosc
import bang.expansions.train_robbery.trains as trt
from metrics import Metrics
from globals import G, PendingAction
from globals import G
debug_commands = [
@ -58,11 +56,8 @@ debug_commands = [
"help": "Remove a card from hand/equip - sample /removecard 0",
},
{"cmd": "/getcard", "help": "Get a brand new card - sample /getcard Birra"},
{"cmd": "/equipcard", "help": "Equip a brand new card - sample /getcard Barile"},
{"cmd": "/meinfo", "help": "Get player data"},
{"cmd": "/gameinfo", "help": "Get game data"},
{"cmd": "/deckinfo", "help": "Get deck data"},
{"cmd": "/trainfw", "help": "move train forward"},
{"cmd": "/playerinfo", "help": "Get player data - sample /playerinfo player"},
{"cmd": "/cardinfo", "help": "Get card data - sample /cardinfo handindex"},
{"cmd": "/mebot", "help": "Toggles bot mode"},
@ -97,7 +92,6 @@ class Game:
"gold_rush",
"the_valley_of_shadows",
"wild_west_show",
"train_robbery",
]
self.shutting_down = False
self.is_competitive = False
@ -116,13 +110,13 @@ class Game:
self.characters_to_distribute = 2 # personaggi da dare a inizio partita
self.debug = self.name == "debug"
self.dead_roles: List[roles.Role] = []
self.mouse_positions = {}
self.is_changing_pwd = False
self.is_hidden = False
self.rng = random.Random()
self.rpc_log = []
self.is_replay = False
self.replay_speed = 1
self.owner: str | None = None
def shuffle_players(self):
if not self.started:
@ -231,7 +225,6 @@ class Game:
self.reset()
def notify_room(self, sid=None):
"""Notify all players in the room of the room's state."""
if any((p.character is None for p in self.players)) or sid:
G.sio.emit(
"room",
@ -254,8 +247,6 @@ class Game:
"expansions": self.expansions,
"available_expansions": self.available_expansions,
"is_replay": self.is_replay,
"characters_to_distribute": self.characters_to_distribute,
"owner": self.owner,
},
)
G.sio.emit("debug", room=self.name, data=self.debug)
@ -274,7 +265,6 @@ class Game:
G.sio.emit("spectators", room=self.name, data=len(self.spectators))
def toggle_expansion(self, expansion_name):
"""Toggles an expansion on or off."""
if not self.started:
print(f"{self.name}: toggling", expansion_name)
if expansion_name in self.expansions:
@ -284,12 +274,10 @@ class Game:
self.notify_room()
def toggle_competitive(self):
"""Toggles the competitive mode on or off."""
self.is_competitive = not self.is_competitive
self.notify_room()
def toggle_disconnect_bot(self):
"""Toggles the disconnect bot on or off."""
self.disconnect_bot = not self.disconnect_bot
self.notify_room()
@ -306,16 +294,14 @@ class Game:
return
if player in self.players or len(self.players) >= 10:
return
if len(self.players) > 7:
if "dodge_city" not in self.expansions:
self.expansions.append("dodge_city")
player.join_game(self)
if player.is_admin():
self.feature_flags()
self.players.append(player)
if len(self.players) == 1:
self.owner = player.name
if len(self.players) > 7:
if "dodge_city" not in self.expansions:
self.expansions.append("dodge_city")
print(f"{self.name}: Added player {player.name} to game; {len(self.players)=}")
print(f"{self.name}: Added player {player.name} to game")
self.notify_room()
G.sio.emit("chat_message", room=self.name, data=f"_joined|{player.name}")
@ -324,7 +310,7 @@ class Game:
self.is_changing_pwd = True
if self.password == "":
self.password = "".join(
random.choice("AEOUJKZT123456789") for x in range(6)
random.choice("AEIOUJKZT123456789") for x in range(6)
)
print(self.name, "is now private pwd", self.password)
else:
@ -346,12 +332,7 @@ class Game:
data=f"_choose_character|{player.name}|{player.character.name}",
)
player.prepare()
cards_to_draw = (
player.max_lives
if not player.character.check(self, chwws.BigSpencer)
else 5
)
for _ in range(cards_to_draw):
for _ in range(player.max_lives):
self.deck.draw(player=player)
player.notify_self()
self.players_map = {c.name: i for i, c in enumerate(self.players)}
@ -363,14 +344,12 @@ class Game:
roles_str += f"|{role}|{str(current_roles.count(role))}"
G.sio.emit("chat_message", room=self.name, data=f"_allroles{roles_str}")
self.play_turn()
self.notify_stations()
def choose_characters(self):
n = self.characters_to_distribute
all_chars = characters.all_characters(self.expansions)
if len(all_chars) // len(self.players) < n:
n = len(all_chars) // len(self.players)
char_cards = self.rng.sample(all_chars, len(self.players) * n)
char_cards = self.rng.sample(
characters.all_characters(self.expansions), len(self.players) * n
)
for i, player in enumerate(self.players):
player.set_available_character(char_cards[i * n : i * n + n])
@ -469,7 +448,7 @@ class Game:
def discard_others(self, attacker: pl.Player, card_name: str = None):
self.attack_in_progress = True
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.waiting_for = 0
self.ready_count = 0
@ -478,12 +457,8 @@ class Game:
if p.get_discarded(attacker=attacker, card_name=card_name):
self.waiting_for += 1
p.notify_self()
elif card_name == "Tornado" and len(p.hand) == 0:
self.deck.draw(player=p)
self.deck.draw(player=p)
p.notify_self()
if self.waiting_for == 0:
attacker.pending_action = PendingAction.PLAY
attacker.pending_action = pl.PendingAction.PLAY
attacker.notify_self()
self.attack_in_progress = False
elif card_name == "Poker":
@ -491,7 +466,7 @@ class Game:
def attack_others(self, attacker: pl.Player, card_name: str = None):
self.attack_in_progress = True
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.waiting_for = 0
self.ready_count = 0
@ -501,7 +476,7 @@ class Game:
self.waiting_for += 1
p.notify_self()
if self.waiting_for == 0:
attacker.pending_action = PendingAction.PLAY
attacker.pending_action = pl.PendingAction.PLAY
attacker.notify_self()
self.attack_in_progress = False
if self.pending_winners and not self.someone_won:
@ -509,7 +484,7 @@ class Game:
def indian_others(self, attacker: pl.Player):
self.attack_in_progress = True
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.waiting_for = 0
self.ready_count = 0
@ -519,7 +494,7 @@ class Game:
self.waiting_for += 1
p.notify_self()
if self.waiting_for == 0:
attacker.pending_action = PendingAction.PLAY
attacker.pending_action = pl.PendingAction.PLAY
attacker.notify_self()
self.attack_in_progress = False
if self.pending_winners and not self.someone_won:
@ -556,26 +531,26 @@ class Game:
self.attack_in_progress = True
self.ready_count = 0
self.waiting_for = 1
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.get_player_named(target_username).notify_self()
elif not attacker.is_my_turn or len(self.attack_queue) == 0:
self.players[self.turn].pending_action = PendingAction.PLAY
self.players[self.turn].pending_action = pl.PendingAction.PLAY
def steal_discard(self, attacker: pl.Player, target_username: str, card: cs.Card):
p = self.get_player_named(target_username)
if p != attacker and p.get_discarded(
attacker,
card_name=card.name,
action="steal" if (isinstance(card, cs.Panico) or isinstance(card, trt.PassengerCar)) else "discard",
action="steal" if isinstance(card, cs.Panico) else "discard",
):
self.ready_count = 0
self.waiting_for = 1
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.get_player_named(target_username).notify_self()
else:
attacker.pending_action = PendingAction.CHOOSE
attacker.pending_action = pl.PendingAction.CHOOSE
attacker.target_p = target_username
if isinstance(card, cs.CatBalou):
attacker.choose_action = "discard"
@ -589,7 +564,7 @@ class Game:
):
self.ready_count = 0
self.waiting_for = 1
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.get_player_named(target_username).notify_self()
@ -597,14 +572,14 @@ class Game:
if self.get_player_named(target_username).get_dueled(attacker=attacker):
self.ready_count = 0
self.waiting_for = 1
attacker.pending_action = PendingAction.WAIT
attacker.pending_action = pl.PendingAction.WAIT
attacker.notify_self()
self.get_player_named(target_username).notify_self()
def emporio(self):
pls = self.get_alive_players()
self.available_cards = [self.deck.draw(True) for i in range(len(pls))]
self.players[self.turn].pending_action = PendingAction.CHOOSE
self.players[self.turn].pending_action = pl.PendingAction.CHOOSE
self.players[self.turn].choose_text = "choose_card_to_get"
self.players[self.turn].available_cards = self.available_cards
G.sio.emit(
@ -626,10 +601,10 @@ class Game:
)
player.hand.append(card)
player.available_cards = []
player.pending_action = PendingAction.WAIT
player.pending_action = pl.PendingAction.WAIT
player.notify_self()
pls = self.get_alive_players()
next_player = pls[
nextPlayer = pls[
(
pls.index(self.players[self.turn])
+ (len(pls) - len(self.available_cards))
@ -637,33 +612,28 @@ class Game:
% len(pls)
]
if len(self.available_cards) == 1:
G.sio.emit(
"chat_message",
room=self.name,
data=f"_choose_emporio|{next_player.name}|{self.available_cards[0].name}",
)
next_player.hand.append(self.available_cards.pop())
next_player.notify_self()
nextPlayer.hand.append(self.available_cards.pop())
nextPlayer.notify_self()
G.sio.emit("emporio", room=self.name, data='{"name":"","cards":[]}')
self.players[self.turn].pending_action = PendingAction.PLAY
self.players[self.turn].pending_action = pl.PendingAction.PLAY
self.players[self.turn].notify_self()
elif next_player == self.players[self.turn]:
elif nextPlayer == self.players[self.turn]:
G.sio.emit("emporio", room=self.name, data='{"name":"","cards":[]}')
self.players[self.turn].pending_action = PendingAction.PLAY
self.players[self.turn].pending_action = pl.PendingAction.PLAY
self.players[self.turn].notify_self()
else:
next_player.pending_action = PendingAction.CHOOSE
next_player.choose_text = "choose_card_to_get"
next_player.available_cards = self.available_cards
nextPlayer.pending_action = pl.PendingAction.CHOOSE
nextPlayer.choose_text = "choose_card_to_get"
nextPlayer.available_cards = self.available_cards
G.sio.emit(
"emporio",
room=self.name,
data=json.dumps(
{"name": next_player.name, "cards": self.available_cards},
{"name": nextPlayer.name, "cards": self.available_cards},
default=lambda o: o.__dict__,
),
)
next_player.notify_self()
nextPlayer.notify_self()
def get_player_named(self, name: str, next=False) -> pl.Player:
if next:
@ -671,7 +641,6 @@ class Game:
return self.players[self.players_map[name]]
def responders_did_respond_resume_turn(self, did_lose=False):
"""Called when all Players have responded to an event/attack."""
print(f"{self.name}: did_lose", did_lose)
if self.player_bangs > 0 and self.check_event(ce.PerUnPugnoDiCarte):
self.player_bangs -= 1
@ -738,7 +707,7 @@ class Game:
elif self.poker_on and not any(
c.number == 1 for c in self.deck.scrap_pile[-tmp:]
):
self.players[self.turn].pending_action = PendingAction.CHOOSE
self.players[self.turn].pending_action = pl.PendingAction.CHOOSE
self.players[
self.turn
].choose_text = f"choose_from_poker;{min(2, tmp)}"
@ -749,15 +718,14 @@ class Game:
print("attack completed, next attack")
atk = self.attack_queue.pop(0)
self.attack(atk[0], atk[1], atk[2], atk[3], skip_queue=True)
elif self.players[self.turn].pending_action == PendingAction.CHOOSE:
elif self.players[self.turn].pending_action == pl.PendingAction.CHOOSE:
self.players[self.turn].notify_self()
else:
self.players[self.turn].pending_action = PendingAction.PLAY
self.players[self.turn].pending_action = pl.PendingAction.PLAY
self.poker_on = False
self.players[self.turn].notify_self()
def announces_winners(self, winners=None):
"""Announces the winners of the game in the chat"""
if winners is None:
print(f"{self.name}: WE HAVE A WINNER - pending winners")
else:
@ -791,12 +759,10 @@ class Game:
return self.reset()
def next_player(self):
"""Returns the next player in turn order"""
pls = self.get_alive_players()
return pls[(pls.index(self.players[self.turn]) + 1) % len(pls)]
def play_turn(self):
"""Starts the turn of the current player"""
self.incremental_turn += 1
if not self.is_replay:
Metrics.send_metric(
@ -818,7 +784,7 @@ class Game:
elif self.check_event(cew.Camposanto):
pl.lives = 1
pl.set_role = self.dead_roles.pop(
self.rng.randint(0, len(self.dead_roles) - 1)
random.randint(0, len(self.dead_roles) - 1)
)
pl.is_dead = False
pl.is_ghost = False
@ -856,7 +822,6 @@ class Game:
)
):
self.deck.flip_event()
self.deck.move_train_forward()
if self.check_event(ce.RouletteRussa):
self.is_russian_roulette_on = True
if self.players[self.turn].get_banged(self.deck.event_cards[0]):
@ -932,31 +897,17 @@ class Game:
data=json.dumps(self.deck.shop_cards, default=lambda o: o.__dict__),
)
def notify_stations(self, sid=None):
if "train_robbery" in self.expansions:
room = self.name if sid is None else sid
G.sio.emit(
"stations",
room=room,
data=json.dumps(
{
"stations": self.deck.stations,
"current_train": self.deck.current_train,
},
default=lambda o: o.__dict__,
),
)
def notify_scrap_pile(self, sid=None):
print(f"{self.name}: scrap")
room = self.name if sid is None else sid
if self.deck.peek_scrap_pile():
G.sio.emit("scrap", room=room, data=self.deck.peek_scrap_pile()[0].__dict__)
G.sio.emit("scrap", room=room, data=self.deck.peek_scrap_pile().__dict__)
else:
G.sio.emit("scrap", room=room, data=None)
def handle_disconnect(self, player: pl.Player):
print(f"{self.name}: player {player.name} left the game")
self.mouse_positions.pop(player.name, None)
if player in self.spectators:
self.spectators.remove(player)
G.sio.emit("spectators", room=self.name, data=len(self.spectators))
@ -993,8 +944,6 @@ class Game:
self.deck = None
return True
else:
self.owner = next((p.name for p in self.players if not p.is_bot), None)
self.notify_room()
return False
def player_death(self, player: pl.Player, disconnected=False):
@ -1047,9 +996,9 @@ class Game:
self.deck.draw(True, player=player.attacker)
player.attacker.notify_self()
print(f"{self.name}: player {player.name} died")
if self.waiting_for > 0 and player.pending_action == PendingAction.RESPOND:
if self.waiting_for > 0 and player.pending_action == pl.PendingAction.RESPOND:
self.responders_did_respond_resume_turn()
player.pending_action = PendingAction.WAIT
player.pending_action = pl.PendingAction.WAIT
if player.is_dead:
return
@ -1252,29 +1201,6 @@ class Game:
def get_alive_players(self):
return [p for p in self.players if not p.is_dead or p.is_ghost]
def get_other_players(self, player:pl.Player):
return [{
"name": p.name,
"dist": 0,
"lives": p.lives,
"max_lives": p.max_lives,
"is_sheriff": isinstance(p.role, roles.Sheriff),
"cards": len(p.hand) + len(p.equipment),
"is_ghost": p.is_ghost,
"is_bot": p.is_bot,
"icon": p.role.icon
if (
p.role is not None
and (
self.initial_players == 3
or isinstance(p.role, roles.Sheriff)
)
)
else "🤠",
"avatar": p.avatar,
"role": p.role,
} for p in self.get_alive_players() if p != player]
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)

View File

@ -1,6 +1,9 @@
from __future__ import annotations
from enum import IntEnum
import json
from random import randrange, sample, uniform, randint
from random import random, randrange, sample, uniform, randint
import socketio
import bang.deck as deck
import bang.roles as r
import bang.cards as cs
import bang.expansions.dodge_city.cards as csd
@ -14,11 +17,9 @@ import bang.expansions.gold_rush.shop_cards as grc
import bang.expansions.gold_rush.characters as grch
import bang.expansions.the_valley_of_shadows.cards as tvosc
import bang.expansions.the_valley_of_shadows.characters as tvosch
import bang.expansions.train_robbery.stations as trs
import bang.expansions.train_robbery.trains as trt
from typing import List, TYPE_CHECKING, Callable
from typing import List, TYPE_CHECKING
from metrics import Metrics
from globals import G, PendingAction
from globals import G
import sys
if TYPE_CHECKING:
@ -44,6 +45,15 @@ robot_pictures = [
]
class PendingAction(IntEnum):
PICK = 0
DRAW = 1
PLAY = 2
RESPOND = 3
WAIT = 4
CHOOSE = 5
class Player:
def is_admin(self):
return self.discord_id in {"244893980960096266", "539795574019457034"}
@ -92,7 +102,6 @@ class Player:
self.is_bot = bot
self.discord_token = discord_token
self.discord_id = None
self.did_choose_callback = None
self.played_cards = 0
self.avatar = ""
self.last_played_card: cs.Card = None
@ -271,25 +280,25 @@ class Player:
if self.pending_action == PendingAction.DRAW and self.game.check_event(
ce.Peyote
):
self.is_drawing = True
self.set_choose_action(
"choose_guess",
[
self.available_cards = [
{"icon": "🔴", "noDesc": True},
{"icon": "", "noDesc": True},
],
)
]
self.is_drawing = True
self.choose_text = "choose_guess"
self.pending_action = PendingAction.CHOOSE
elif (
self.can_play_ranch
and self.pending_action == PendingAction.PLAY
and self.game.check_event(ce.Ranch)
):
self.can_play_ranch = False
self.available_cards = [c for c in self.hand]
self.discarded_cards = []
self.available_cards.append({"icon": "", "noDesc": True})
self.is_playing_ranch = True
self.set_choose_action(
"choose_ranch", [c for c in self.hand] + [{"icon": "", "noDesc": True}]
)
self.choose_text = "choose_ranch"
self.pending_action = PendingAction.CHOOSE
elif (
self.character
and self.character.check(self.game, chars.SuzyLafayette)
@ -303,9 +312,9 @@ class Player:
if self.gold_nuggets >= 2 and any(
(isinstance(c, grc.Zaino) for c in self.gold_rush_equipment)
):
for card in self.gold_rush_equipment:
if isinstance(card, grc.Zaino):
card.play_card(self, None)
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)
return # play card will notify the player
if self.character.check(self.game, chw.TerenKill):
picked: cs.Card = self.game.deck.pick_and_scrap()
@ -329,7 +338,9 @@ class Player:
self.game.players[self.game.turn].notify_self()
self.scrapped_cards = 0
self.previous_pending_action = self.pending_action
self.set_choose_action("choose_sid_scrap", self.hand)
self.pending_action = PendingAction.CHOOSE
self.choose_text = "choose_sid_scrap"
self.available_cards = self.hand
self.lives += 1
ser = self.__dict__.copy()
@ -413,12 +424,6 @@ class Player:
self.game.rpc_log.append(f"{self.name};draw;")
self.draw("")
elif self.pending_action == PendingAction.PLAY:
must_play_cards = [
c
for c in self.hand
if c.must_be_used and c.can_be_used_now
and self.game.check_event(ce.LeggeDelWest)
]
non_blocked_cards = [
card
for card in self.hand
@ -518,8 +523,8 @@ class Player:
if self.gold_nuggets > 0 and any(
(c.number <= self.gold_nuggets for c in self.game.deck.shop_cards)
):
for i, card in enumerate(self.game.deck.shop_cards):
if card.number <= self.gold_nuggets:
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}")
self.buy_gold_rush_card(i)
return
@ -671,19 +676,19 @@ class Player:
self.end_turn()
elif self.pending_action == PendingAction.RESPOND:
did_respond = False
for i, card in enumerate(self.hand):
if card.can_be_used_now and (
card.name in self.expected_response
for i in range(len(self.hand)):
if self.hand[i].can_be_used_now and (
self.hand[i].name in self.expected_response
or self.character.check(self.game, chd.ElenaFuente)
):
self.game.rpc_log.append(f"{self.name};respond;{i}")
self.respond(i)
did_respond = True
break
for i, card in enumerate(self.equipment):
for i in range(len(self.equipment)):
if (
not self.game.check_event(ce.Lazo)
and card.name in self.expected_response
and self.equipment[i].name in self.expected_response
):
self.game.rpc_log.append(f"{self.name};respond;{len(self.hand)+i}")
self.respond(len(self.hand) + i)
@ -737,9 +742,6 @@ class Player:
self.has_played_bang = False
self.special_use_count = 0
self.bang_used = 0
if any((isinstance(c, trt.DiningCar) for c in self.equipment)):
if self.game.deck.pick_and_scrap().suit == cs.Suit.HEARTS:
self.lives = min(self.lives + 1, self.max_lives)
if self.game.check_event(cew.DarlingValentine):
hand = len(self.hand)
for _ in range(hand):
@ -774,9 +776,7 @@ class Player:
for p in self.game.get_alive_players()
)
):
self.set_choose_action(
"choose_fratelli_di_sangue",
[
self.available_cards = [
{
"name": p.name,
"icon": p.role.icon
@ -793,17 +793,18 @@ class Player:
for p in self.game.get_alive_players()
if p != self and p.lives < p.max_lives
]
+ [{"icon": "", "noDesc": True}],
)
self.available_cards.append({"icon": "", "noDesc": True})
self.choose_text = "choose_fratelli_di_sangue"
self.pending_action = PendingAction.CHOOSE
self.is_giving_life = True
elif (
self.game.check_event(ceh.NuovaIdentita)
and self.not_chosen_character is not None
and not again
):
self.set_choose_action(
"choose_nuova_identita", [self.character, self.not_chosen_character]
)
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)
@ -833,9 +834,7 @@ class Player:
and sum((c.name == cs.Bang(0, 0).name for c in self.hand)) >= 2
):
self.is_using_checchino = True
self.set_choose_action(
"choose_cecchino",
[
self.available_cards = [
{
"name": p["name"],
"icon": p["role"].icon
@ -851,8 +850,9 @@ class Player:
for p in self.game.get_visible_players(self)
if p["dist"] <= self.get_sight()
]
+ [{"icon": "", "noDesc": True}],
)
self.available_cards.append({"icon": "", "noDesc": True})
self.choose_text = "choose_cecchino"
self.pending_action = PendingAction.CHOOSE
self.notify_self()
if (
self.is_my_turn
@ -878,15 +878,15 @@ class Player:
self.notify_self()
elif self.character.check(self.game, chars.KitCarlson) and not self.is_ghost:
self.is_drawing = True
self.set_choose_action(
"choose_card_to_get", [self.game.deck.draw() for i in range(3)]
)
self.available_cards = [self.game.deck.draw() for i in range(3)]
self.choose_text = "choose_card_to_get"
self.pending_action = PendingAction.CHOOSE
self.notify_self()
elif self.character.check(self.game, grch.DutchWill) and not self.is_ghost:
self.is_drawing = True
self.set_choose_action(
"choose_card_to_get", [self.game.deck.draw() for i in range(2)]
)
self.available_cards = [self.game.deck.draw() for i in range(2)]
self.choose_text = "choose_card_to_get"
self.pending_action = PendingAction.CHOOSE
self.notify_self()
elif (
self.character.check(self.game, chd.PatBrennan)
@ -896,10 +896,10 @@ class Player:
and len(self.game.get_player_named(pile).equipment) > 0
):
self.is_drawing = True
self.available_cards = self.game.get_player_named(pile).equipment
self.pat_target = pile
self.set_choose_action(
"choose_card_to_get", self.game.get_player_named(pile).equipment
)
self.choose_text = "choose_card_to_get"
self.pending_action = PendingAction.CHOOSE
self.notify_self()
else:
self.pending_action = PendingAction.PLAY
@ -998,13 +998,12 @@ class Player:
def manette(self):
if self.game.check_event(ceh.Manette):
self.set_choose_action(
"choose_manette",
[
self.choose_text = "choose_manette"
self.available_cards = [
{"name": "", "icon": "♦♣♥♠"[s], "alt_text": "", "noDesc": True}
for s in [0, 1, 2, 3]
],
)
]
self.pending_action = PendingAction.CHOOSE
def pick(self):
if self.pending_action != PendingAction.PICK:
@ -1013,8 +1012,10 @@ class Player:
if any((isinstance(c, grc.FerroDiCavallo) for c in self.gold_rush_equipment)):
pickable_cards += 1
if self.is_my_turn and self.attacker is None:
for i, card in enumerate(self.equipment):
if i < len(self.equipment) and isinstance(card, cs.Dinamite):
for i in range(len(self.equipment)):
if i < len(self.equipment) and isinstance(
self.equipment[i], cs.Dinamite
):
while pickable_cards > 0:
pickable_cards -= 1
picked: cs.Card = self.game.deck.pick_and_scrap()
@ -1087,8 +1088,8 @@ class Player:
):
self.notify_self()
return
for i, card in enumerate(self.equipment):
if isinstance(card, cs.Prigione):
for i in range(len(self.equipment)):
if isinstance(self.equipment[i], cs.Prigione):
while pickable_cards > 0:
pickable_cards -= 1
picked: cs.Card = self.game.deck.pick_and_scrap()
@ -1123,8 +1124,8 @@ class Player:
)
break
break
for i, card in enumerate(self.equipment):
if isinstance(card, tvosc.SerpenteASonagli):
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()
@ -1183,7 +1184,8 @@ class Player:
def get_playable_cards(self):
playable_cards = []
for i, card in enumerate(self.hand):
for i in range(len(self.hand)):
card = self.hand[i]
if (
isinstance(card, cs.Bang)
and self.has_played_bang
@ -1196,7 +1198,7 @@ class Player:
playable_cards.append(i)
return playable_cards
def play_card(self, hand_index: int, against:str=None, _with:int=None):
def play_card(self, hand_index: int, against=None, _with=None):
if self.is_bot:
data = {"index": hand_index, "against": against, "with": _with}
self.game.rpc_log.append(f"{self.name};play_card;{json.dumps(data)}")
@ -1261,7 +1263,6 @@ class Player:
(
self.game.check_event(ceh.Manette)
and card.suit != self.committed_suit_manette
and card.number != 42
)
)
)
@ -1394,9 +1395,6 @@ class Player:
self.target_p = self.rissa_targets.pop(0).name
print(f"rissa targets: {self.rissa_targets}")
self.notify_self()
elif self.did_choose_callback is not None:
self.did_choose_callback(self, card_index)
self.notify_self()
elif self.choose_text == "choose_ricercato":
player = self.game.get_player_named(
self.available_cards[card_index]["name"]
@ -1532,9 +1530,6 @@ class Player:
room=player.game.name,
data=f"_play_card_against|{self.name}|Sventagliata|{player.name}",
)
self.bang_used += 1
self.has_played_bang = True
if self.pending_action == PendingAction.CHOOSE:
self.pending_action = PendingAction.PLAY
self.notify_self()
elif "choose_play_as_bang" in self.choose_text:
@ -1574,10 +1569,6 @@ class Player:
self.game.deck.scrap(self.hand.pop(card_index), player=self)
self.mancato_needed -= 1
else:
if self.attacker and "gold_rush" in self.game.expansions and not self.is_ghost:
if isinstance(self.attacker, Player):
self.attacker.gold_nuggets += 1
self.attacker.notify_self()
self.lives -= 1
self.mancato_needed = 0
if self.mancato_needed <= 0:
@ -1592,7 +1583,7 @@ class Player:
"icon": p.role.icon
if (self.game.initial_players == 3)
else "⭐️"
if isinstance(p.role, r.Sheriff)
if p["is_sheriff"]
else "🤠",
"avatar": p.avatar,
"is_character": True,
@ -1872,10 +1863,7 @@ class Player:
):
self.expected_response.append(cs.Bang(0, 0).name)
if self.character.check(self.game, chw.BigSpencer):
self.expected_response = self.game.deck.mancato_cards.copy()
self.expected_response.remove(cs.Mancato(0, 0).name)
if any((isinstance(c, trt.Caboose) for c in self.equipment)):
self.expected_response.append([c.name for c in self.equipment if not c.usable_next_turn])
self.expected_response = []
self.on_failed_response_cb = self.take_damage_response
self.notify_self()
@ -1938,7 +1926,7 @@ class Player:
self.notify_self()
def get_discarded(self, attacker=None, card_name=None, action=None):
if card_name in {"Tornado", "Poker", "Bandidos", "Circus Wagon"}:
if card_name in {"Tornado", "Poker", "Bandidos"}:
self.pending_action = PendingAction.CHOOSE
self.available_cards = self.hand.copy()
if card_name == "Tornado":
@ -1947,18 +1935,10 @@ class Player:
self.choose_text = "choose_poker"
if card_name == "Bandidos":
self.choose_text = "choose_bandidos"
self.attacker = attacker
self.mancato_needed = min(2, len(self.hand))
self.available_cards.append(
{"name": "-1hp", "icon": "💔", "noDesc": True}
)
if card_name == "Circus Wagon":
from bang.expansions.train_robbery.trains import CircusWagon
self.set_choose_action(
"choose_circus_wagon",
self.hand.copy(),
CircusWagon.choose_circus_wagon,
)
return True
else:
if self.can_escape(card_name) or self.character.check(
@ -2008,17 +1988,6 @@ class Player:
self.attacker = attacker
self.attacking_card = card_name
print(f"attacker -> {attacker}")
# check for trt.PrivateCar
if (card_name == "Bang!" and any(
(isinstance(c, trt.PrivateCar) for c in self.equipment)
) and len(self.hand) == 0):
self.take_no_damage_response()
G.sio.emit(
"chat_message",
room=self.game.name,
data=f"_in_private_car|{self.name}|{attacker.name}",
)
return False
if (
isinstance(attacker, Player)
and attacker.character.check(self.game, tvosch.ColoradoBill)
@ -2063,7 +2032,7 @@ class Player:
)
)
and not self.can_escape(card_name)
):
) or card_name == "Mira":
print("Cant defend")
if not no_dmg:
self.take_damage_response()
@ -2106,8 +2075,8 @@ class Player:
)
if (
self.character.check(self.game, chars.CalamityJanet)
or self.game.check_event(cew.RegolamentoDiConti)
) and cs.Bang(0, 0).name not in self.expected_response:
and cs.Bang(0, 0).name not in self.expected_response
):
self.expected_response.append(cs.Bang(0, 0).name)
if self.character.check(self.game, chw.BigSpencer):
self.expected_response = []
@ -2124,7 +2093,9 @@ class Player:
if len(equipments) == 0:
return False
else:
self.set_choose_action("choose_dalton", equipments)
self.choose_text = "choose_dalton"
self.pending_action = PendingAction.CHOOSE
self.available_cards = equipments
return True
def get_indians(self, attacker):
@ -2134,11 +2105,6 @@ class Player:
(isinstance(c, grc.Calumet) for c in self.gold_rush_equipment)
):
return False
# check for trt.PrisonerCar
if any(
(isinstance(c, trt.PrisonerCar) for c in self.equipment)
):
return False
if (
not self.game.is_competitive
and not any(
@ -2172,11 +2138,6 @@ class Player:
def get_dueled(self, attacker):
self.attacker = attacker
self.attacking_card = "Duello"
if not self.is_my_turn and any(
(isinstance(c, trt.PrisonerCar) for c in self.equipment)
):
self.take_no_damage_response()
return False
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (
not self.game.is_competitive
and not any(
@ -2234,9 +2195,6 @@ class Player:
break
def take_damage_response(self):
if self.attacking_card == "Mira":
self.lives -= 2
else:
self.lives -= 1
G.sio.emit("hurt", room=self.sid, data=f"")
if self.lives > 0:
@ -2372,7 +2330,7 @@ class Player:
self.game.deck.scrap(card, True, player=self)
self.notify_self()
self.mancato_needed -= 1
if isinstance(card, tvosc.RitornoDiFiamma) and self.attacker != "Roulette Russa":
if isinstance(card, tvosc.RitornoDiFiamma):
self.game.attack(self, self.attacker.name, card_name=card.name)
if self.mancato_needed <= 0:
if self.event_type == "duel":
@ -2507,35 +2465,14 @@ class Player:
self.game.deck.draw(True, player=self)
self.game.deck.draw(True, player=self)
self.special_use_count += 1
gary_looter = None
for p in self.game.players:
if p != self and p.character.check(self.game, chw.GaryLooter):
gary_looter = p
break
if gary_looter is not None:
G.sio.emit(
"card_drawn",
room=self.game.name,
data={"player": gary_looter.name, "pile": self.name},
)
gary_looter.hand.append(card)
gary_looter.notify_self()
else:
self.game.deck.scrap(card, player=self)
G.sio.emit(
"chat_message",
room=self.game.name,
data=f"_scrapped|{self.name}|{card.name}|{card.num_suit()}",
)
self.notify_self()
def special(self, data):
self.character.special(self, data)
def gold_rush_discard(self):
self.set_choose_action(
"gold_rush_discard",
[
self.available_cards = [
{
"name": p.name,
"icon": p.role.icon
@ -2550,12 +2487,11 @@ class Player:
}
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)
)
and any((e.number + 1 <= self.gold_nuggets for e in p.gold_rush_equipment))
]
+ [{"icon": "", "noDesc": True}],
)
self.available_cards.append({"icon": "", "noDesc": True})
self.choose_text = "gold_rush_discard"
self.pending_action = PendingAction.CHOOSE
self.notify_self()
def buy_gold_rush_card(self, index):
@ -2583,59 +2519,8 @@ class Player:
self.game.deck.shop_deck.append(card)
self.game.deck.shop_cards[index] = None
self.game.deck.fill_gold_rush_shop()
G.sio.emit(
"card_scrapped",
room=self.game.name,
data={"player": self.name, "pile": "gold_rush", "card": card.__dict__},
)
self.notify_self()
def buy_train(self, index):
if self.pending_action != PendingAction.PLAY:
return
print(
f"{self.name} wants to buy train card on station index {index} in room {self.game.name}"
)
station: trs.StationCard = self.game.deck.stations[index]
train_index = len(self.game.deck.current_train) - 5 - index
if train_index < 0 or train_index >= len(self.game.deck.current_train):
return
train: trt.TrainCard = self.game.deck.current_train[train_index]
if train is not None and not train.is_locomotive:
if station.check_price(self):
print(f"{station=} {train=}")
station.attached_train = train
G.sio.emit(
"chat_message",
room=self.game.name,
data=f"_bought_train|{self.name}|{station.name}|{train.name}",
)
G.sio.emit(
"card_scrapped",
room=self.game.name,
data={"player": self.name, "pile": "train_robbery", "card": train.__dict__},
)
# shift train forward
for i in range(train_index, len(self.game.deck.current_train) - 1):
self.game.deck.current_train[i] = self.game.deck.current_train[
i + 1
]
self.game.notify_stations()
# self.game.deck.current_train[train_index] = None
self.notify_self()
def set_choose_action(
self,
choose_text: str,
available_cards: List,
did_choose_callback: Callable[['Player', int], None] = None,
):
self.pending_action = PendingAction.CHOOSE
self.choose_text = choose_text
self.available_cards = available_cards
self.did_choose_callback = did_choose_callback
def check_can_end_turn(self):
must_be_used_cards = [c for c in self.hand if c.must_be_used]
if self.game.check_event(ce.LeggeDelWest) and len(must_be_used_cards) > 0:
@ -2745,8 +2630,6 @@ class Player:
and not self.equipment[i].can_be_used_now
):
self.equipment[i].can_be_used_now = True
if isinstance(self.equipment[i], trt.TrainCard):
self.equipment[i].usable_next_turn = False
for i in range(len(self.hand)):
if self.hand[i].must_be_used:
self.hand[i].must_be_used = False

View File

@ -1,17 +1,6 @@
from enum import IntEnum
class G:
sio = None
def __init__(self):
pass
class PendingAction(IntEnum):
PICK = 0
DRAW = 1
PLAY = 2
RESPOND = 3
WAIT = 4
CHOOSE = 5

View File

@ -1,10 +1,10 @@
certifi==2022.12.7
dnspython==2.3.0
eventlet==0.35.2
eventlet==0.33.3
python-engineio==4.3.4
python-socketio==5.8.0
six==1.16.0
pytest==7.2.2
requests==2.32.0
requests==2.28.2
discord-webhook==1.1.0
datadog==0.45.0

View File

@ -16,8 +16,8 @@ import socketio
from discord_webhook import DiscordWebhook
from bang.game import Game
from bang.players import Player
from globals import G, PendingAction
from bang.players import PendingAction, Player
from globals import G
from metrics import Metrics
sys.setrecursionlimit(10**6) # this should prevents bots from stopping
@ -58,7 +58,7 @@ for file in [f for f in os.listdir(".") if ".js" in f or ".map" in f or ".html"
static_files[f"/{file}"] = f"./{file}"
HASTEBIN_HEADERS = {
"Authorization": "Bearer " + os.getenv("HASTEBIN_TOKEN", ""),
"Authorization": "Bearer 2cf615e88992970f3396663c5bfb2f599151192bcef5fa99f5569c2e29617ba7cdcfa5df14f90afc61bcf0fe6e475ae49cba98ebded3e0b7b3fdf0c648c76496",
"content-type": "text/plain",
}
@ -179,6 +179,32 @@ def get_online_players(sid):
sio.emit("players", room="lobby", data=online_players)
@sio.event
def mouse_position(sid, data):
ses: Player = sio.get_session(sid)
if ses.game is not None:
if not data or (data["x"] == 0 and data["y"] == 0):
print("Mouse position is 0,0, ignoring")
else:
data["color"] = sid.encode("utf-8").hex()[0:6]
ses.game.mouse_positions[ses.name] = data
sio.emit(
"mouse_positions_update",
room=ses.game.name,
data=[
{
"name": x,
"data": {
"x": data["x"],
"y": data["y"],
"color": data["color"],
},
}
for x, data in ses.game.mouse_positions.items()
],
)
@sio.event
@bang_handler
def report(sid, text):
@ -262,21 +288,15 @@ def get_me(sid, data):
if sio.get_session(sid).game:
sio.get_session(sid).game.notify_room()
else:
dt = data.get("discord_token", None)
username = data.get("username", None)
if username is None:
username = f"player_{random.randint(0,100)}"
sio.save_session(sid, Player(username, sid, discord_token=dt))
p = sio.get_session(sid)
print(f"{sid} is now {username}")
dt = data["discord_token"] if "discord_token" in data else None
sio.save_session(sid, Player("player", sid, discord_token=dt))
if "replay" in data and data["replay"] is not None:
create_room(sid, data["replay"])
p.game.is_hidden = True
sid = sio.get_session(sid)
sid.game.is_hidden = True
eventlet.sleep(0.5)
response = requests.get(
f"https://hastebin.com/raw/{data['replay']}",
headers=HASTEBIN_HEADERS,
timeout=3,
f"https://hastebin.com/raw/{data['replay']}", headers=HASTEBIN_HEADERS
)
if response.status_code != 200:
sio.emit(
@ -286,20 +306,20 @@ def get_me(sid, data):
)
return
log = response.text.splitlines()
p.game.spectators.append(p)
sid.game.spectators.append(sid)
if "ffw" not in data:
p.game.replay(log)
sid.game.replay(log)
else:
p.game.replay(log, speed=0, fast_forward=int(data["ffw"]))
sid.game.replay(log, speed=0, fast_forward=int(data["ffw"]))
return
if data["name"] in games and (room := games[data["name"]]) is not None:
if not room.started:
join_room(sid, data)
elif room.started:
print("room exists")
if username != "player" and any(
if data["username"] is not None and any(
(
p.name == username
p.name == data["username"]
for p in room.players
if (
p.is_bot
@ -317,7 +337,7 @@ def get_me(sid, data):
or (dt is not None and p.discord_token == dt)
or p.sid is None
)
and p.name == username
and p.name == data["username"]
][0]
bot.sid = sid
bot.is_bot = False
@ -346,33 +366,29 @@ def get_me(sid, data):
room.notify_scrap_pile(sid)
room.notify_all()
room.notify_gold_rush_shop()
room.notify_stations()
room.notify_event_card()
room.notify_event_card_wildwestshow(sid)
else:
create_room(sid, data["name"])
p: Player = sio.get_session(sid)
if p.game is None:
if (p := sio.get_session(sid)).game is None:
sio.emit("me", data={"error": "Wrong password/Cannot connect"}, room=sid)
else:
sio.emit("me", data=p.name, room=sid)
if username == "player" or any(
if data["username"] is None or any(
(
pl.name == username
pl.name == data["username"]
for pl in p.game.players
if not (
(dt is not None and pl.discord_token == dt)
or pl.sid is None
or pl == p
(dt is not None and pl.discord_token == dt) or pl.sid is None
)
)
):
sio.emit("change_username", room=sid)
elif p.name != username:
else:
sio.emit(
"chat_message",
room=p.game.name,
data=f"_change_username|{p.name}|{username}",
data=f"_change_username|{p.name}|{data['username']}",
)
p.name = data["username"]
sio.emit("me", data=p.name, room=sid)
@ -423,11 +439,8 @@ def private(sid):
@sio.event
@bang_handler
def toggle_expansion(sid, expansion_name):
game = sio.get_session(sid).game
if "suggest" in expansion_name:
sio.emit("suggest_expansion", room=game.name, data=expansion_name.split(";")[1])
return
game.toggle_expansion(expansion_name)
g = sio.get_session(sid).game
g.toggle_expansion(expansion_name)
advertise_lobbies()
@ -447,27 +460,29 @@ def toggle_replace_with_bot(sid):
@bang_handler
def join_room(sid, room):
room_name = room["name"]
pwd = room.get("password", "")
if pwd is None:
pwd = ""
if games[room_name].password != "" and games[room_name].password != pwd.upper():
if (
games[room_name].password != ""
and games[room_name].password != room.get("password", "").upper()
):
return
player = sio.get_session(sid)
if not games[room_name].started:
print(f"{sid} joined a room named {room_name}")
sio.leave_room(sid, "lobby")
sio.enter_room(sid, room_name)
while any(
(p.name == player.name and not p.is_bot for p in games[room_name].players)
(
p.name == sio.get_session(sid).name and not p.is_bot
for p in games[room_name].players
)
):
player.name += f"_{random.randint(0,100)}"
sio.emit("me", data=player.name, room=sid)
games[room_name].add_player(player)
sio.get_session(sid).name += f"_{random.randint(0,100)}"
sio.emit("me", data=sio.get_session(sid).name, room=sid)
games[room_name].add_player(sio.get_session(sid))
advertise_lobbies()
else:
games[room_name].spectators.append(player)
player.game = games[room_name]
player.pending_action = PendingAction.WAIT
games[room_name].spectators.append(sio.get_session(sid))
sio.get_session(sid).game = games[room_name]
sio.get_session(sid).pending_action = PendingAction.WAIT
sio.enter_room(sid, games[room_name].name)
games[room_name].notify_room(sid)
eventlet.sleep(0.5)
@ -679,14 +694,6 @@ def buy_gold_rush_card(sid, data: int):
ses.buy_gold_rush_card(data)
@sio.event
@bang_handler
def buy_train(sid, data: int):
ses: Player = sio.get_session(sid)
ses.game.rpc_log.append(f"{ses.name};buy_train;{data}")
ses.buy_train(data)
@sio.event
@bang_handler
def chat_message(sid, msg, pl=None):
@ -761,12 +768,6 @@ def chat_message(sid, msg, pl=None):
if "/togglecomp" in msg and ses.game:
ses.game.toggle_competitive()
return
elif "/set_chars" in msg and not ses.game.started:
cmd = msg.split()
if len(cmd) == 2 and int(cmd[1]) > 0:
ses.game.characters_to_distribute = int(cmd[1])
ses.game.notify_room()
return
if "/debug" in msg:
cmd = msg.split()
if (
@ -800,6 +801,10 @@ def chat_message(sid, msg, pl=None):
"text": "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()
if len(cmd) == 2 and int(cmd[1]) > 0:
ses.game.characters_to_distribute = int(cmd[1])
elif "/suicide" in msg and ses.game.started and ses.lives > 0:
ses.lives = 0
ses.notify_self()
@ -1038,9 +1043,7 @@ def chat_message(sid, msg, pl=None):
cmd = msg.split()
if len(cmd) >= 2:
import bang.expansions.train_robbery.trains as trt
cards = cs.get_starting_deck(ses.game.expansions)
cards.extend(trt.get_all_cards())
card_names = " ".join(cmd[1:]).split(",")
for cn in card_names:
ses.equipment.append(
@ -1099,21 +1102,6 @@ def chat_message(sid, msg, pl=None):
"type": "json",
},
)
elif "/deckinfo" in msg:
sio.emit(
"chat_message",
room=sid,
data={
"color": "",
"text": json.dumps(
ses.game.deck.__dict__,
default=lambda o: f"<{o.__class__.__name__}() not serializable>",
),
"type": "json",
},
)
elif "/trainfw" in msg:
ses.game.deck.move_train_forward()
elif "/status" in msg and ses.is_admin():
sio.emit("mount_status", room=sid)
elif "/meinfo" in msg:
@ -1308,29 +1296,6 @@ def get_wildwestshowcards(sid):
"wwscards_info", room=sid, data=json.dumps(chs, default=lambda o: o.__dict__)
)
@sio.event
@bang_handler
def get_trainrobberycards(sid):
print("get_trainrobberycards")
import bang.expansions.train_robbery.cards as trc
import bang.expansions.train_robbery.stations as trs
import bang.expansions.train_robbery.trains as trt
chs = []
chs.extend(trt.get_locomotives())
chs.extend(trt.get_all_cards())
sio.emit(
"trainrobberycards_info", room=sid, data=json.dumps({
"cards": chs,
"stations": trs.get_all_stations()
}, default=lambda o: o.__dict__)
)
@sio.event
@bang_handler
def get_expansion_info(sid, id):
from bang.expansions import get_expansion_info
sio.emit("expansion_info", room=sid, data=json.dumps(get_expansion_info(id), default=lambda o: o.__dict__))
@sio.event
@bang_handler
@ -1339,7 +1304,7 @@ def discord_auth(sid, data):
"https://discord.com/api/oauth2/token",
data={
"client_id": "1059452581027532880",
"client_secret": os.getenv("DISCORD_SECRET", ""),
"client_secret": "Mc8ZlMQhayzi1eOqWFtGHs3L0iXCzaEu",
"grant_type": "authorization_code",
"redirect_uri": data["origin"],
"code": data["code"],

View File

@ -1,4 +1,3 @@
from typing import Any, List
import pytest
from bang.characters import Character
from bang.game import Game
@ -9,7 +8,7 @@ from globals import G
G.sio = DummySocket()
def started_game(expansions=[], players=4, character=Character("test_char", 4)) -> Game:
def started_game(expansions, players=4, character=Character("test_char", 4)):
g = Game("test")
g.expansions = expansions
ps = [Player(f"p{i}", f"p{i}") for i in range(players)]
@ -24,19 +23,19 @@ def started_game(expansions=[], players=4, character=Character("test_char", 4))
return g
def set_events(g: Game, event_cards) -> None:
def set_events(g: Game, event_cards):
g.deck.event_cards = event_cards
def current_player(g: Game) -> Player:
def current_player(g: Game):
return g.players[g.turn]
def next_player(g: Game) -> Player:
def next_player(g: Game):
return g.players[(g.turn + 1) % len(g.players)]
def current_player_with_cards(g: Game, cards: List[Any]) -> Player:
def current_player_with_cards(g: Game, cards):
p = current_player(g)
p.draw("")
p.hand = cards

View File

@ -3,8 +3,7 @@ from bang.characters import Character
from bang.cards import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from globals import PendingAction
from bang.players import Player, PendingAction
# test card Barile
def test_barile():

View File

@ -2,8 +2,7 @@ from random import randint
from bang.characters import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from globals import PendingAction
from bang.players import Player, PendingAction
from bang.cards import *
def test_bartcassidy():

View File

@ -3,7 +3,7 @@ from bang.characters import Character
from bang.expansions.dodge_city.cards import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from bang.players import Player, PendingAction
import bang.cards as cs
# test Borraccia

View File

@ -1,9 +1,8 @@
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from bang.players import Player, PendingAction
from bang.roles import *
from bang.cards import *
from globals import PendingAction
from tests import started_game

View File

@ -1,8 +1,7 @@
from bang.characters import Character
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from globals import PendingAction
from bang.players import Player, PendingAction
from bang.roles import *
from bang.cards import *

View File

@ -1,24 +0,0 @@
from tests import started_game, set_events, current_player, next_player, current_player_with_cards
from bang.expansions.fistful_of_cards.card_events import *
import bang.cards as cs
def test_miniera_abbandonata():
g = started_game(['fistful_of_cards'])
set_events(g, [MinieraAbbandonata()])
p = current_player(g)
starting_cards = len(p.hand)
g.deck.scrap_pile = [
cs.Bang(0, 0),
cs.Bang(0, 1),
cs.Bang(0, 2),
cs.Bang(0, 3),
]
p.draw("")
assert len(p.hand) == starting_cards + 2
# check the last two cards are the ones from the scrap pile
assert p.hand[-2].name == cs.Bang(0, 0).name
assert p.hand[-2].number == 3
assert p.hand[-1].name == cs.Bang(0, 0).name
assert p.hand[-1].number == 2

View File

@ -1,24 +0,0 @@
from random import randint
from bang.characters import Character
from bang.expansions.train_robbery.trains import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
import bang.cards as cs
from globals import PendingAction
from tests import started_game, set_events, current_player, next_player, current_player_with_cards
def test_cattle_truck():
g = started_game()
g.deck.scrap_pile = [cs.CatBalou(0,1), cs.CatBalou(0,2), cs.CatBalou(0,3)]
p = current_player_with_cards(g, [CattleTruck()])
p.play_card(0)
assert p.pending_action == PendingAction.CHOOSE
p.choose(0)
assert p.pending_action == PendingAction.PLAY
assert len(p.hand) == 1
assert len(g.deck.scrap_pile) == 2

View File

@ -3,9 +3,8 @@ from bang.characters import Character
from bang.expansions.the_valley_of_shadows.characters import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from bang.players import Player, PendingAction
import bang.cards as cs
from globals import PendingAction
# test TucoFranziskaner
def test_TucoFranziskaner():

View File

@ -3,12 +3,8 @@ from bang.characters import Character
from bang.expansions.the_valley_of_shadows.cards import *
from bang.deck import Deck
from bang.game import Game
from bang.players import Player
from bang.players import Player, PendingAction
import bang.cards as cs
from globals import PendingAction
from tests import started_game, set_events, current_player, next_player, current_player_with_cards
# test UltimoGiro
def test_ultimo_giro():
@ -226,29 +222,6 @@ def test_bandidos():
assert p1.pending_action == PendingAction.WAIT
assert p.pending_action == PendingAction.PLAY
def test_bandidos_with_gold_rush():
g = Game('test')
g.expansions = ['gold_rush']
ps = [Player(f'p{i}', f'p{i}') for i in range(2)]
for p in ps:
g.add_player(p)
g.start_game()
for p in ps:
p.available_characters = [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 = [Bandidos(0,0), Bandidos(0,0)]
p.play_card(0)
assert len(p.hand) == 1
assert p.pending_action == PendingAction.WAIT
assert p1.pending_action == PendingAction.CHOOSE
p1.choose(len(p1.hand))
assert p1.lives == 3
assert p.pending_action == PendingAction.PLAY
assert p.gold_nuggets == 1
# test Poker
def test_poker():
g = Game('test')
@ -304,63 +277,3 @@ def test_tornado():
assert p.pending_action == PendingAction.PLAY
assert len(p.hand) == 2
assert len(p1.hand) == 2
def test_sventagliata():
g = Game('test')
ps = [Player(f'p{i}', f'p{i}') 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]
p2 = g.players[(g.turn + 2) % 3]
p.draw('')
p.hand = [Sventagliata('Hearts', 10), Bang('Hearts', 10)]
p1.hand = [Mancato('Spades', 2)]
p2.hand = [Mancato('Clubs', 5)]
# Play Sventagliata
p.play_card(0, against=p1.name)
assert p.pending_action == PendingAction.CHOOSE
assert len(p.available_cards) > 0 # Ensure there are available targets
# Simulate choosing a secondary target
secondary_target = p.available_cards[0]['name']
assert secondary_target != p.name and secondary_target != p1.name # Ensure the secondary target is correct
p.choose(0) # Choose the first available target
assert p.pending_action == PendingAction.WAIT
assert p1.pending_action == PendingAction.RESPOND
# Simulate p1 responding to the Bang
p1.respond(0) # Assuming p1 plays a Mancato card in response
assert p1.pending_action == PendingAction.WAIT
assert p.pending_action == PendingAction.WAIT
p2.respond(0) # Assuming p2 plays a Mancato card in response
assert p2.pending_action == PendingAction.WAIT
assert p.pending_action == PendingAction.PLAY
# check bang cannot be played
assert len(p.hand) == 1
p.play_card(0, against=p2.name)
assert p.pending_action == PendingAction.PLAY
assert len(p.hand) == 1
def test_mira():
g = started_game(['the_valley_of_shadows'])
p = current_player(g)
p.draw('')
p.hand = [Mira(0, 0), Bang(0, 0)]
target = next_player(g)
target.hand = []
target_health = target.lives
p.play_card(0, against=target.name, _with=1)
assert target.lives == target_health - 2

View File

@ -4,7 +4,7 @@ from tests import started_game, set_events, current_player, next_player, current
from bang.expansions.wild_west_show.characters import *
from bang.cards import Card, Suit
import bang.roles as roles
from globals import PendingAction
from bang.players import PendingAction
# test TerenKill

View File

@ -4,7 +4,7 @@ from tests import started_game, set_events, current_player, next_player, current
from bang.expansions.wild_west_show.card_events import *
from bang.cards import Card, Suit
import bang.roles as roles
from globals import PendingAction
from bang.players import PendingAction
# test Camposanto

View File

@ -9,11 +9,12 @@
},
"dependencies": {
"@datadog/browser-rum": "^4.36.0",
"@vueuse/core": "^9.13.0",
"bang-vue-socket.io": "^4.0.0",
"caniuse-lite": "^1.0.30001470",
"pretty-checkbox-vue": "^1.1.9",
"register-service-worker": "^1.7.2",
"vue": "^2.7.16",
"vue": "^2.7.14",
"vue-clipboard2": "^0.3.3",
"vue-i18n": "^8.28.2",
"vue-json-viewer": "^2.2.22",
@ -22,7 +23,7 @@
"devDependencies": {
"eslint": "^8.36.0",
"eslint-plugin-vue": "^9.10.0",
"vite": "^4.5.3",
"vite": "^4.2.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-pwa": "^0.14.6",
"vite-plugin-vue2": "^2.0.3",

View File

@ -29,7 +29,7 @@
</select>
<select id="lang" class="btn" v-model="$i18n.locale" @change="storeLangPref">
<option
v-for="(lang, i) in ['it.🇮🇹.Italiano', 'en.🇬🇧.English', 'cs.🇨🇿.Čeština', 'fr.🇫🇷.Français', 'es.🇪🇸.Español', 'ru.🇷🇺.Русский']"
v-for="(lang, i) in ['it.🇮🇹.Italiano', 'en.🇬🇧.English', 'cs.🇨🇿.Čeština']"
:key="`lang-${i}`"
:value="lang.split('.')[0]">
{{lang.split('.')[1]}} {{lang.split('.')[2]}}
@ -189,7 +189,6 @@ export default {
trackResources: true, //non so cosa faccia, proviamo
trackLongTasks: true, //non so cosa faccia, proviamo
defaultPrivacyLevel: 'allow',
enableExperimentalFeatures: ['clickmap'],
proxyUrl: (Vue.config.devtools ? `http://${window.location.hostname}:5001` : window.location.origin) + '/ddproxy',
});
datadogRum.setUser({name: localStorage.getItem('username')})

View File

@ -1,49 +0,0 @@
<template>
<p class="animated" :style="style">{{text}}</p>
</template>
<script>
export default {
name: 'AnimatedEffect',
props: {
text: String,
startPosition: Object,
},
data: () => ({
style: ''
}),
computed: {
},
methods: {
},
mounted() {
let startPosition = {
top: this.startPosition.top + Math.random() * 100 - 50,
left: this.startPosition.left + Math.random() * 100 - 25,
}
let endPosition = {
top: startPosition.top + Math.random() * 50 - 25,
left: startPosition.left + Math.random() * 50 - 25,
}
this.style = `position: absolute;top:${startPosition.top}px;left: ${startPosition.left}px;`
setTimeout(() => {
this.style = `position:absolute;;top:${endPosition.top}px;left:${endPosition.left}px;`
}, 400)
setTimeout(() => {
this.style = `position:absolute;;top:${endPosition.top}px;left:${endPosition.left}px;opacity:0;`
}, 900)
setTimeout(() => {
this.style = `display:none;`
}, 1500)
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.animated {
position: absolute;
font-size: 26pt;
transition: all 0.5s;
}
</style>

View File

@ -11,7 +11,6 @@
'gold-rush': card.expansion === 'gold_rush',
brown: card.kind === 0,
black: card.kind === 1,
'train-piece': card.type && card.type === 'train',
}"
>
<h4>{{ cardName }}</h4>
@ -21,7 +20,6 @@
:style="`background-image: url(${card.avatar});`"
></div>
<div :class="{ emoji: true, bottomed: card.avatar }">{{ emoji }}</div>
<div v-if="card.isMe" :class="{ emoji: true, bottomed: card.avatar, isMe: true }"></div>
<div class="alt_text">{{ card.alt_text }}</div>
<div class="suit">
{{ number
@ -65,7 +63,7 @@ export default {
},
suit() {
if (this.card && !isNaN(this.card.suit)) {
let x = ["♦️", "♣️", "♥️", "♠️", "🤑", "🚂"];
let x = ["♦️", "♣️", "♥️", "♠️", "🤑"];
return x[this.card.suit];
} else if (this.card.suit) {
return this.card.suit;
@ -116,7 +114,7 @@ export default {
#816b45 10px
);
}
.card:not(.back, .fistful-of-cards, .high-noon, .gold-rush, .train-piece):before {
.card:not(.back, .fistful-of-cards, .high-noon, .gold-rush):before {
content: "";
background-image: radial-gradient(var(--bg-color) 13%, #0000 5%),
radial-gradient(var(--bg-color) 14%, transparent 5%),
@ -232,18 +230,6 @@ export default {
padding: 4pt;
border-radius: 12pt;
}
.wip::after {
content: "WIP";
position: absolute;
bottom: -12pt;
right: -12pt;
background: red;
font-size: 10pt;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
padding: 4pt;
border-radius: 12pt;
}
.avatar {
position: absolute;
width: 36pt;
@ -285,42 +271,10 @@ export default {
font-size: 26pt;
top: 35%;
}
.card .emoji.isMe {
position: absolute;
text-align: center;
width: 100%;
font-size: 16pt;
top: 52%;
right: 12pt;
}
.emoji.isMe::after {
content: "🫵";
display: block;
}
.card:HOVER .isMe::after {
content: "👋";
animation: wave 0.5s infinite;
will-change: transform;
}
@keyframes wave {
0% {
transform: translate(-5px, 0px) rotate(0deg);
}
50% {
transform: translate(-5px, -5px) rotate(25deg);
}
100% {
transform: translate(-5px, 0) rotate(0deg);
}
}
.emoji.bottomed {
top: 45%;
left: 8pt;
}
.emoji.bottomed.emoji.isMe {
top: 60%;
left: -5pt;
}
.card.must-be-used {
filter: drop-shadow(0 0 5px red);
}
@ -351,7 +305,6 @@ export default {
}
.cant-play {
filter: brightness(0.5);
cursor: not-allowed;
}
.expansion {
position: absolute;
@ -361,24 +314,4 @@ export default {
border-radius: 100%;
transform: scale(0.8);
}
.train-piece {
background: linear-gradient(180deg, rgba(218,101,64,1) 0%, rgba(217,197,184,1) 13%, rgba(217,197,184,1) 53%, rgba(235,169,95,1) 61%, rgba(158,81,55,1) 91%, rgba(158,81,55,1) 100%);
box-shadow: 0 0 0pt 2pt var(--font-color), 0 0 5pt 2pt #aaa;
}
.train-piece .emoji {
transform: scaleX(-1);
/* filter: grayscale(1); */
}
.train-piece .suit, .train-piece .expansion {
display: none;
}
.train-piece h4 {
position: absolute;
text-align: center;
width: 100%;
bottom: -10pt;
top: unset;
font-size: 11pt;
color: #FFE27E;
}
</style>

View File

@ -21,8 +21,7 @@
@click="fillCmd(msg.cmd)">{{msg.cmd}} <i class="std-text" style="font-size:8pt;">{{msg.help}}</i></p>
</div>
<form @submit="sendChatMessage" id="msg-form">
<input id="my-msg" autocomplete="off" v-model="text" style="flex-grow:2;"
@keydown.tab.prevent="tabComplete($event.target.value)"/>
<input id="my-msg" autocomplete="off" v-model="text" style="flex-grow:2;"/>
<input id="submit-message" type="submit" class="btn" :value="$t('submit')"/>
</form>
</div>
@ -78,22 +77,13 @@ export default {
msg = msg.text
}
let desc = undefined
let desc_pos = -1
let params = msg.split('|')
let type = params.shift().substring(1)
if (["flipped", "scrapped", "respond", "play_card", "play_card_green", "play_card_with", "purchase_card", "play_card_against", "play_card_against_with", "play_card_for", "spilled_beer", "diligenza", "wellsfargo", "saloon", "special_calamity", "won", "choose_emporio", "died_role"].indexOf(type) !== -1) {
if (type.indexOf("_with") !== -1) {
params[params.length - 1] = this.$t(`cards.${params[params.length - 1]}.name`)
}
if (["flipped", "respond", "play_card", "play_card_against", "play_card_for", "spilled_beer", "diligenza", "wellsfargo", "saloon", "special_calamity", "won", "choose_emporio", "died_role"].indexOf(type) !== -1) {
desc = this.$t(`cards.${params[1]}.desc`)
desc_pos = 3
params[1] = this.$t(`cards.${params[1]}.name`)
} else if (type === "choose_character"){
params.push(this.$t(`cards.${params[1]}.desc`))
} else if (type === "flip_event"){
desc = this.$t(`cards.${params[0]}.desc`)
params[0] = this.$t(`cards.${params[0]}.name`);
desc_pos = 1
} else if (type === "allroles") {
params.forEach((p,i)=>{
if (i%2 === 0) {
@ -106,11 +96,10 @@ export default {
type += "4"
}
}
let parts = this.$t(`chat.${type}`, params).split(';').map((x, i)=>({text:x, desc:(i===desc_pos&&desc?desc:null)}))
if (t_color != null) {
this.messages.push({color:t_color, bgcolor: bg_color, text:false, parts: parts})
this.messages.push({color:t_color, bgcolor: bg_color, text:false, parts:this.$t(`chat.${type}`, params).split(';').map(x=>({text:x}))});
} else {
this.messages.push({text:false, parts: parts});
this.messages.push({text:false, parts: this.$t(`chat.${type}`, params).split(';').map((x, i)=>({text:x, desc:(i===3&&desc?desc:null)}))});
}
if (type == 'turn' && params[0] == this.username) {
this.playEffects(turn_sfx);
@ -165,12 +154,6 @@ export default {
this.text = cmd;
document.getElementById('my-msg').focus();
},
tabComplete() {
if (this.commandSuggestion.length > 0) {
let cmd = this.commandSuggestion[0].cmd;
this.text = cmd + ' ';
}
},
playEffects(path) {
const promise = (new Audio(path)).play();
if(promise !== undefined){

View File

@ -1,7 +1,7 @@
<template>
<div >
<div class="deck">
<div class="deck" id="gold-rush-deck" :style="`position:relative;${goldRushShopOpen?'border: 2px dashed #6a6a6a42;border-radius:8pt':''}`" v-if="goldRushCards.length > 0" >
<div class="deck" :style="`position:relative;${goldRushShopOpen?'border: 2px dashed #6a6a6a42;border-radius:8pt':''}`" v-if="goldRushCards.length > 0" >
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[0])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(95deg) translate(30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 0" :key="goldRushCards[0].name" :card="goldRushCards[0]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[0].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(0)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[1])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(90deg) translate(0, -40px) scale(0.6)`" v-if="goldRushCards.length > 1" :key="goldRushCards[1].name" :card="goldRushCards[1]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[1].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(1)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[2])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(85deg) translate(-30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 2" :key="goldRushCards[2].name" :card="goldRushCards[2]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[2].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(2)}"/>
@ -11,10 +11,6 @@
<card :card="goldRushCardBack" :donotlocalize="true" class="gold-rush back last-event" @click.native="goldRushShopOpen = !goldRushShopOpen"/>
</div>
</div>
<div v-if="currentStations.length > 0" id="train-robbery-deck" class="deck" :style="`position:relative;border: 2px dashed #6a6a6a42;border-radius:8pt;align-items: flex-end;flex-direction:row;`" >
<station-card @click.native="()=>{buyTrain(i)}" v-for="station, i in currentStations" :key="station.name" :card="station" :price="station.price" :trainPiece="trainPieceForStation(i)"
@pointerenter.native="()=>{setStationDesc(i)}" @pointerleave.native="stationDesc = null"/>
</div>
<div v-if="eventCard" style="position:relative">
<div class="card fistful-of-cards" style="position:relative; bottom:-3pt;right:-3pt;"/>
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
@ -41,7 +37,7 @@
</div>
</div>
<transition name="list">
<p v-if="eventCard" class="center-stuff">🔥 <b>{{eventDesc}}</b> 🎴</p>
<p v-if="eventCard" class="center-stuff"><b>{{eventDesc}}</b></p>
</transition>
<transition name="list">
<p v-if="eventCardWildWestShow && !eventCardWildWestShow.back" class="center-stuff">🎪 <b>{{eventDescWildWestShow}}</b> 🎪</p>
@ -52,11 +48,6 @@
<p class="center-stuff">🤑 <b>{{goldRushDesc.number - gold_rush_discount}} 💵</b> 🤑</p>
</div>
</transition>
<transition name="list">
<div v-if="stationDesc">
<p class="center-stuff"><i>{{stationDesc}}</i></p>
</div>
</transition>
<div style="margin-bottom:6pt;margin-bottom: 6pt;display: flex;flex-direction: column;">
<button class="btn" v-if="pending_action == 2 && can_gold_rush_discard" @click="$socket.emit('gold_rush_discard')">{{$t('gold_rush_discard')}}</button>
</div>
@ -68,7 +59,6 @@
<script>
import Card from '@/components/Card.vue'
import StationCard from '@/components/StationCard.vue'
export default {
name: 'Deck',
@ -77,7 +67,6 @@ export default {
},
components: {
Card,
StationCard
},
data: () => ({
card: {
@ -99,11 +88,8 @@ export default {
goldRushCards: [],
gold_nuggets: 0,
goldRushDesc: null,
stationDesc: null,
can_gold_rush_discard: false,
gold_rush_discount: 0,
currentStations: [],
currentTrain: [],
}),
sockets: {
self(self){
@ -137,11 +123,6 @@ export default {
console.log('GOLD RUSH:'+ cards)
this.goldRushCards = JSON.parse(cards)
},
stations(stations) {
let msg = JSON.parse(stations)
this.currentStations = msg.stations
this.currentTrain = msg.current_train
},
},
computed: {
endTurnCard() {
@ -182,15 +163,6 @@ export default {
},
},
methods: {
trainPieceForStation(i) {
let index = this.currentTrain.length-5-i;
if (index < 0 || index >= this.currentTrain.length)
return null;
return this.currentTrain[index];
},
buyTrain(i) {
this.$socket.emit('buy_train', i)
},
action(pile) {
if (this.pending_action !== false && this.pending_action < 2) {
// console.log('action')
@ -217,14 +189,6 @@ export default {
setGoldRushDesc(card) {
this.goldRushDesc = card
},
setStationDesc(index) {
console.log('setStationDesc', index)
this.stationDesc = this.$t(`cards.${this.currentStations[index].name}.desc`)
const trainPiece = this.trainPieceForStation(index)
if (trainPiece) {
this.stationDesc += '\n\n🚂' + this.$t(`cards.${trainPiece.name}.desc`) + '🚋'
}
},
},
mounted() {
if (window.innerWidth < 1000) {

View File

@ -1,117 +0,0 @@
<template>
<div class="popup-overlay" v-if="show" @click="handleOverlayClick">
<div class="popup-content" @click.stop>
<button class="close-button" @click="close">×</button>
<h2>{{ expansion.name }}</h2>
<div v-for="section in expansion.cards" :key="section.type" class="section">
<h3>{{ section.type }}</h3>
<div class="cards-container flexy-cards-wrapper">
<div v-for="card in section.cards" :key="card.name" class="flexy-cards">
<Card :card="card" v-if="section.type !== 'stations'" :class="getClass(expansion, section)"/>
<StationCard :card="card" :price="card.price" v-else-if="section.type === 'stations'"/>
<div style="margin-left:6pt;">
<p>{{$t(`cards.${card.name}.desc`)}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Card from '@/components/Card.vue';
import StationCard from './StationCard.vue';
export default {
props: {
show: Boolean,
expansion: Object, // Expecting an object with id, name, and cards
},
components: {
Card,
StationCard,
},
methods: {
close() {
this.$emit('close');
},
handleOverlayClick() {
this.close();
},
getClass(expansion, section) {
let classes = ''
if (section.type == 'events') {
classes += 'last-event';
}
if (expansion.id == 'fistful_of_cards') {
classes += ' fistful-of-cards';
} else if (expansion.id == 'high_noon') {
classes += ' high-noon';
} else if (expansion.id == 'gold_rush') {
classes += ' gold-rush';
} else if (expansion.id == 'train_robbery') {
classes += ' train-robbery';
} else if (expansion.id == 'the_valley_of_shadows') {
classes += ' valley-of-shadows';
} else if (expansion.id == 'wild_west_show') {
classes += ' wild-west-show';
}
console.log(classes);
return classes;
}
},
};
</script>
<style scoped>
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.popup-content {
position: relative;
background: white;
padding: 20px;
border-radius: 5px;
max-width: 80%;
max-height: 80%;
overflow-y: auto;
}
.close-button {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
.section {
margin-bottom: 20px;
}
.cards-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.card {
box-sizing: border-box;
margin-bottom: 10px;
}
.flexy-cards-wrapper {
display: flex;
flex-flow: wrap;
}
.flexy-cards {
flex: 30%;
display:flex;
}
</style>

View File

@ -127,32 +127,15 @@
</div>
</div>
</div>
<h2 id="trainrobberycards">{{$t('help.trainrobberycards')}}</h2>
<div class="flexy-cards-wrapper">
<div v-for="(c, i) in trainrobberycards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
<Card :card="c" class="train-robbery" @pointerenter.native="''" @pointerleave.native="''"/>
<div style="margin-left:6pt;">
<p>{{$t(`cards.${c.name}.desc`)}}</p>
</div>
</div>
<div v-for="(c, i) in trainrobberystations" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
<StationCard :card="c" class="train-robbery" @pointerenter.native="''" @pointerleave.native="''" :price="c.price"/>
<div style="margin-left:6pt;">
<p>{{$t(`cards.${c.name}.desc`)}}</p>
</div>
</div>
</div>
</div>
</template>
<script>
import Card from '@/components/Card.vue'
import StationCard from './StationCard.vue'
export default {
name: 'Help',
components: {
Card,
StationCard,
},
props: {
inGame: Boolean
@ -169,8 +152,6 @@ export default {
goldrushcards: [],
valleyofshadowscards: [],
wildwestshowcards: [],
trainrobberycards: [],
trainrobberystations: [],
}),
computed: {
endTurnCard() {
@ -215,14 +196,6 @@ export default {
...x,
}))
},
trainrobberycards_info(cardsJson) {
this.trainrobberycards = JSON.parse(cardsJson).cards.map(x=>({
...x,
}))
this.trainrobberystations = JSON.parse(cardsJson).stations.map(x=>({
...x,
}))
},
},
mounted() {
this.$socket.emit('get_cards')
@ -232,7 +205,6 @@ export default {
this.$socket.emit('get_goldrushcards')
this.$socket.emit('get_valleyofshadowscards')
this.$socket.emit('get_wildwestshowcards')
this.$socket.emit('get_trainrobberycards')
document.getElementById('help').scrollIntoView();
}
}

View File

@ -47,6 +47,12 @@
:value="$t('copy')"
/>
</div>
<!-- <div class="players-table"> -->
<!-- <div style="position: relative;width:260pt;height:400pt;"> -->
<!-- :style="p.style"/> -->
<!-- </div> -->
<!-- </div> -->
<div style="position: relative">
<div
v-if="showTurnFlow"
@ -65,18 +71,26 @@
<div
v-for="p in playersTable"
v-bind:key="p.card.name"
:id="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>
<transition-group
v-if="p.max_lives && !p.is_ghost"
name="list"
tag="div"
class="tiny-health"
>
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i">
{{(!p.is_sheriff || i !== (p.max_lives-1))?'❤️':'💛'}}</span>
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i"></span>
<span
v-for="(n, i) in p.max_lives - p.lives"
v-bind:key="`${i}-sk`"
@ -106,12 +120,6 @@
class="character tiny-character"
@click.native="selectedInfo = [p.character]"
/>
<div
v-if="p.gold_nuggets && p.gold_nuggets > 0"
style="position: absolute; top: 45pt; left: -5pt; font-size: 9pt;"
>
<h3 style="background:gold;border-radius:15pt;padding:0pt 2pt;color:black"> 💵 {{ p.gold_nuggets }} </h3>
</div>
<tiny-hand
:id="p.name + '-hand'"
:ncards="p.ncards"
@ -195,20 +203,17 @@
<p v-else style="min-height: 19px"></p>
<h3>{{ $t("expansions") }}</h3>
<div class="players-table" style="justify-content: flex-start">
<div v-for="ex in expansionsStatus" :key="ex.id" class="expansion-card" style="position: relative">
<card
:id="ex.id"
v-for="ex in expansionsStatus"
v-bind:key="ex.id"
:card="ex.card"
:class="{
'cant-play': !ex.enabled,
...ex.card.classes,
}"
style="cursor: pointer"
:donotlocalize="true"
@click.native="toggleExpansions(ex.id)"
/>
<button class="info-button" @click="showExpansionInfo(ex.id)">?</button>
</div>
</div>
<p v-if="isRoomOwner">{{ $t("click_to_toggle") }}</p>
<h3>{{ $t("mods") }}</h3>
@ -220,57 +225,6 @@
style="margin-top: 5px; margin-bottom: 3px"
>{{ $t("mod_comp") }}</PrettyCheck
>
<br/>
<br/>
<span>{{$t("characters_to_distribute")}}</span>
<input
type="button"
:class="{btn:true, 'small-btn':true, active: characters_to_distribute === 1}"
:value="1"
:disabled="!isRoomOwner"
@click="
(e) => {
this.$socket.emit('chat_message', '/set_chars 1');
e.preventDefault();
}
"
/>
<input
type="button"
:class="{btn:true, 'small-btn':true, active: characters_to_distribute === 2}"
:value="2"
:disabled="!isRoomOwner"
@click="
(e) => {
this.$socket.emit('chat_message', '/set_chars 2');
e.preventDefault();
}
"
/>
<input
type="button"
:class="{btn:true, 'small-btn':true, active: characters_to_distribute === 3}"
:value="3"
:disabled="!isRoomOwner"
@click="
(e) => {
this.$socket.emit('chat_message', '/set_chars 3');
e.preventDefault();
}
"
/>
<input
type="button"
:class="{btn:true, 'small-btn':true, active: characters_to_distribute === 4}"
:value="4"
:disabled="!isRoomOwner"
@click="
(e) => {
this.$socket.emit('chat_message', '/set_chars 4');
e.preventDefault();
}
"
/>
<h3>{{ $t("bots") }}</h3>
<input
type="button"
@ -308,12 +262,6 @@
:midPosition="c.midPosition"
:endPosition="c.endPosition"
/>
<AnimatedEffect
v-for="c in fullScreenEffects"
v-bind:key="c.key"
:text="c.text"
:startPosition="c.startPosition"
/>
<div v-if="started">
<deck
:endTurnAction="
@ -339,6 +287,12 @@
</div>
</div>
<chat :username="username" />
<div id="cursors" style="position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none;">
<div v-for="x, i in mouse_positions" :key="x.name" :style="{position:'absolute', top: x.data.y + 'px', left: x.data.x + 'px', transition: 'all 0.1s linear'}">
<div :style="`width: 0;height: 0;border-left: 10px solid transparent;border-right: 10px solid transparent;border-bottom: 20px solid #${x.data.color};transform: rotate(-30deg);`"/>
<span>{{ x.name }}</span>
</div>
</div>
<Chooser
v-if="selectedInfo"
:text="$t('details')"
@ -388,18 +342,10 @@
<transition name="bounce">
<DeadRoleNotification
v-if="deadRoleData"
:key="deadRoleData.name"
:playerCard="deadRoleData"
:playerRole="deadRoleData.role"
/>
</transition>
<transition name="bounce">
<ExpansionPopup
:show="showPopup"
:expansion="selectedExpansionInfo"
@close="closePopup"
/>
</transition>
</div>
</template>
@ -418,8 +364,6 @@ import DeadRoleNotification from "./DeadRoleNotification.vue";
import AnimatedCard from "./AnimatedCard.vue";
import { emojiMap } from "@/utils/emoji-map.js";
import { expansionsMap } from "@/utils/expansions-map.js";
import AnimatedEffect from './AnimatedEffect.vue';
import ExpansionPopup from '@/components/ExpansionPopup.vue';
const cumulativeOffset = function (element) {
var top = 0,
@ -434,6 +378,7 @@ const cumulativeOffset = function (element) {
left: left - Math.floor(Math.random() * 20) + 10,
};
};
import { useMouse } from '@vueuse/core'
export default {
name: "Lobby",
@ -449,8 +394,7 @@ export default {
Status,
DeadRoleNotification,
AnimatedCard,
AnimatedEffect,
ExpansionPopup,
Card,
},
data: () => ({
username: "",
@ -477,18 +421,12 @@ export default {
displayAdminStatus: false,
is_replay: false,
turn: -1,
deadRoleData: false,
deadRoleData: null,
cardsToAnimate: [],
characters_to_distribute: 2,
fullScreenEffects: [],
showPopup: false,
selectedExpansionInfo: {},
owner: undefined,
mouse_positions: {},
my_mouse_pos: {},
}),
sockets: {
expansion_info(data) {
this.selectedExpansionInfo = JSON.parse(data);
},
room(data) {
this.lobbyName = data.name;
if (!data.started) {
@ -504,8 +442,6 @@ export default {
this.togglable_expansions = data.available_expansions;
this.expansions = data.expansions;
this.is_replay = data.is_replay;
this.owner = data.owner;
this.characters_to_distribute = data.characters_to_distribute;
this.players = data.players.map((x) => {
return {
name: x.name,
@ -519,9 +455,22 @@ export default {
notify_dead_role(data) {
this.deadRoleData = data;
setTimeout(() => {
this.deadRoleData = false;
this.deadRoleData = null;
}, 4000);
},
mouse_positions_update(data) {
let newData = data.filter((x) => x.name !== this.username).map((x) => {
return {
name: x.name,
data: {
x: x.data.x * window.innerWidth,
y: x.data.y * window.innerHeight,
color: x.data.color,
}
};
});
this.mouse_positions = newData;
},
debug(data) {
this.debug_mode = data;
},
@ -585,94 +534,14 @@ export default {
this.cardsToAnimate = this.cardsToAnimate.filter((x) => x.key !== key);
}, 1800);
},
chat_message(msg) {
if (typeof msg !== "string") {
let key = Math.random();
let username = msg.text.substring(1, msg.text.indexOf(":")-1);
setTimeout(() => {
this.fullScreenEffects.push({
key: key,
text: '💬',
startPosition: cumulativeOffset(document.getElementById(username)),
});
}, 50);
setTimeout(() => {
this.fullScreenEffects = this.fullScreenEffects.filter(
(x) => x.key !== key
);
}, 3000);
return;
}
let params = msg.split('|')
let type = params.shift().substring(1)
let messageMap = {
prison_turn: '⛓️;🔒;⏭️',
explode: '💥;🧨',
purchase_card: '🛒;💸',
prison_free: '🆓;🔑',
snake_bit: '🐍;🩸',
beer_save: '🍺;😇',
sheriff: '⭐',
spilled_beer: '🍺;😭',
use_special: '🔝;✨',
died: '💀;👻;😭;☠️;🪦;F',
died_role: '💀;👻;😭;☠️;🪦;F',
}
if (messageMap[type]) {
let key = Math.random();
let avail = messageMap[type].split(';');
for (let i = 0; i < 5; i++) {
setTimeout(() => {
this.fullScreenEffects.push({
key: key+i,
text: avail[Math.floor(Math.random() * avail.length)],
startPosition: cumulativeOffset(document.getElementById(params[0])),
});
}, 50 * i);
setTimeout(() => {
this.fullScreenEffects = this.fullScreenEffects.filter(
(x) => x.key !== key+i
);
}, 3000);
}
}
},
suggest_expansion(expansionName) {
if (this.expansions.includes(expansionName)) return;
let key = Math.random();
let decel = document.getElementById(expansionName);
if (!decel) return;
let decelOffset = cumulativeOffset(decel);
for (let i = 0; i < 6; i++) {
setTimeout(() => {
this.fullScreenEffects.push({
key: key+i,
text: i == 0 ? '🤠' : i == 5 ? '💭' : emojiMap[expansionName],
startPosition: decelOffset,
});
}, 50 * i);
setTimeout(() => {
this.fullScreenEffects = this.fullScreenEffects.filter(
(x) => x.key !== key+i
);
}, 3000);
}
},
card_scrapped(data) {
let decel = document.getElementById("actual-scrap");
if (!decel) {
console.log("card_scrapped no deck");
return;
}
let phand = document.getElementById(`${data.player}-hand`);
if (data.pile == "train_robbery") {
decel = phand
phand = document.getElementById("train-robbery-deck");
} else if (data.pile == "gold_rush") {
decel = phand
phand = document.getElementById("gold-rush-deck");
}
let decelOffset = cumulativeOffset(decel);
let phand = document.getElementById(`${data.player}-hand`);
if (!phand) {
console.log("card_scrapped no phand");
return;
@ -731,7 +600,11 @@ export default {
return "";
},
isRoomOwner() {
return this.owner === this.username;
if (this.players.length > 0) {
let pls = this.players.filter((x) => !x.is_bot);
return pls.length > 0 && pls[0].name == this.username;
}
return false;
},
startGameCard() {
if (!this.started && this.players.length > 2 && this.isRoomOwner) {
@ -769,14 +642,6 @@ export default {
},
},
methods: {
showExpansionInfo(id) {
this.showPopup = true;
this.$socket.emit("get_expansion_info", id);
},
closePopup() {
this.showPopup = false;
this.selectedExpansionCards = [];
},
getExpansionCard(id) {
let ex = expansionsMap[id];
ex.classes = {
@ -802,9 +667,29 @@ export default {
document.title = "PewPew!";
},
toggleExpansions(name) {
if (!this.isRoomOwner) return this.$socket.emit("toggle_expansion", `suggest;${name}`);
if (!this.isRoomOwner) return;
this.$socket.emit("toggle_expansion", name);
},
// updateMousePosition() {
// this.my_mouse_pos = {
// x, y
// }
// this.$socket.emit("mouse_position", this.my_mouse_pos);
// },
startSendingMousePositions() {
// this.$el.addEventListener('mousemove', this.updateMousePosition());
const { x, y } = useMouse()
this.sendInterval = setInterval(() => {
if (x == 0 && y == 0) return;
if (this.my_mouse_pos.x == x && this.my_mouse_pos.y == y) return;
if (this.my_mouse_pos.x == 0 || this.my_mouse_pos.y == 0) return;
// normalize to page size
this.my_mouse_pos = Object.assign({}, { x: x.value / window.innerWidth, y: y.value/ window.innerHeight });
console.log(this.my_mouse_pos);
this.$socket.emit("mouse_position", this.my_mouse_pos);
}, 100);
},
toggleCompetitive() {
if (!this.isRoomOwner) return;
this.$socket.emit("toggle_comp");
@ -828,7 +713,6 @@ export default {
let icon = "";
let nonBots = this.players.filter((x) => !x.is_bot);
let isOwner = nonBots.length > 0 && nonBots[0].name == player.name;
let isMe = this.username == player.name;
if (!this.started) icon = "🤠";
else
icon =
@ -842,12 +726,11 @@ export default {
return {
name: player.name,
number:
(isMe
(this.username == player.name
? this.$t("you")
: isOwner
? this.$t("owner")
: "") + (player.dist ? `${player.dist}` : ""),
isMe: isMe,
icon: icon,
is_character: true,
avatar: player.avatar,
@ -932,6 +815,7 @@ export default {
replay: this.$route.query.replay,
ffw: this.$route.query.ffw,
});
this.startSendingMousePositions();
},
};
</script>
@ -998,13 +882,6 @@ export default {
justify-content: space-evenly;
margin-bottom: 12pt;
}
.small-btn {
min-width: 28pt;
}
.small-btn.active {
color: var(--bg-color);
background: var(--font-color);
}
#admin-status {
position: absolute;
width: 100%;
@ -1067,29 +944,4 @@ export default {
transform: translateY(-5px) scale(1.05);
}
}
@media only screen and (max-width: 500pt) {
.players-table {
border-bottom: dashed #ccc2;
}
}
.info-button {
position: absolute;
top: 5px;
right: 5px;
background-color: #007bff;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 16px;
}
.info-button:hover {
background-color: #0056b3;
}
</style>

View File

@ -11,8 +11,11 @@
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
<span v-for="(n, i) in lives" v-bind:key="i" :alt="i"></span>
</transition-group>
<div v-if="gold_nuggets > 0" style="position:relative;left:-90pt;top:65pt;justify-content: space-evenly;width: 25pt;">
<h3 style="background:gold;border-radius:15pt;padding:2pt 2pt;color:black;text-align:center"> 💵 {{ gold_nuggets }} </h3>
<div v-if="gold_nuggets > 0" style="display: flex;align-items: center;margin-left: 12pt;margin-right: -10pt;justify-content: space-evenly;width: 25pt;">
<transition name="list">
<span :key="gold_nuggets">{{gold_nuggets}}</span>
</transition>
<span>💵</span>
</div>
<transition-group v-if="lives > 0 || is_ghost" name="list" id="equipment" tag="div" style="margin: 0 0 0 10pt; display:flex;">
<Card v-for="card in equipmentComputed" v-bind:key="card.name+card.number" :card="card"
@ -273,7 +276,7 @@ export default {
is_character: true,
is_player: true
}})
if (this.card_against && this.card_against.can_target_self && (this.equipment.length > 0 || this.card_against.name === 'Tequila')) {
if (this.card_against && this.card_against.can_target_self && this.equipment.length > 0) {
vis.push({
name: this.name,
number: '0⛰',
@ -500,7 +503,7 @@ export default {
padding: 10pt 40pt 0pt 40pt;
overflow:auto;
border-radius: 4pt;
min-height: 122pt;
min-height: 40pt;
}
@media not all and (min-resolution:.001dpcm)
{ @supports (-webkit-appearance:none) and (stroke-color:transparent) {

View File

@ -1,126 +0,0 @@
<template>
<div>
<div :class="{stationcard:true, 'cant-play':!trainPiece || trainPiece.is_locomotive}">
<h4>{{ cardName }}</h4>
<div :class="{ emoji: true, bottomed: card.avatar }">{{ emoji }}</div>
<div class="alt_text">{{ card.alt_text }}</div>
<div class="price">
<card v-for="c, i in price" :key="i" :card="c"/>
</div>
</div>
<card v-if="trainPiece" :card="trainPiece" :class="{'cant-play':trainPiece.is_locomotive}"/>
</div>
</template>
<script>
import Card from '@/components/Card.vue'
export default {
name: "StationCard",
props: {
card: Object,
price: Array,
trainPiece: Object,
donotlocalize: Boolean,
},
components: {
Card
},
computed: {
cardName() {
if (
!this.donotlocalize &&
this.$t(`cards.${this.card.name}.name`) !==
`cards.${this.card.name}.name`
) {
return this.$t(`cards.${this.card.name}.name`);
}
if (this.card.name == "you") {
return this.$t("you");
}
return this.card.name;
},
emoji() {
return this.card.icon != "you" ? this.card.icon : this.$t("you");
},
suit() {
if (this.card && !isNaN(this.card.suit)) {
let x = ["♦️", "♣️", "♥️", "♠️", "🤑", "🚂"];
return x[this.card.suit];
} else if (this.card.suit) {
return this.card.suit;
}
return "";
},
number() {
if (isNaN(this.card.suit)) return this.card.number;
if (this.card.number === 1) return "A";
else if (this.card.number === 11) return "J";
else if (this.card.number === 12) return "Q";
else if (this.card.number === 13) return "K";
else return this.card.number;
},
},
};
</script>
<style scoped>
.stationcard {
cursor: pointer;
width: 60pt;
min-width: 60pt;
height: 60pt;
margin: 6pt;
border-radius: 16pt 16pt 2pt 2pt;
position: relative;
transition: all 0.5s ease-in-out;
text-overflow: ellipsis;
word-wrap: normal;
color: white;
background: repeating-linear-gradient(
0deg,
rgb(198 78 45),
rgb(198 78 45) 5px,
rgb(178 58 25) 5px,
rgb(178 58 25) 10px
);
border: 2pt solid rgb(198 78 45);
box-shadow: 0 0 0pt 2pt var(--font-color), 0 0 5pt 2pt #aaa;
}
.stationcard h4 {
position: absolute;
text-align: center;
width: 100%;
top: -15pt;
font-size: 10pt;
}
.alt_text {
right: 3pt;
text-align: center;
position: absolute;
font-size: small;
bottom: 20pt;
left: 3pt;
}
.stationcard .price {
justify-content: center;
/* left: -12pt; */
margin-top: -20pt;
/* left: 0; */
display: flex;
width: 60pt;
transform: scale(0.3);
}
.price .card {
transform: rotate(14deg);
}
.train-piece {
margin: 6pt;
}
.cant-play {
cursor: not-allowed;
}
.stationcard .card {
cursor: unset;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@
"spectate_lobbies": "Spectate ongoing games:",
"no_lobby_available": "No lobbies available",
"create_lobby": "Open a lobby:",
"characters_to_distribute": "Characters to distribute: ",
"lobby_name": "Name:",
"leave_room": "Leave lobby",
"warning": "Warning!",
@ -100,56 +99,47 @@
"copy": "Copy invite",
"no_players_in_range": "You can't see the other players, equip a weapon or a scope!",
"cantplaycard": "You can't play this card like that!",
"choose_baggage_car": "Discard this for a Missed! Panic!, Cat Balou, or an extra BANG!",
"choose_mail_car": "Choose which card to give to another player",
"choose_other_player": "Choose the player to give the card to",
"choose_sleeper_car": "Choose a card to discard for Sleeper Car",
"choose_buy_train": "Discard a card to rob the train, and get the carriage",
"chat": {
"spectators": " | A spectator is watching the game | {n} spectators are watching the game",
"chat": "Chat",
"joined": "👋 ;{0}; joined the lobby",
"died": "☠️ ;{0}; died",
"died_role": "☠️ ;{0}; was a ;{1};!",
"won": "🏆 ;{0}; won! Their role was ;{1};",
"joined": ";{0}; joined the lobby",
"died": ";{0}; died",
"died_role": ";{0}; was a ;{1};!",
"won": ";{0}; won! Their role was ;{1};",
"choose_character": ";{0}; has ;{1}; as character, his special ability is: ;{2};!",
"starting": "The game is starting!",
"sheriff": ";{0}; is the sheriff!",
"sheriff": ";{0}; is the sheriff!",
"did_choose_character": ";{0}; did choose the character.",
"turn": "It is the turn of ;{0};.",
"turn": "It is the turn of ;{0};.",
"draw_from_scrap": ";{0}; did draw the first card from the scrap pile.",
"draw_from_player": ";{0}; did draw the first card from the hand of ;{1};.",
"flipped": ";{0}; flipped a ;{1}; ;{2};.",
"scrapped": ";{0}; discarded a ;{1}; ;{2};.",
"explode": "💥 ;{0}; blew up the dynamite.",
"explode": ";{0}; blew up the dynamite.",
"beer_save": ";{0}; used a beer to save his life.",
"get_nugget": ";{0}; got a gold nugget using a Beer.",
"play_card": ";{0}; played ;{1};.",
"play_card_green": ";{0}; put in game ;{1};.",
"play_card_with": ";{0}; played ;{1};, discarding ;{2};.",
"purchase_card": ";{0}; purchased ;{1};.",
"play_card_against": ";{0}; played ;{1}; against ;{2};.",
"play_card_against_with": ";{0}; played ;{1}; against ;{2};, discarding ;{3};.",
"play_card_for": ";{0}; played ;{1}; for ;{2};.",
"spilled_beer": ";{0}; spilled a ;{1};.",
"diligenza": ";{0}; played ;{1}; and draws 2 cards.",
"wellsfargo": ";{0}; played ;{1}; and draws 3 cards.",
"saloon": "🍻 ;{0}; played ;{1}; and heals 1 HP to everyone alive.",
"saloon": ";{0}; player ;{1}; and heals 1 HP to everyone alive.",
"special_bart_cassidy": ";{0}; received a compensation because he was injured.",
"special_el_gringo": ";{0}; stole a card from ;{1}; when he was was injured.",
"special_calamity": ";{0}; played ;{1}; as Bang! against ;{2};.",
"allroles3": "In the game there are: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};.",
"allroles4": "In the game there are: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};, ;{7}; ;{6};.",
"guess": "🤔 ;{0}; guesses ;{1};.",
"guess": ";{0}; guesses ;{1};.",
"guess_right": ";{0}; was right.",
"guess_wrong": ";{0}; was wrong.",
"fratelli_sangue": ";{0}; gave one of his lives to ;{1};.",
"doctor_heal": ";{0}; was healed by the doctor.",
"respond": "↩️ ;{0}; responded with ;{1};.",
"change_username": "✏️ ;{0}; is now ;{1};.",
"respond": ";{0}; responded with ;{1};.",
"change_username": ";{0}; is now ;{1};.",
"lobby_reset": "Going back to lobby in ;{0}; seconds...",
"prison_free": "🆓 ;{0}; got out of prison",
"prison_turn": "🔐 ;{0}; stayed in prison this turn",
"prison_free": ";{0}; got out of prison",
"prison_turn": ";{0}; stayed in prison this turn",
"flip_event": "🎴 EVENT: ;{0}; 🎴",
"choose_manette": ";{0}; committed to play only cards of suit ;{1}; in this turn.",
"UnionPacific": ";{0}; played Union Pacific and draws 4 cards from the deck",
@ -157,10 +147,8 @@
"gold_rush_pay_discard": ";{0}; discarded ;{2}; from ;{1};.",
"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.",
"in_private_car": "🚋💁🏻 ;{0}; is in a private car and couldn't get Bang!ed by ;{1};",
"bought_train": "🚂 ;{0}; robbed the train at ;{1}; and got the ;{2};"
"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."
@ -542,7 +530,7 @@
},
"Fratelli Di Sangue": {
"name": "Blood Brothers",
"desc": "At the begin of their turn, players can lose 1 hp (except the last one) to give it to another player"
"desc": "At the begin of their turn, payers can lose 1 hp (except the last one) to give it to another player"
},
"I Dalton": {
"name": "The Daltons",
@ -712,7 +700,7 @@
"desc": "During your turn you can use any card as BANG! (Click on the card once equipped)."
},
"SerpenteASonagli": {
"name": "Rattle Snake",
"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": {
@ -828,7 +816,7 @@
"desc": "Before drawing, players with more cards in their hand than Youl must give him a card of their choice."
},
"Camposanto": {
"name": "Bone Orchard",
"name": "Camposanto",
"desc": "At the beginning of their turn, each eliminated player comes back to the game with 1 health point. Draw a role card at random from the eliminated players."
},
"Darling Valentine": {
@ -844,7 +832,7 @@
"desc": "When Helena enters the game, \"draw!\": if Hearts or Diamonds are drawn, shuffle the active role cards except for the Sheriff, and redistribute them at random."
},
"Lady Rosa del Texas": {
"name": "Lady Rose of Texas",
"name": "Lady Rosa del Texas",
"desc": "During their turn, each player can switch places with the player on their right, who skips their next turn. (Click the card)"
},
"Miss Susanna": {
@ -852,7 +840,7 @@
"desc": "During their turn, each player must play at least 3 cards. If they don't, they lose 1 health point."
},
"Regolamento di Conti": {
"name": "Showdown",
"name": "Regolamento di Conti",
"desc": "All cards can be played as if they were BANG!. BANG! cards can be played as if they were Missed! (Click the card)"
},
"Sacagaway": {
@ -862,130 +850,6 @@
"Wild West Show": {
"name": "Wild West Show",
"desc": "The goal for each player becomes: \"Be the last one standing!\""
},
"Boom Town": {
"name": "Boom Town",
"desc": "Discard a Bang! to rob the train"
},
"Caticor": {
"name": "Caticor",
"desc": "Discard a Cat Balou or Panico to rob the train"
},
"Creepy Creek": {
"name": "Creepy Creek",
"desc": "Discard a card of spades to rob the train"
},
"Crowns Hole": {
"name": "Crown's Hole",
"desc": "Discard a beer to rob the train"
},
"Deadwood": {
"name": "Deadwood",
"desc": "Discard an equipment card to rob the train"
},
"Dodgeville": {
"name": "Dodgeville",
"desc": "Discard a Missed! to rob the train"
},
"Fort Worth": {
"name": "Fort Worth",
"desc": "Discard a card with number 10, J, Q, K, A to rob the train"
},
"Frisco": {
"name": "Frisco",
"desc": "Discard a card of clubs to rob the train"
},
"Miners Oath": {
"name": "Miner's Oath",
"desc": "Discard a card of diamonds to rob the train"
},
"San Tafe": {
"name": "San Tafe",
"desc": "Discard a card of hearts to rob the train"
},
"Tombrock": {
"name": "Tombrock",
"desc": "Lose 1 life point to rob the train"
},
"Yooma": {
"name": "Yooma",
"desc": "Discard a card with number between 2 and 9 to rob the train"
},
"Virginia Town": {
"name": "Virginia Town",
"desc": "Discard two cards to rob the train"
},
"Ironhorse": {
"name": "Ironhorse",
"desc": "Each player, including the one who activated the effect, is targeted by a BANG!\nNo player is responsible for any loss of life points.\nIf all players are eliminated at the same time, the Outlaws win."
},
"Leland": {
"name": "Leland",
"desc": "LOCOMOTIVE: perform the effect of the Emporium, starting with the current player and proceeding clockwise."
},
"Baggage Car": {
"name": "Baggage Car",
"desc": "Discard this card to gain the effect of a Missed!, Panic!, Cat Balou, or an extra BANG! card."
},
"Caboose": {
"name": "Caboose",
"desc": "To discard one of your blue-bordered cards, include a car as if it were a Missed!"
},
"Cattle Truck": {
"name": "Cattle Truck",
"desc": "Discard this card to look at the top 3 cards of the discard pile and choose 1 to add to your hand."
},
"Circus Wagon": {
"name": "Circus Wagon",
"desc": "Discard this card. Each other player must discard a card that he has in play."
},
"Coal Hopper": {
"name": "Coal Hopper",
"desc": "Discard this card to draw a card and discard a car in play in front of any player of your choice."
},
"Dining Car": {
"name": "Dining Car",
"desc": "At the beginning of your turn, \"pick!\": if it's Hearts, regain 1 life point."
},
"Express Car": {
"name": "Express Car",
"desc": "Discard all your hand cards and take another turn."
},
"Ghost Car": {
"name": "Ghost Car",
"desc": "Play it on any player except the Sheriff. If you are eliminated, you remain in play, but cannot look at or lose any life points."
},
"Lounge Car": {
"name": "Lounge Car",
"desc": "Discard this card to draw 2 cards from the deck and put 1 of them in play in front of you and 1 in front of another player."
},
"Lumber Flatcar": {
"name": "Lumber Flatcar",
"desc": "Play this card on any player (including yourself). While this card is in play, that player sees all other players at a distance increased by 1."
},
"Mail Car": {
"name": "Mail Car",
"desc": "Discard this card to draw 3 cards and give 1 of them to another player of your choice."
},
"Observation Car": {
"name": "Observation Car",
"desc": "You see other players at a distance reduced by 1. Other players see you at a distance increased by 1."
},
"Passenger Car": {
"name": "Passenger Car",
"desc": "Discard this card to draw a card (from hand or in play) from another player."
},
"Prisoner Car": {
"name": "Prisoner Car",
"desc": "Duel and Indians! cards played by other players have no effect on you."
},
"Private Car": {
"name": "Private Car",
"desc": "If you have no cards in hand, you cannot be targeted by BANG! cards."
},
"Sleeper Car": {
"name": "Sleeper Car",
"desc": "Once per turn, you may discard another of your blue-bordered cards in play."
}
},
"help": {
@ -1037,9 +901,7 @@
"highnooncards": "High Noon - Event Cards",
"foccards": "Fistful of Cards - Event Cards",
"goldrushcards": "Gold Rush Cards",
"valleyofshadowscards": "The Valley of Shadows Cards",
"wildwestshowcards": "Wild West Show Cards",
"trainrobberycards": "Train Robbery Cards"
"valleyofshadowscards": "The Valley of Shadows Cards"
},
"theme": {
"sepia": "Sepia",

View File

@ -1,918 +0,0 @@
{
"trademark": "¡Bang! es una marca registrada propiedad de DVGiochi",
"tip_1": "¡Mejor con amigos!",
"tip_2": "¡El icono 🤖️ significa que un jugador es un bot!",
"tip_3": "¡Puedes hacer clic en las cartas de personajes y equipos de otros jugadores si deseas saber más!",
"tip_4": "¡Incluye Dodge City, High Noon, Fistful Of Cards!",
"tip_5": "¿Encontraste un error? ¡Envíanos un mensaje en Discord!",
"tip_6": "¡Ahora con un servidor de Discord!",
"tip_7": "¡Si quieres ayudarnos a traducir el juego a tu idioma, contáctanos en Discord!",
"tip_8": "¡Si te desconectas durante una partida en curso, serás reemplazado por un bot (mientras estás ausente)!",
"tip_9": "¡Si estás en el móvil, mantén pulsada una carta para leer la descripción!",
"tip_10": "¡En caso de duda, dispara a todos los demás jugadores!",
"online_players": "Jugadores en línea: ",
"shuffle_players": "Mezclar jugadores",
"choose_username": "Elige un nombre de usuario:",
"report_bug": "Escribe en qué consiste el error",
"report": "Reportar un error",
"available_lobbies": "Salas disponibles:",
"spectate_lobbies": "Ver partidas en curso:",
"no_lobby_available": "No hay salas disponibles",
"create_lobby": "Abrir una sala:",
"characters_to_distribute": "Personajes a distribuir: ",
"lobby_name": "Nombre:",
"leave_room": "Salir de la sala",
"warning": "¡Advertencia!",
"connection_error": "No se puede conectar al servidor.",
"end_turn": "¡Terminar turno!",
"start_game": "¡Empezar!",
"expansions": "Expansiones",
"click_to_toggle": "Haz clic para alternar",
"details": "Detalles",
"ok": "OK",
"you": "TÚ",
"owner": "PROPIETARIO",
"cancel": "CANCELAR",
"send": "ENVIAR",
"password": "Contraseña: ",
"room_password_prompt": "Contraseña de la sala: ",
"private_room": "Sala privada",
"room": "Sala: ",
"room_players": "Jugadores (tú eres {username})",
"choose_character": "Elige tu personaje",
"choose_card": "Elige una carta",
"choose_card_from": " de ",
"flip_card": "↙️ Voltear una carta",
"draw_cards": "⏬ Roba tus cartas del mazo",
"play_cards": "▶️ Juega tus cartas",
"respond_card": "↩️ Responder a la carta",
"wait": "⏸ Espera",
"choose_cards": "🔽 Elige una carta",
"take_dmg": "Recibir daño",
"choose_response": "Elige tu respuesta ",
"choose_response_to": "a ",
"choose_response_needed": "NECESARIA ",
"choose_manette": "Elige un palo, solo podrás jugar cartas con ese palo en este turno.",
"hand": "MANO",
"card_against": "¿Contra quién jugarás tu carta?",
"choose_sid_scrap": "Descarta 2 cartas para no morir",
"choose_card_to_get": "Elige una carta",
"choose_guess": "Adivina el color del palo",
"choose_ranch": "Elige las cartas para reemplazar",
"choose_dalton": "Elige qué equipo descartar",
"choose_fratelli_di_sangue": "Elige a quién quieres donar una de tus vidas",
"choose_fantasma": "Elige a quién resucitar",
"choose_sventagliata": "Elige otro objetivo para golpear con Fanning",
"choose_tornado": "Elige una carta para descartar por el tornado",
"choose_bandidos": "Elige entre descartar 2 cartas o perder una vida (1 carta si solo tienes 1)",
"choose_poker": "Todos descartan 1 carta (si no hay As, el jugador que jugó poker elige 2 cartas)",
"choose_from_poker;2": "Elige qué cartas obtener (máx. 2)",
"choose_from_poker;1": "Elige qué carta obtener",
"choose_cecchino": "Elige a quién disparar",
"choose_rimbalzo_player": "Elige el objetivo del rebote",
"choose_rimbalzo_card": "Elige la carta para descartar el rebote",
"choose_nuova_identita": "Elige si quieres mantener tu personaje actual o si quieres cambiarlo y empezar con 2 PV",
"choose_bicchierino": "Un jugador de tu elección recupera 1 PV",
"choose_bottiglia": "¡Elige cómo jugar la Botella!",
"choose_complice": "¡Elige cómo jugar el Cómplice!",
"choose_ricercato": "Elige contra quién jugar Se Busca.",
"choose_birra_function": "Elige entre obtener 1 pepita de oro descartando la cerveza o si quieres jugar la cerveza.",
"choose_play_as_bang": "Elige qué carta jugar como Bang!",
"choose_flint_special": "Elige qué carta intercambiar.",
"emporio_others": "{0} está eligiendo qué carta obtener de la Tienda General",
"you_died": "ESTÁS MUERTO",
"spectate": "ESPECTAR",
"you_win": "HAS GANADO",
"you_lose": "HAS PERDIDO",
"special_ability": "HABILIDAD ESPECIAL",
"gold_rush_discard": "Descarta el equipo de la fiebre del oro de otro jugador (pagando el precio + 1)",
"gold_rush_discard_2": "Descarta el equipo de la fiebre del oro de otro jugador (pagando el precio + 1)",
"discard": "DESCARTAR",
"to_regain_1_hp": "PARA RECUPERAR 1 PV",
"play_your_turn": "JUEGA TU TURNO",
"you_are": "Eres",
"did_pick_as": "eligió esto como segunda carta",
"blackjack_special": "Si la carta es de diamantes o corazones, roba otra carta.",
"choose_scarp_card_to": "ELIGE QUÉ CARTA DESCARTAR PARA USAR",
"pick_a_card": "VOLTEAR UNA CARTA",
"to_defend_from": "PARA DEFENDERTE DE",
"submit": "Enviar",
"copy": "Copiar invitación",
"no_players_in_range": "No puedes ver a los otros jugadores, equipa un arma o un visor!",
"cantplaycard": "¡No puedes jugar esta carta así!",
"chat": {
"spectators": " | Un espectador está viendo la partida | {n} espectadores están viendo la partida",
"chat": "Chat",
"joined": "👋 ;{0}; se unió a la sala",
"died": "☠️ ;{0}; murió",
"died_role": "☠️ ;{0}; era un ;{1};!",
"won": "🏆 ;{0}; ganó! Su rol era ;{1};",
"choose_character": ";{0}; tiene ;{1}; como personaje, su habilidad especial es: ;{2};!",
"starting": "¡El juego está comenzando!",
"sheriff": "⭐ ;{0}; es el sheriff!",
"did_choose_character": ";{0}; eligió el personaje.",
"turn": "⏩ Es el turno de ;{0};.",
"draw_from_scrap": ";{0}; robó la primera carta del montón de descartes.",
"draw_from_player": ";{0}; robó la primera carta de la mano de ;{1};.",
"flipped": ";{0}; volteó un ;{1}; ;{2};.",
"scrapped": ";{0}; descartó un ;{1}; ;{2};.",
"explode": "💥 ;{0}; hizo explotar la dinamita.",
"beer_save": ";{0}; usó una cerveza para salvar su vida.",
"get_nugget": ";{0}; obtuvo una pepita de oro usando una Cerveza.",
"play_card": ";{0}; jugó ;{1};.",
"play_card_green": ";{0}; puso en juego ;{1};.",
"play_card_with": ";{0}; jugó ;{1};, descartando ;{2};.",
"purchase_card": ";{0}; compró ;{1};.",
"play_card_against": ";{0}; jugó ;{1}; contra ;{2};.",
"play_card_against_with": ";{0}; jugó ;{1}; contra ;{2};, descartando ;{3};.",
"play_card_for": ";{0}; jugó ;{1}; para ;{2};.",
"spilled_beer": ";{0}; derramó una ;{1};.",
"diligenza": ";{0}; jugó ;{1}; y roba 2 cartas.",
"wellsfargo": ";{0}; jugó ;{1}; y roba 3 cartas.",
"saloon": "🍻 ;{0}; jugó ;{1}; y cura 1 PV a todos los vivos.",
"special_bart_cassidy": ";{0}; recibió una compensación porque fue herido.",
"special_el_gringo": ";{0}; robó una carta de ;{1}; cuando fue herido.",
"special_calamity": ";{0}; jugó ;{1}; como Bang! contra ;{2};.",
"allroles3": "En el juego hay: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};.",
"allroles4": "En el juego hay: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};, ;{7}; ;{6};.",
"guess": "🤔 ;{0}; adivina ;{1};.",
"guess_right": ";{0}; tenía razón.",
"guess_wrong": ";{0}; estaba equivocado.",
"fratelli_sangue": ";{0}; dio una de sus vidas a ;{1};.",
"doctor_heal": ";{0}; fue curado por el doctor.",
"respond": "↩️ ;{0}; respondió con ;{1};.",
"change_username": "✏️ ;{0}; ahora es ;{1};.",
"lobby_reset": "Volviendo a la sala en ;{0}; segundos...",
"prison_free": "🆓 ;{0}; salió de la prisión",
"prison_turn": "🔐 ;{0}; se quedó en prisión este turno",
"flip_event": "🎴 EVENTO: ;{0}; 🎴",
"choose_manette": ";{0}; se comprometió a jugar solo cartas del palo ;{1}; en este turno.",
"UnionPacific": ";{0}; jugó Union Pacific y roba 4 cartas del mazo",
"use_special": ";{0}; usó la habilidad especial de su personaje (;{1};)",
"gold_rush_pay_discard": ";{0}; descartó ;{2}; de ;{1};.",
"choose_emporio": ";{0}; eligió ;{1}; de la Tienda General.",
"shotgun_scrap": "Cuando la escopeta golpeó a ;{0};, una carta voló de su mano (;{1};)",
"taglia_reward": "💰 ;{1}; obtuvo una carta de la recompensa por ;{0};",
"snake_bit": "🐍 ;{0}; fue mordido por la serpiente de cascabel."
},
"foc": {
"leggedelwest": "Debe jugar esta carta en este turno si es posible."
},
"mods": "Modificadores",
"bots": "Bots",
"add_bot": "Añadir un bot",
"remove_bot": "Eliminar un bot",
"minimum_players": "El juego necesita al menos 3 jugadores para empezar",
"mod_comp": "Modo competitivo (desactiva recibir daño automáticamente)",
"disconnect_bot": "Reemplazar jugadores que se desconecten por bots",
"your_turn": "¡Juega tu turno!",
"your_response": "¡Responde!",
"your_choose": "¡Elige una carta!",
"cards": {
"Barile": {
"name": "Barril",
"desc": "Cuando alguien juega un Bang contra ti. Puedes voltear la primera carta del mazo, si el palo es corazones, cuenta como una carta Fallaste"
},
"Dinamite": {
"name": "Dinamita",
"desc": "Al jugar la dinamita, colócala frente a ti, permanecerá inofensiva durante toda una ronda. Al comienzo del siguiente turno antes de robar y antes de cualquier volteo de carta (ej. Prisión), voltea una carta de la parte superior del mazo. Si una carta está entre 2 y 9 de picas (inclusive), la dinamita explota: pierdes 3 vidas y descartas la carta, de lo contrario, pasa la dinamita al siguiente jugador, quien robará a su vez después de haber terminado su turno"
},
"Mirino": {
"name": "Mira",
"desc": "Ves a los otros jugadores a una distancia de -1"
},
"Mustang": {
"name": "Mustang",
"desc": "Los otros jugadores te ven a una distancia de +1"
},
"Prigione": {
"name": "Prisión",
"desc": "Equipa esta carta a otro jugador, excepto el Sheriff. El jugador elegido al comienzo de su turno, debe voltear una carta antes de robar: si es corazones, descarta esta carta y juega el turno normalmente, de lo contrario, descarta esta carta y salta el turno"
},
"Remington": {
"name": "Remington",
"desc": "Puedes disparar a otro jugador a una distancia de 3 o menos"
},
"Rev Carabine": {
"name": "Rev. Carabina",
"desc": "Puedes disparar a otro jugador a una distancia de 4 o menos"
},
"Schofield": {
"name": "Schofield",
"desc": "Puedes disparar a otro jugador a una distancia de 2 o menos"
},
"Volcanic": {
"name": "Volcánica",
"desc": "Puedes disparar a otro jugador a una distancia de 1 o menos, sin embargo, ya no tienes el límite de 1 Bang"
},
"Winchester": {
"name": "Winchester",
"desc": "Puedes disparar a otro jugador a una distancia de 5 o menos"
},
"Bang!": {
"name": "Bang!",
"desc": "Dispara a un jugador a la vista. Si no tienes armas, tu vista es de 1"
},
"Birra": {
"name": "Cerveza",
"desc": "Juega esta carta para recuperar un punto de vida. No puedes curarte más allá del límite máximo de tu personaje. Si estás a punto de perder tu último punto de vida, también puedes jugar esta carta en el turno de tu oponente. La cerveza ya no tiene efecto si solo quedan dos jugadores"
},
"Cat Balou": {
"name": "Cat Balou",
"desc": "Elige y descarta una carta de otro jugador."
},
"Diligenza": {
"name": "Diligencia",
"desc": "Roba 2 cartas del mazo."
},
"Duello": {
"name": "Duelo",
"desc": "Juega esta carta contra cualquier jugador. Por turnos, comenzando con tu oponente, puedes descartar una carta Bang!, el primer jugador que no lo haga pierde 1 vida."
},
"Emporio": {
"name": "Tienda General",
"desc": "Pon sobre la mesa N cartas del mazo, donde N es el número de jugadores vivos, por turnos, comenzando contigo, elige una carta y agrégala a tu mano"
},
"Gatling": {
"name": "Gatling",
"desc": "Dispara a todos los otros jugadores"
},
"Indiani!": {
"name": "¡Indios!",
"desc": "Todos los demás jugadores deben descartar una carta Bang! o perder 1 punto de vida"
},
"Mancato!": {
"name": "¡Fallaste!",
"desc": "Usa esta carta para cancelar el efecto de un Bang"
},
"Panico!": {
"name": "¡Pánico!",
"desc": "Roba una carta a un jugador a una distancia de 1"
},
"Saloon": {
"name": "Saloon",
"desc": "Todos recuperan 1 punto de vida"
},
"WellsFargo": {
"name": "Wells Fargo",
"desc": "Roba 3 cartas del mazo"
},
"Binocolo": {
"name": "Binoculares",
"desc": "Ves a los otros jugadores a una distancia de -1"
},
"Riparo": {
"name": "Escondite",
"desc": "Los otros jugadores te ven a una distancia de +1"
},
"Pugno!": {
"name": "¡Puñetazo!",
"desc": "Dispara a un jugador a una distancia de 1"
},
"Rag Time": {
"name": "Rag Time",
"desc": "Roba una carta de otro jugador a cualquier distancia"
},
"Rissa": {
"name": "Pelea",
"desc": "Elige una carta para descartar de la mano/equipo de todos los demás jugadores"
},
"Schivata": {
"name": "Esquiva",
"desc": "Usa esta carta para cancelar el efecto de un Bang y luego roba una carta."
},
"Springfield": {
"name": "Springfield",
"desc": "Dispara a un jugador a cualquier distancia"
},
"Tequila": {
"name": "Tequila",
"desc": "Cura 1 PV a un jugador de tu elección (puede ser tú)"
},
"Whisky": {
"name": "Whisky",
"desc": "Cura 2 PV"
},
"Bibbia": {
"name": "Biblia",
"desc": "Usa esta carta para cancelar el efecto de un Bang y luego roba una carta."
},
"Cappello": {
"name": "Sombrero de Diez Galones",
"desc": "Usa esta carta para cancelar el efecto de un Bang"
},
"Placca Di Ferro": {
"name": "Placa de Hierro",
"desc": "Usa esta carta para cancelar el efecto de un Bang"
},
"Sombrero": {
"name": "Sombrero",
"desc": "Usa esta carta para cancelar el efecto de un Bang"
},
"Pugnale": {
"name": "Cuchillo",
"desc": "Dispara a un jugador a una distancia de 1"
},
"Derringer": {
"name": "Derringer",
"desc": "Dispara a un jugador a una distancia de 1 y luego roba una carta."
},
"Borraccia": {
"name": "Cantimplora",
"desc": "Recupera 1 PV"
},
"Can Can": {
"name": "Can Can",
"desc": "Elige y descarta una carta de otro jugador."
},
"Conestoga": {
"name": "Conestoga",
"desc": "Roba una carta de otro jugador a cualquier distancia"
},
"Fucile Da Caccia": {
"name": "Rifle de Búfalo",
"desc": "Dispara a un jugador a cualquier distancia"
},
"Pony Express": {
"name": "Pony Express",
"desc": "Roba 3 cartas del mazo"
},
"Pepperbox": {
"name": "Pepperbox",
"desc": "Dispara a un jugador a la vista. Si no tienes armas, tu vista es de 1"
},
"Howitzer": {
"name": "Howitzer",
"desc": "Dispara a todos los otros jugadores"
},
"Bart Cassidy": {
"name": "Bart Cassidy",
"desc": "Cada vez que es herido, roba una carta."
},
"Black Jack": {
"name": "Black Jack",
"desc": "Al comienzo de su turno, cuando tiene que robar, muestra a todos la segunda carta, si es corazones o diamantes, roba una tercera carta sin mostrarla."
},
"Calamity Janet": {
"name": "Calamity Janet",
"desc": "Puede usar las cartas Fallaste! como Bang! y viceversa."
},
"El Gringo": {
"name": "El Gringo",
"desc": "Cada vez que es herido, roba una carta de la mano del jugador atacante."
},
"Jesse Jones": {
"name": "Jesse Jones",
"desc": "Cuando tiene que robar sus cartas, puede robar la primera carta de la mano de otro jugador. (haz clic en las cartas del jugador enemigo si deseas usar la habilidad)"
},
"Jourdonnais": {
"name": "Jourdonnais",
"desc": "Juega como si tuviera un Barril siempre activo, si equipa otro Barril, puede voltear 2 cartas."
},
"Kit Carlson": {
"name": "Kit Carlson",
"desc": "Cuando tiene que robar, mira 3 cartas y elige 2, colocando la otra carta en la parte superior del mazo."
},
"Lucky Duke": {
"name": "Lucky Duke",
"desc": "Cada vez que tiene que voltear una carta, puede voltear 2 veces."
},
"Paul Regret": {
"name": "Paul Regret",
"desc": "Los otros jugadores lo ven a una distancia de +1."
},
"Pedro Ramirez": {
"name": "Pedro Ramirez",
"desc": "Cuando tiene que robar, puede tomar la primera carta del montón de descartes. (haz clic en las cartas descartadas para usar la habilidad)"
},
"Rose Doolan": {
"name": "Rose Doolan",
"desc": "Ve a los otros jugadores a una distancia de -1."
},
"Sid Ketchum": {
"name": "Sid Ketchum",
"desc": "Puede descartar 2 cartas para recuperar 1 PV."
},
"Slab The Killer": {
"name": "Slab The Killer",
"desc": "Para esquivar sus cartas Bang!, los otros jugadores necesitan 2 cartas Fallaste!"
},
"Suzy Lafayette": {
"name": "Suzy Lafayette",
"desc": "Siempre que tiene la mano vacía, roba una carta."
},
"Vulture Sam": {
"name": "Vulture Sam",
"desc": "Cuando un jugador muere, obtiene todas las cartas en la mano y los equipos del muerto."
},
"Willy The Kid": {
"name": "Willy The Kid",
"desc": "No tiene límites en la cantidad de Bang que puede usar."
},
"Pixie Pete": {
"name": "Pixie Pete",
"desc": "Roba 3 cartas en lugar de 2."
},
"Tequila Joe": {
"name": "Tequila Joe",
"desc": "Cuando juega una Cerveza, recupera 2 PV."
},
"Greg Digger": {
"name": "Greg Digger",
"desc": "Siempre que un jugador muere, recupera hasta 2 vidas."
},
"Herb Hunter": {
"name": "Herb Hunter",
"desc": "Siempre que un jugador muere, roba 2 cartas."
},
"Elena Fuente": {
"name": "Elena Fuente",
"desc": "Puede usar cualquier carta de su mano como Fallaste."
},
"Bill Noface": {
"name": "Bill Noface",
"desc": "Roba 1 carta + 1 carta por cada herida que tiene."
},
"Molly Stark": {
"name": "Molly Stark",
"desc": "Cuando usa una carta de su mano fuera de su turno, roba una carta."
},
"Apache Kid": {
"name": "Apache Kid",
"desc": "Las cartas de diamantes ♦️ jugadas contra él no tienen efecto (no funciona en duelos)."
},
"Sean Mallory": {
"name": "Sean Mallory",
"desc": "Puede mantener hasta 10 cartas en su mano al terminar el turno."
},
"Belle Star": {
"name": "Belle Star",
"desc": "Durante su turno, las cartas verdes y azules de los otros jugadores no funcionan."
},
"Vera Custer": {
"name": "Vera Custer",
"desc": "Antes de robar, puede elegir la habilidad especial de otro jugador vivo. Esta habilidad se usa hasta el próximo turno."
},
"Chuck Wengam": {
"name": "Chuck Wengam",
"desc": "En su turno, puede decidir perder 1 PV para robar 2 cartas del mazo."
},
"Pat Brennan": {
"name": "Pat Brennan",
"desc": "En lugar de robar, puede robar una carta del equipo de otro jugador. (haz clic en el jugador enemigo si deseas usar la habilidad)"
},
"José Delgado": {
"name": "José Delgado",
"desc": "En su turno, puede descartar una carta azul para robar 2 cartas, hasta dos veces por turno."
},
"Doc Holyday": {
"name": "Doc Holyday",
"desc": "Puede descartar 2 cartas para jugar un Bang."
},
"Fuorilegge": {
"name": "Forajido"
},
"Rinnegato": {
"name": "Renegado"
},
"Sceriffo": {
"name": "Sheriff"
},
"Vice": {
"name": "Ayudante"
},
"Miniera Abbandonata": {
"name": "Mina Abandonada",
"desc": "Los jugadores roban del montón de descartes en su fase 1 y descartan en la parte superior del mazo durante la fase 3 (si el montón de descartes se agota, deben robar y descartar en la parte superior del mazo)"
},
"Il Giudice": {
"name": "El Juez",
"desc": "No puedes equipar cartas a ti mismo ni a otros jugadores"
},
"Agguato": {
"name": "Emboscada",
"desc": "La distancia base entre dos jugadores es de 1"
},
"Rimbalzo": {
"name": "Rebote",
"desc": "El jugador puede jugar Bang contra las cartas equipadas por los otros jugadores, si no juegan Fallaste, se descartan (haz clic en la carta de evento)"
},
"Cecchino": {
"name": "Francotirador",
"desc": "Durante su turno, los jugadores pueden descartar 2 Bang! para disparar un Bang! que requiere 2 Fallaste (haz clic en la carta)"
},
"Lazo": {
"name": "Lazo",
"desc": "Las cartas en la ranura de equipo no funcionan"
},
"Ranch": {
"name": "Rancho",
"desc": "Después de robar, el jugador puede descartar tantas cartas como quiera de su mano y robar tantas del mazo"
},
"Dead Man": {
"name": "Hombre Muerto",
"desc": "El primer jugador que muera vuelve a la vida con 2 PV y 2 cartas"
},
"Liquore Forte": {
"name": "Licor Fuerte",
"desc": "Los jugadores pueden saltar el robo para recuperar 1 PV (haz clic en la carta de evento para usar)"
},
"Vendetta": {
"name": "Venganza",
"desc": "Al terminar el turno, el jugador voltea una carta del mazo, si es un ❤️ juega otro turno (pero no voltea otra carta)"
},
"Roulette Russa": {
"name": "Ruleta Rusa",
"desc": "Comenzando por el sheriff, cada jugador descarta 1 Fallaste, el primero que no lo haga pierde 2 PV"
},
"Legge Del West": {
"name": "Ley del Oeste",
"desc": "Cada jugador muestra la segunda carta que roba y debe usarla en esa ronda (si es posible)"
},
"Peyote": {
"name": "Peyote",
"desc": "En lugar de robar, el jugador intenta adivinar el color del palo, si acierta, agrega la carta a su mano y continúa intentando adivinar la siguiente carta"
},
"Fratelli Di Sangue": {
"name": "Hermanos de Sangre",
"desc": "Al comienzo de su turno, los jugadores pueden perder 1 PV (excepto el último) para dárselo a otro jugador"
},
"I Dalton": {
"name": "Los Dalton",
"desc": "Los jugadores que tengan cartas azules equipadas, descartan una de esas cartas a su elección"
},
"Sermone": {
"name": "El Sermón",
"desc": "Los jugadores no pueden jugar Bang! durante su turno"
},
"Città Fantasma": {
"name": "Ciudad Fantasma",
"desc": "Todos los jugadores muertos vuelven a la vida en su turno, no pueden morir y roban 3 cartas en lugar de 2. Cuando terminan su turno, mueren."
},
"Il Reverendo": {
"name": "El Reverendo",
"desc": "Las cervezas no se pueden jugar"
},
"Sbornia": {
"name": "Resaca",
"desc": "Los personajes pierden sus habilidades especiales"
},
"Il Dottore": {
"name": "El Doctor",
"desc": "El jugador con la menor cantidad de PV recupera 1 PV"
},
"Corsa All Oro": {
"name": "Fiebre del Oro",
"desc": "Los turnos se juegan en sentido antihorario"
},
"Maledizione": {
"name": "Maldición",
"desc": "Todas las cartas son de picas ♠️"
},
"Sparatoria": {
"name": "Tiroteo",
"desc": "El límite de Bang! del turno es de 2"
},
"Benedizione": {
"name": "Bendición",
"desc": "Todas las cartas son de corazones ❤️"
},
"Il Treno": {
"name": "Llegada del Tren",
"desc": "Los jugadores roban 1 carta extra"
},
"Sete": {
"name": "Sed",
"desc": "Los jugadores solo roban 1 carta al comienzo de su turno"
},
"Nuova Identita": {
"name": "Nueva Identidad",
"desc": "Al comienzo de su turno, cada jugador puede elegir cambiar su personaje con el otro mostrado al comienzo del juego. Si lo hace, comienza con 2 PV"
},
"Manette": {
"name": "Esposas",
"desc": "Después de robar en la fase 1, el jugador declara un palo. Solo podrá usar cartas de ese palo durante ese turno"
},
"Mezzogiorno di Fuoco": {
"name": "Mediodía de Fuego",
"desc": "Cada jugador pierde 1 PV al comenzar su turno"
},
"Per Un Pugno Di Carte": {
"name": "Por un Puñado de Cartas",
"desc": "Al comienzo de su turno, el jugador es objetivo de tantos Bang como cartas tenga en su mano"
},
"Pepita": {
"name": "Pepita de Oro"
},
"Bicchierino": {
"name": "Chupito",
"desc": "Puedes elegir a un jugador que recupere 1 PV (incluso tú)"
},
"Bottiglia": {
"name": "Botella",
"desc": "Se puede jugar como Pánico!, Cerveza o Bang!"
},
"Complice": {
"name": "Cómplice",
"desc": "Se puede jugar como Tienda General, Duelo o Cat Balou."
},
"Corsa All Oro_gr": {
"name": "Fiebre del Oro",
"desc": "Terminas tu turno, recuperas todos tus PV y comienzas un nuevo turno."
},
"Rum": {
"name": "Ron",
"desc": "Voltea 4 cartas, por cada palo diferente, ganas 1 PV."
},
"Union Pacific": {
"name": "Union Pacific",
"desc": "Roba 4 cartas del mazo."
},
"Calumet": {
"name": "Calumet",
"desc": "Las cartas de diamantes jugadas por otros jugadores no tienen efecto sobre ti."
},
"Cinturone": {
"name": "Cinturón",
"desc": "Puedes mantener hasta 8 cartas cuando terminas tu turno."
},
"Ferro di Cavallo": {
"name": "Herradura",
"desc": "Cada vez que tienes que voltear una carta, ¡volteas dos veces!"
},
"Piccone": {
"name": "Pico",
"desc": "Obtienes 1 carta más cuando robas al comienzo del turno."
},
"Ricercato": {
"name": "Se Busca",
"desc": "Juega sobre otro jugador, quien mate al objetivo obtiene 2 cartas extra y 1 pepita de oro."
},
"Setaccio": {
"name": "Cazador de Oro",
"desc": "Paga 1 pepita de oro para robar 1 carta del mazo, hasta dos veces por turno. (Haz clic en la carta equipada para usar)"
},
"Stivali": {
"name": "Botas",
"desc": "Cada vez que pierdes 1 PV, robas 1 carta del mazo."
},
"Talismano": {
"name": "Amuleto de la Suerte",
"desc": "Cada vez que pierdes 1 PV, obtienes 1 pepita de oro."
},
"Zaino": {
"name": "Mochila",
"desc": "Paga 2 pepitas de oro para curar 1 PV. (Haz clic en la carta equipada para usar)"
},
"Don Bell": {
"name": "Don Bell",
"desc": "Cuando termina su turno, voltea una carta, si es Corazones ❤️ o Diamantes ♦️, juega nuevamente."
},
"Dutch Will": {
"name": "Dutch Will",
"desc": "Roba 2 cartas, descarta 1 y toma 1 pepita de oro."
},
"Jacky Murieta": {
"name": "Jacky Murieta",
"desc": "Durante su turno puede pagar 2 pepitas de oro para tener la capacidad de disparar otro Bang!"
},
"Josh McCloud": {
"name": "Josh McCloud",
"desc": "Puede pagar 2 pepitas de oro para obtener la primera carta del mazo de fiebre del oro."
},
"Madam Yto": {
"name": "Madam Yto",
"desc": "Roba 1 carta del mazo cada vez que se juega una cerveza."
},
"Pretty Luzena": {
"name": "Pretty Luzena",
"desc": "Una vez por turno, puede tener un descuento de 1 en la tienda de cartas de fiebre del oro."
},
"Raddie Snake": {
"name": "Raddie Snake",
"desc": "Puede descartar 1 pepita de oro para robar 1 carta del mazo durante su turno (máximo 2 veces por turno)."
},
"Simeon Picos": {
"name": "Simeon Picos",
"desc": "Obtiene 1 pepita de oro cada vez que pierde 1 PV."
},
"Fantasma": {
"name": "Fantasma",
"desc": "Juega sobre cualquier jugador eliminado: ese jugador vuelve al juego, pero no puede ganar ni perder puntos de vida."
},
"Lemat": {
"name": "Lemat",
"desc": "Durante tu turno puedes usar cualquier carta como Bang! (Haz clic en la carta una vez equipada)."
},
"SerpenteASonagli": {
"name": "Serpiente de Cascabel",
"desc": "Juega sobre cualquier jugador. Al comienzo de su turno, si ese jugador roba en Picas, pierde 1 punto de vida."
},
"Shotgun": {
"name": "Escopeta",
"desc": "Cada vez que hieres a un jugador, debe descartar una carta de su mano."
},
"Taglia": {
"name": "Recompensa",
"desc": "Juega sobre cualquier jugador. Si ese jugador es golpeado por Bang!, la persona que disparó obtiene una carta del mazo."
},
"Mira": {
"name": "Apuntar",
"desc": "Juega esta carta junto con una carta Bang!. Si el objetivo es golpeado, pierde 2 puntos de vida."
},
"RitornoDiFiamma": {
"name": "Retroceso",
"desc": "Cuenta como una carta Fallaste!. El jugador que disparó es el objetivo de un Bang!."
},
"Bandidos": {
"name": "Bandidos",
"desc": "Cada jugador elige entre descartar 2 cartas de su mano (o 1 si solo tiene 1) o perder 1 punto de vida."
},
"Fuga": {
"name": "Fuga",
"desc": "Puede jugarse fuera de turno. Evita el efecto de una carta marrón (no Bang!) de la que eres objetivo."
},
"Sventagliata": {
"name": "Acribillar",
"desc": "Cuenta como el único Bang! del turno. Un jugador de tu elección a una distancia de 1 del objetivo (si lo hay, excluyéndote a ti mismo) también es objetivo de un Bang."
},
"UltimoGiro": {
"name": "Última Ronda",
"desc": "Recupera 1 punto de vida"
},
"Poker": {
"name": "Póker",
"desc": "Todos los demás jugadores descartan una carta de su mano, al mismo tiempo. Si no hay Ases, roba hasta 2 de esas cartas."
},
"Salvo": {
"name": "¡Salvado!",
"desc": "Puede jugarse fuera de turno. Evita que otro jugador pierda 1 punto de vida. Si sobrevive, roba 2 cartas de su mano o del mazo (a tu elección)."
},
"Tomahawk": {
"name": "Tomahawk",
"desc": "Ataca a un jugador hasta una distancia de 2."
},
"Tornado": {
"name": "Tornado",
"desc": "Todos descartan una carta de su mano (si es posible), luego roban 2 cartas del mazo."
},
"Black Flower": {
"name": "Flor Negra",
"desc": "Una vez por turno, puedes usar una carta de Picas para disparar un Bang! extra"
},
"Colorado Bill": {
"name": "Colorado Bill",
"desc": "Cada vez que juegas una carta Bang!, si robas una Picas, el disparo no puede ser evitado."
},
"Der Spot Burst Ringer": {
"name": "Der Spot Burst Ringer",
"desc": "Una vez por turno, puedes usar una carta Bang! como una Gatling."
},
"Evelyn Shebang": {
"name": "Evelyn Shebang",
"desc": "Puedes elegir no robar cartas durante tu fase de robo. Por cada carta no robada, disparas un Bang! a un objetivo alcanzable, a un objetivo diferente."
},
"Henry Block": {
"name": "Henry Block",
"desc": "Cualquiera que robe o descarte una de tus cartas en juego o en mano es objetivo de un Bang!."
},
"Lemonade Jim": {
"name": "Lemonade Jim",
"desc": "Cada vez que otro jugador juega una Cerveza, puedes descartar una carta de tu mano para recuperar también 1 punto de vida."
},
"Mick Defender": {
"name": "Mick Defender",
"desc": "Si eres objetivo de una carta marrón (no Bang!), puedes usar una carta Fallaste! para evitar 1 efecto."
},
"Tuco Franziskaner": {
"name": "Tuco Franziskaner",
"desc": "Si no tienes cartas azules equipadas, robas 2 cartas adicionales."
},
"Big Spencer": {
"name": "Big Spencer",
"desc": "Comienza con 5 cartas. No puede jugar Fallaste!"
},
"Flint Westwood": {
"name": "Flint Westwood",
"desc": "Puede intercambiar una carta de su mano con 2 cartas aleatorias de la mano de otro jugador durante su turno."
},
"Gary Looter": {
"name": "Gary Looter",
"desc": "Roba todas las cartas sobrantes descartadas por otros jugadores al final de cada turno."
},
"Greygory Deckard": {
"name": "Greygory Deckard",
"desc": "Al comienzo de su turno, puede robar 2 personajes aleatorios y gana todas sus habilidades."
},
"John Pain": {
"name": "John Pain",
"desc": "Si tiene menos de 6 cartas en su mano, cuando un jugador \"¡roba!\", John agrega la carta robada a su mano."
},
"Lee Van Kliff": {
"name": "Lee Van Kliff",
"desc": "Durante su turno, puede descartar una carta Bang! para repetir el efecto de una carta marrón que acaba de jugar."
},
"Teren Kill": {
"name": "Teren Kill",
"desc": "Cada vez que va a ser eliminado, \"¡roba!\": si no es una carta de Picas, Teren se queda en 1 punto de vida y roba 1 carta."
},
"Youl Grinner": {
"name": "Youl Grinner",
"desc": "Antes de robar, los jugadores con más cartas en su mano que Youl deben darle una carta de su elección."
},
"Camposanto": {
"name": "Cementerio",
"desc": "Al comienzo de su turno, cada jugador eliminado vuelve al juego con 1 punto de vida. Roba una carta de rol al azar de los jugadores eliminados."
},
"Darling Valentine": {
"name": "Darling Valentine",
"desc": "Al comienzo de su turno, cada jugador descarta su mano y roba el mismo número de cartas del mazo."
},
"Dorothy Rage": {
"name": "Dorothy Rage",
"desc": "Durante su turno, cada jugador puede forzar a otro jugador a jugar una carta."
},
"Helena Zontero": {
"name": "Helena Zontero",
"desc": "Cuando Helena entra en el juego, \"¡roba!\": si se roba un Corazón o un Diamante, baraja las cartas de rol activas excepto el Sheriff y redistribúyelas al azar."
},
"Lady Rosa del Texas": {
"name": "Lady Rose of Texas",
"desc": "Durante su turno, cada jugador puede cambiar de lugar con el jugador a su derecha, quien se salta su próximo turno. (Haz clic en la carta)"
},
"Miss Susanna": {
"name": "Miss Susanna",
"desc": "Durante su turno, cada jugador debe jugar al menos 3 cartas. Si no lo hacen, pierden 1 punto de vida."
},
"Regolamento di Conti": {
"name": "Rendición de Cuentas",
"desc": "Todas las cartas pueden jugarse como si fueran Bang!. Las cartas Bang! pueden jugarse como si fueran Fallaste! (Haz clic en la carta)"
},
"Sacagaway": {
"name": "Sacagaway",
"desc": "Todos los jugadores juegan con sus cartas boca arriba (excepto su carta de rol!)."
},
"Wild West Show": {
"name": "Wild West Show",
"desc": "El objetivo de cada jugador se convierte en: \"¡Sé el último en pie!\""
}
},
"help": {
"character": "Personajes",
"characters_special": "Cada personaje tiene habilidades especiales y una cantidad de vidas que los hacen únicos. \nLas vidas son el número de puntos de vida que puedes perder antes de morir e indican también el número máximo de cartas que puedes mantener en tu mano.",
"deathnobeer": "Cuando pierdes tu último punto de vida y no tienes una cerveza 🍺️ en tu mano, mueres. \nTus cartas se descartan y tu rol se revela a todos.",
"discard": "Descartar",
"distance": "Distancia",
"distancecalc": "La distancia se calcula automáticamente por el juego y corresponde a la distancia mínima entre los jugadores a la izquierda y a la derecha.",
"drawinstructions": "Para robar cartas, debes hacer clic en el mazo cuando veas esta animación.",
"drawthecards": "Roba las cartas",
"endgame": "Fin del juego",
"endgameconditions": "El juego termina cuando se cumple una de las siguientes condiciones:",
"endgamesheriffwin": "Todos los forajidos 🐺️ y renegados 🦅️ están muertos. \nEn este caso, el sheriff ⭐️ y los ayudantes 🎖️ ganan.",
"endgameshriffdeath": "El sheriff ⭐️ muere. \nSi el renegado 🦅️ es el último jugador en pie, gana, de lo contrario, los forajidos ganan.",
"endingturn": "Cuando hayas terminado de jugar tus cartas, es decir, cuando no quieras o no puedas jugar más cartas, debes descartar las cartas que excedan tu número actual de vidas.\n\nLuego pasas el turno al siguiente jugador haciendo clic en terminar turno.",
"equipment": "EQUIPO",
"justoneweapon": "Solo puedes tener 1 arma equipada.",
"maxtwocardsequip": "No puedes tener 2 cartas con el mismo nombre equipadas.",
"outlawreward": "Cualquiera que mate a un forajido 🐺️ roba 3 cartas del mazo (otros forajidos también 🐺️).",
"playerdeath": "La muerte de un jugador",
"playingcards": "Jugar las cartas",
"playingdmg": "Puedes jugar tus cartas para ti mismo o para dañar a otros jugadores intentando eliminarlos.",
"playingduringturn": "Solo puedes jugar las cartas en tu turno. Para jugar cartas, haz clic en las cartas de tu mano.\nCon la excepción de cartas usadas como respuesta, como Fallaste 😅️.",
"playingifyouwant": "No estás obligado a jugar cartas.",
"playlimit": "Solo hay 3 limitaciones:",
"playonlyonebang": "Solo puedes jugar 1 Bang! \npor turno (se refiere solo a cartas llamadas Bang!)",
"rewardspen": "Penalidades y recompensas",
"roles": "Roles",
"sheriffkillsvice": "Si el sheriff ⭐️ mata a un ayudante, pierde todas las cartas en su mano y en juego frente a él.",
"thecards": "Cartas",
"title": "Cómo jugar",
"turndiscard": "Descarta las cartas sobrantes",
"turndraw": "Roba 2 cartas",
"turnplay": "Juega cualquier cantidad de cartas",
"turns": "Turnos",
"turnstart": "Siempre comienza con el Sheriff ⭐️, y el juego continúa en el sentido de las agujas del reloj, los turnos se dividen en 3 fases.",
"weapon": "ARMA",
"renegade": "Renegado",
"vice": "Ayudante",
"outlaw": "Forajido",
"sheriff": "Sheriff",
"allcharacters": "Todos los personajes",
"gotoallcharacters": "Ir a todos los personajes",
"gotocards": "Ir a las cartas",
"gotohighnoon": "Ir al Mediodía",
"gotofoc": "Ir a Un Puñado de Cartas",
"gotogoldrush": "Ir a Fiebre del Oro",
"highnooncards": "Mediodía - Cartas de Evento",
"foccards": "Un Puñado de Cartas - Cartas de Evento",
"goldrushcards": "Cartas de Fiebre del Oro",
"valleyofshadowscards": "Cartas del Valle de las Sombras"
},
"theme": {
"sepia": "Sepia",
"light": "Claro",
"dark": "Oscuro",
"grayscale": "Escala de Grises",
"black": "Negro"
}
}

View File

@ -1,918 +0,0 @@
{
"trademark": "Bang! est une marque déposée détenue par DVGiochi",
"tip_1": "C'est mieux avec des amis !",
"tip_2": "L'icône 🤖️ signifie qu'un joueur est un bot !",
"tip_3": "Vous pouvez cliquer sur les cartes des personnages et des équipements des autres joueurs si vous souhaitez en savoir plus !",
"tip_4": "Dodge City, High Noon, Fistful Of Cards inclus !",
"tip_5": "Trouvé un bug ? Envoyez-nous un message sur Discord !",
"tip_6": "Maintenant avec un serveur Discord !",
"tip_7": "Si vous voulez nous aider à traduire le jeu dans votre langue, contactez-nous sur Discord !",
"tip_8": "Si vous vous déconnectez pendant une partie en cours, vous serez remplacé par un bot (pendant votre absence) !",
"tip_9": "Si vous êtes sur mobile, maintenez une carte pour lire la description !",
"tip_10": "En cas de doute, tirez sur tous les autres joueurs !",
"online_players": "Joueurs en ligne : ",
"shuffle_players": "Mélanger les joueurs",
"choose_username": "Choisissez un nom d'utilisateur :",
"report_bug": "Décrivez le bug",
"report": "Signaler un bug",
"available_lobbies": "Salles disponibles :",
"spectate_lobbies": "Observer les parties en cours :",
"no_lobby_available": "Aucune salle disponible",
"create_lobby": "Ouvrir une salle :",
"characters_to_distribute": "Personnages à distribuer : ",
"lobby_name": "Nom :",
"leave_room": "Quitter la salle",
"warning": "Attention !",
"connection_error": "Impossible de se connecter au serveur.",
"end_turn": "Terminer le tour !",
"start_game": "Commencer !",
"expansions": "Extensions",
"click_to_toggle": "Cliquez pour basculer",
"details": "Détails",
"ok": "OK",
"you": "VOUS",
"owner": "PROPRIÉTAIRE",
"cancel": "ANNULER",
"send": "ENVOYER",
"password": "Mot de passe : ",
"room_password_prompt": "Mot de passe de la salle : ",
"private_room": "Salle privée",
"room": "Salle : ",
"room_players": "Joueurs (vous êtes {username})",
"choose_character": "Choisissez votre personnage",
"choose_card": "Choisissez une carte",
"choose_card_from": " de ",
"flip_card": "↙️ Retourner une carte",
"draw_cards": "⏬ Piochez vos cartes du paquet",
"play_cards": "▶️ Jouez vos cartes",
"respond_card": "↩️ Répondez à la carte",
"wait": "⏸ Attendre",
"choose_cards": "🔽 Choisissez une carte",
"take_dmg": "Subir des dégâts",
"choose_response": "Choisissez votre réponse ",
"choose_response_to": "à ",
"choose_response_needed": "NÉCESSAIRE ",
"choose_manette": "Choisissez une couleur, vous ne pourrez jouer que des cartes de cette couleur ce tour.",
"hand": "MAIN",
"card_against": "Contre qui jouez-vous votre carte ?",
"choose_sid_scrap": "Défaussez 2 cartes pour ne pas mourir",
"choose_card_to_get": "Choisissez une carte",
"choose_guess": "Devinez la couleur de la carte",
"choose_ranch": "Choisissez les cartes à remplacer",
"choose_dalton": "Choisissez quel équipement défausser",
"choose_fratelli_di_sangue": "Choisissez à qui donner une de vos vies",
"choose_fantasma": "Choisissez qui ramener à la vie",
"choose_sventagliata": "Choisissez une autre cible pour le tir",
"choose_tornado": "Choisissez une carte à défausser pour le tornade",
"choose_bandidos": "Choisissez entre défausser 2 cartes ou perdre une vie (1 carte si vous n'avez qu'une)",
"choose_poker": "Tout le monde défausse 1 carte (s'il n'y a pas d'As, le joueur ayant joué le poker choisit 2 cartes)",
"choose_from_poker;2": "Choisissez quelles cartes récupérer (max 2)",
"choose_from_poker;1": "Choisissez quelle carte récupérer",
"choose_cecchino": "Choisissez qui tirer",
"choose_rimbalzo_player": "Choisissez la cible du rebond",
"choose_rimbalzo_card": "Choisissez la carte à défausser pour le rebond",
"choose_nuova_identita": "Choisissez si vous voulez garder votre personnage actuel ou en changer et repartir de 2 PV",
"choose_bicchierino": "Un joueur de votre choix regagne 1 PV",
"choose_bottiglia": "Choisissez comment jouer la Bouteille !",
"choose_complice": "Choisissez comment jouer Complice !",
"choose_ricercato": "Choisissez contre qui jouer Recherché.",
"choose_birra_function": "Choisissez entre obtenir 1 pépite d'or en défaussant la bière ou jouer la bière.",
"choose_play_as_bang": "Choisissez quelle carte jouer comme Bang !",
"choose_flint_special": "Choisissez quelle carte échanger.",
"emporio_others": "{0} choisit quelle carte récupérer du Magasin général",
"you_died": "VOUS ÊTES MORT",
"spectate": "SPECTATEUR",
"you_win": "VOUS AVEZ GAGNÉ",
"you_lose": "VOUS AVEZ PERDU",
"special_ability": "CAPACITÉ SPÉCIALE",
"gold_rush_discard": "Défaussez l'équipement de ruée vers l'or d'un autre joueur (en payant le prix + 1)",
"gold_rush_discard_2": "Défaussez l'équipement de ruée vers l'or d'un autre joueur (en payant le prix + 1)",
"discard": "DÉFAUSSER",
"to_regain_1_hp": "POUR REGAINER 1 PV",
"play_your_turn": "JOUEZ VOTRE TOUR",
"you_are": "Vous êtes",
"did_pick_as": "a choisi cette carte comme deuxième",
"blackjack_special": "Si la carte est un carreau ou un cœur, il pioche une autre carte.",
"choose_scarp_card_to": "CHOISISSEZ QUELLE CARTE DÉFAUSSER POUR UTILISER",
"pick_a_card": "RETIRER UNE CARTE",
"to_defend_from": "POUR VOUS DÉFENDRE DE",
"submit": "Soumettre",
"copy": "Copier l'invitation",
"no_players_in_range": "Vous ne voyez pas les autres joueurs, équipez une arme ou une lunette !",
"cantplaycard": "Vous ne pouvez pas jouer cette carte de cette façon !",
"chat": {
"spectators": " | Un spectateur regarde la partie | {n} spectateurs regardent la partie",
"chat": "Chat",
"joined": "👋 ;{0}; a rejoint la salle",
"died": "☠️ ;{0}; est mort",
"died_role": "☠️ ;{0}; était un ;{1}; !",
"won": "🏆 ;{0}; a gagné ! Leur rôle était ;{1};",
"choose_character": ";{0}; a ;{1}; comme personnage, sa capacité spéciale est : ;{2}; !",
"starting": "La partie commence !",
"sheriff": "⭐ ;{0}; est le shérif !",
"did_choose_character": ";{0}; a choisi le personnage.",
"turn": "⏩ C'est le tour de ;{0};.",
"draw_from_scrap": ";{0}; a pioché la première carte de la pile de défausse.",
"draw_from_player": ";{0}; a pioché la première carte de la main de ;{1};.",
"flipped": ";{0}; a retourné un ;{1}; ;{2};.",
"scrapped": ";{0}; a défaussé un ;{1}; ;{2};.",
"explode": "💥 ;{0}; a fait exploser la dynamite.",
"beer_save": ";{0}; a utilisé une bière pour sauver sa vie.",
"get_nugget": ";{0}; a obtenu une pépite d'or en utilisant une bière.",
"play_card": ";{0}; a joué ;{1};.",
"play_card_green": ";{0}; a mis en jeu ;{1};.",
"play_card_with": ";{0}; a joué ;{1};, en défaussant ;{2};.",
"purchase_card": ";{0}; a acheté ;{1};.",
"play_card_against": ";{0}; a joué ;{1}; contre ;{2};.",
"play_card_against_with": ";{0}; a joué ;{1}; contre ;{2};, en défaussant ;{3};.",
"play_card_for": ";{0}; a joué ;{1}; pour ;{2};.",
"spilled_beer": ";{0}; a renversé une ;{1};.",
"diligenza": ";{0}; a joué ;{1}; et pioche 2 cartes.",
"wellsfargo": ";{0}; a joué ;{1}; et pioche 3 cartes.",
"saloon": "🍻 ;{0}; a joué ;{1}; et soigne 1 PV à tous les vivants.",
"special_bart_cassidy": ";{0}; a reçu une compensation car il a été blessé.",
"special_el_gringo": ";{0}; a volé une carte à ;{1}; lorsqu'il a été blessé.",
"special_calamity": ";{0}; a joué ;{1}; comme Bang ! contre ;{2};.",
"allroles3": "Dans le jeu il y a : ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};.",
"allroles4": "Dans le jeu il y a : ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};, ;{7}; ;{6};.",
"guess": "🤔 ;{0}; devine ;{1};.",
"guess_right": ";{0}; avait raison.",
"guess_wrong": ";{0}; avait tort.",
"fratelli_sangue": ";{0}; a donné une de ses vies à ;{1};.",
"doctor_heal": ";{0}; a été soigné par le docteur.",
"respond": "↩️ ;{0}; a répondu avec ;{1};.",
"change_username": "✏️ ;{0}; est maintenant ;{1};.",
"lobby_reset": "Retour à la salle dans ;{0}; secondes...",
"prison_free": "🆓 ;{0}; est sorti de prison",
"prison_turn": "🔐 ;{0}; est resté en prison ce tour",
"flip_event": "🎴 ÉVÉNEMENT : ;{0}; 🎴",
"choose_manette": ";{0}; s'est engagé à ne jouer que des cartes de couleur ;{1}; ce tour.",
"UnionPacific": ";{0}; a joué Union Pacific et pioche 4 cartes du paquet",
"use_special": ";{0}; a utilisé la capacité spéciale de son personnage (;{1};)",
"gold_rush_pay_discard": ";{0}; a défaussé ;{2}; de ;{1};.",
"choose_emporio": ";{0}; a choisi ;{1}; du Magasin général.",
"shotgun_scrap": "Lorsque le fusil a touché ;{0}; une carte a volé de sa main (;{1};)",
"taglia_reward": "💰 ;{1}; a obtenu une carte de la prime sur ;{0};",
"snake_bit": "🐍 ;{0}; a été mordu par le serpent à sonnettes."
},
"foc": {
"leggedelwest": "Il doit jouer cette carte ce tour si possible."
},
"mods": "Modificateurs",
"bots": "Bots",
"add_bot": "Ajouter un bot",
"remove_bot": "Supprimer un bot",
"minimum_players": "Le jeu nécessite au moins 3 joueurs pour commencer",
"mod_comp": "Mode compétitif (désactive la prise de dégâts automatique)",
"disconnect_bot": "Remplacer les joueurs qui se déconnectent par des bots",
"your_turn": "Jouez votre tour !",
"your_response": "Répondez !",
"your_choose": "Choisissez une carte !",
"cards": {
"Barile": {
"name": "Baril",
"desc": "Quand quelqu'un joue un Bang contre vous. Vous pouvez retourner la première carte du paquet, si la couleur est Cœur alors elle compte comme une carte Raté"
},
"Dinamite": {
"name": "Dynamite",
"desc": "Lorsque vous jouez la Dynamite, placez-la devant vous, elle restera inoffensive pendant un tour complet. Au début du prochain tour avant de piocher et avant tout retournement de carte (par exemple Prison), retournez une carte du dessus du paquet. Si une carte est entre 2 et 9 de pique (inclus), la dynamite explose : vous perdez 3 vies et défaussez la carte, sinon passez la dynamite au joueur suivant, qui piochera à son tour après avoir terminé son tour"
},
"Mirino": {
"name": "Lunette",
"desc": "Vous voyez les autres joueurs à une distance de -1"
},
"Mustang": {
"name": "Mustang",
"desc": "Les autres joueurs vous voient à une distance de +1"
},
"Prigione": {
"name": "Prison",
"desc": "Équipez cette carte à un autre joueur, sauf le Shérif. Le joueur choisi au début de son tour, doit retourner une carte avant de piocher : si c'est Cœur, défaussez cette carte et jouez le tour normalement, sinon défaussez cette carte et passez le tour"
},
"Remington": {
"name": "Remington",
"desc": "Vous pouvez tirer sur un autre joueur à une distance de 3 ou moins"
},
"Rev Carabine": {
"name": "Rev. Carabine",
"desc": "Vous pouvez tirer sur un autre joueur à une distance de 4 ou moins"
},
"Schofield": {
"name": "Schofield",
"desc": "Vous pouvez tirer sur un autre joueur à une distance de 2 ou moins"
},
"Volcanic": {
"name": "Volcanic",
"desc": "Vous pouvez tirer sur un autre joueur à une distance de 1 ou moins, cependant vous n'avez plus la limite d'un Bang"
},
"Winchester": {
"name": "Winchester",
"desc": "Vous pouvez tirer sur un autre joueur à une distance de 5 ou moins"
},
"Bang!": {
"name": "Bang!",
"desc": "Tirez sur un joueur en vue. Si vous n'avez pas d'armes, votre vue est de 1"
},
"Birra": {
"name": "Bière",
"desc": "Jouez cette carte pour regagner un point de vie. Vous ne pouvez pas soigner plus que la limite maximale de votre personnage. Si vous êtes sur le point de perdre votre dernier point de vie, vous pouvez également jouer cette carte pendant le tour de votre adversaire. La bière ne fait plus effet s'il ne reste que deux joueurs"
},
"Cat Balou": {
"name": "Cat Balou",
"desc": "Choisissez et défaussez une carte d'un autre joueur."
},
"Diligenza": {
"name": "Diligence",
"desc": "Piochez 2 cartes du paquet."
},
"Duello": {
"name": "Duel",
"desc": "Jouez cette carte contre n'importe quel joueur. À tour de rôle, en commençant par votre adversaire, vous pouvez défausser une carte Bang!, le premier joueur qui ne le fait pas perd 1 vie."
},
"Emporio": {
"name": "Magasin Général",
"desc": "Mettez sur la table N cartes du paquet, où N est le nombre de joueurs vivants, à tour de rôle, en commençant par vous, choisissez une carte et ajoutez-la à votre main"
},
"Gatling": {
"name": "Gatling",
"desc": "Tirez sur tous les autres joueurs"
},
"Indiani!": {
"name": "Indiens !",
"desc": "Tous les autres joueurs doivent défausser une carte Bang! ou perdre 1 point de vie"
},
"Mancato!": {
"name": "Raté !",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang"
},
"Panico!": {
"name": "Panique !",
"desc": "Volez une carte à un joueur à une distance de 1"
},
"Saloon": {
"name": "Saloon",
"desc": "Tout le monde regagne 1 point de vie"
},
"WellsFargo": {
"name": "Wells Fargo",
"desc": "Piochez 3 cartes du paquet"
},
"Binocolo": {
"name": "Jumelles",
"desc": "Vous voyez les autres joueurs à une distance de -1"
},
"Riparo": {
"name": "Cachette",
"desc": "Les autres joueurs vous voient à une distance de +1"
},
"Pugno!": {
"name": "Coup de Poing !",
"desc": "Tirez sur un joueur à une distance de 1"
},
"Rag Time": {
"name": "Rag Time",
"desc": "Volez une carte à un autre joueur à n'importe quelle distance"
},
"Rissa": {
"name": "Bagarre",
"desc": "Choisissez une carte à défausser de la main/équipement de tous les autres joueurs"
},
"Schivata": {
"name": "Esquive",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang et ensuite piochez une carte."
},
"Springfield": {
"name": "Springfield",
"desc": "Tirez sur un joueur à n'importe quelle distance"
},
"Tequila": {
"name": "Tequila",
"desc": "Soignez 1 PV à un joueur de votre choix (cela peut être vous)"
},
"Whisky": {
"name": "Whisky",
"desc": "Soignez 2 PV"
},
"Bibbia": {
"name": "Bible",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang et ensuite piochez une carte."
},
"Cappello": {
"name": "Chapeau de Dix Gallons",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang"
},
"Placca Di Ferro": {
"name": "Plaque de Fer",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang"
},
"Sombrero": {
"name": "Sombrero",
"desc": "Utilisez cette carte pour annuler l'effet d'un Bang"
},
"Pugnale": {
"name": "Couteau",
"desc": "Tirez sur un joueur à une distance de 1"
},
"Derringer": {
"name": "Derringer",
"desc": "Tirez sur un joueur à une distance de 1 et ensuite piochez une carte."
},
"Borraccia": {
"name": "Gourde",
"desc": "Regagnez 1 PV"
},
"Can Can": {
"name": "Can Can",
"desc": "Choisissez et défaussez une carte d'un autre joueur."
},
"Conestoga": {
"name": "Conestoga",
"desc": "Volez une carte à un autre joueur à n'importe quelle distance"
},
"Fucile Da Caccia": {
"name": "Fusil de Buffle",
"desc": "Tirez sur un joueur à n'importe quelle distance"
},
"Pony Express": {
"name": "Pony Express",
"desc": "Piochez 3 cartes du paquet"
},
"Pepperbox": {
"name": "Pepperbox",
"desc": "Tirez sur un joueur en vue. Si vous n'avez pas d'armes, votre vue est de 1"
},
"Howitzer": {
"name": "Howitzer",
"desc": "Tirez sur tous les autres joueurs"
},
"Bart Cassidy": {
"name": "Bart Cassidy",
"desc": "Chaque fois qu'il est blessé, il pioche une carte."
},
"Black Jack": {
"name": "Black Jack",
"desc": "Au début de son tour, lorsqu'il doit piocher, il montre à tout le monde la deuxième carte, si c'est un Cœur ou un Carreau, il pioche une troisième carte sans la montrer."
},
"Calamity Janet": {
"name": "Calamity Janet",
"desc": "Elle peut utiliser les cartes Raté ! comme Bang ! et vice versa."
},
"El Gringo": {
"name": "El Gringo",
"desc": "Chaque fois qu'il est blessé, il vole une carte de la main du joueur attaquant."
},
"Jesse Jones": {
"name": "Jesse Jones",
"desc": "Lorsqu'il doit piocher ses cartes, il peut piocher la première carte de la main d'un autre joueur. (cliquez sur les cartes des joueurs ennemis si vous voulez utiliser la capacité)"
},
"Jourdonnais": {
"name": "Jourdonnais",
"desc": "Il joue comme s'il avait un Baril toujours actif, s'il équipe un autre Baril, il peut retourner 2 cartes."
},
"Kit Carlson": {
"name": "Kit Carlson",
"desc": "Lorsqu'il doit piocher, il regarde 3 cartes et en choisit 2, mettant l'autre carte sur le dessus du paquet."
},
"Lucky Duke": {
"name": "Lucky Duke",
"desc": "Chaque fois qu'il doit retourner une carte, il peut retourner 2 fois."
},
"Paul Regret": {
"name": "Paul Regret",
"desc": "Les autres joueurs le voient à une distance de +1."
},
"Pedro Ramirez": {
"name": "Pedro Ramirez",
"desc": "Lorsqu'il doit piocher, il peut prendre la première carte de la pile de défausse. (cliquez sur les cartes défaussées pour utiliser la capacité)"
},
"Rose Doolan": {
"name": "Rose Doolan",
"desc": "Elle voit les autres joueurs à une distance de -1."
},
"Sid Ketchum": {
"name": "Sid Ketchum",
"desc": "Il peut défausser 2 cartes pour regagner 1 PV."
},
"Slab The Killer": {
"name": "Slab The Killer",
"desc": "Pour esquiver ses cartes Bang !, les autres joueurs ont besoin de 2 cartes Raté !"
},
"Suzy Lafayette": {
"name": "Suzy Lafayette",
"desc": "Chaque fois qu'elle a une main vide, elle pioche une carte."
},
"Vulture Sam": {
"name": "Vulture Sam",
"desc": "Lorsqu'un joueur meurt, il obtient toutes les cartes dans la main et les équipements du mort."
},
"Willy The Kid": {
"name": "Willy The Kid",
"desc": "Il n'a pas de limite au nombre de Bang qu'il peut utiliser."
},
"Pixie Pete": {
"name": "Pixie Pete",
"desc": "Il pioche 3 cartes au lieu de 2."
},
"Tequila Joe": {
"name": "Tequila Joe",
"desc": "Lorsqu'il joue une Bière, il regagne 2 PV."
},
"Greg Digger": {
"name": "Greg Digger",
"desc": "Chaque fois qu'un joueur meurt, il regagne jusqu'à 2 vies."
},
"Herb Hunter": {
"name": "Herb Hunter",
"desc": "Chaque fois qu'un joueur meurt, il pioche 2 cartes."
},
"Elena Fuente": {
"name": "Elena Fuente",
"desc": "Elle peut utiliser n'importe quelle carte de sa main comme Raté."
},
"Bill Noface": {
"name": "Bill Noface",
"desc": "Pioche 1 carte + 1 carte pour chaque blessure qu'il a."
},
"Molly Stark": {
"name": "Molly Stark",
"desc": "Lorsqu'elle utilise une carte de sa main en dehors de son tour, elle pioche une carte."
},
"Apache Kid": {
"name": "Apache Kid",
"desc": "Les cartes de carreaux ♦️ jouées contre lui n'ont aucun effet (ne fonctionne pas en duel)."
},
"Sean Mallory": {
"name": "Sean Mallory",
"desc": "Il peut garder jusqu'à 10 cartes dans sa main lorsqu'il termine le tour."
},
"Belle Star": {
"name": "Belle Star",
"desc": "Pendant son tour, les cartes vertes et bleues des autres joueurs ne fonctionnent pas."
},
"Vera Custer": {
"name": "Vera Custer",
"desc": "Avant de piocher, elle peut choisir la capacité spéciale d'un autre joueur vivant. Cette capacité est utilisée jusqu'au prochain tour."
},
"Chuck Wengam": {
"name": "Chuck Wengam",
"desc": "Lors de son tour, il peut décider de perdre 1 PV pour piocher 2 cartes du paquet."
},
"Pat Brennan": {
"name": "Pat Brennan",
"desc": "Au lieu de piocher, il peut voler une carte de l'équipement d'un autre joueur. (cliquez sur le joueur ennemi si vous voulez utiliser la capacité)"
},
"José Delgado": {
"name": "José Delgado",
"desc": "Lors de son tour, il peut défausser une carte bleue pour piocher 2 cartes, jusqu'à deux fois par tour."
},
"Doc Holyday": {
"name": "Doc Holyday",
"desc": "Il peut défausser 2 cartes pour jouer un Bang."
},
"Fuorilegge": {
"name": "Hors-la-loi"
},
"Rinnegato": {
"name": "Renégat"
},
"Sceriffo": {
"name": "Shérif"
},
"Vice": {
"name": "Adjoint"
},
"Miniera Abbandonata": {
"name": "Mine Abandonnée",
"desc": "Les joueurs piochent dans la pile de défausse pendant leur phase 1 et défaussent sur le dessus du paquet pendant la phase 3 (si la pile de défausse est épuisée, ils doivent piocher et défausser sur le dessus du paquet)"
},
"Il Giudice": {
"name": "Le Juge",
"desc": "Vous ne pouvez pas équiper des cartes sur vous-même ou sur d'autres joueurs"
},
"Agguato": {
"name": "Embuscade",
"desc": "La distance de base entre deux joueurs est de 1"
},
"Rimbalzo": {
"name": "Rebond",
"desc": "Le joueur peut jouer Bang contre les cartes équipées par les autres joueurs, s'ils ne jouent pas Raté, elles sont défaussées (cliquez sur la carte d'événement)"
},
"Cecchino": {
"name": "Sniper",
"desc": "Pendant leur tour, les joueurs peuvent défausser 2 Bang! pour tirer un Bang! qui nécessite 2 Raté (cliquez sur la carte)"
},
"Lazo": {
"name": "Lasso",
"desc": "Les cartes dans la fente d'équipement ne fonctionnent pas"
},
"Ranch": {
"name": "Ranch",
"desc": "Après avoir pioché, le joueur peut défausser autant de cartes qu'il veut de sa main et en piocher autant du paquet"
},
"Dead Man": {
"name": "Mort",
"desc": "Le premier joueur qui est mort revient à la vie avec 2 PV et 2 cartes"
},
"Liquore Forte": {
"name": "Liqueur Forte",
"desc": "Les joueurs peuvent sauter la pioche pour regagner 1 PV (cliquez sur la carte d'événement pour utiliser)"
},
"Vendetta": {
"name": "Vengeance",
"desc": "Lorsqu'il termine son tour, le joueur retourne une carte du paquet, si c'est un ❤️, il joue un autre tour (mais il ne retourne pas une autre carte)"
},
"Roulette Russa": {
"name": "Roulette Russe",
"desc": "En commençant par le shérif, chaque joueur défausse un Raté, le premier qui ne le fait pas perd 2 PV"
},
"Legge Del West": {
"name": "La Loi de l'Ouest",
"desc": "Chaque joueur montre la deuxième carte qu'il pioche et doit l'utiliser dans ce tour (si c'est possible)"
},
"Peyote": {
"name": "Peyote",
"desc": "Au lieu de piocher, le joueur essaie de deviner la couleur de la carte, s'il a raison, il ajoute la carte à sa main et continue d'essayer de deviner la carte suivante"
},
"Fratelli Di Sangue": {
"name": "Frères de Sang",
"desc": "Au début de leur tour, les joueurs peuvent perdre 1 PV (sauf le dernier) pour le donner à un autre joueur"
},
"I Dalton": {
"name": "Les Dalton",
"desc": "Les joueurs ayant des cartes bleues équipées, en défaussent une de leur choix"
},
"Sermone": {
"name": "Le Sermon",
"desc": "Les joueurs ne peuvent pas jouer Bang! pendant leur tour"
},
"Città Fantasma": {
"name": "Ville Fantôme",
"desc": "Tous les joueurs morts reviennent à la vie à leur tour, ils ne peuvent pas mourir et piochent 3 cartes au lieu de 2. Lorsqu'ils terminent leur tour, ils meurent."
},
"Il Reverendo": {
"name": "Le Révérend",
"desc": "Les bières ne peuvent pas être jouées"
},
"Sbornia": {
"name": "Gueule de Bois",
"desc": "Les personnages perdent leurs capacités spéciales"
},
"Il Dottore": {
"name": "Le Docteur",
"desc": "Le joueur avec le moins de PV récupère 1 PV"
},
"Corsa All Oro": {
"name": "Ruée vers l'Or",
"desc": "Les tours sont joués dans le sens inverse des aiguilles d'une montre"
},
"Maledizione": {
"name": "Malédiction",
"desc": "Toutes les cartes sont des ♠️"
},
"Sparatoria": {
"name": "Fusillade",
"desc": "La limite de Bang! du tour est de 2"
},
"Benedizione": {
"name": "Bénédiction",
"desc": "Toutes les cartes sont des ❤️"
},
"Il Treno": {
"name": "Arrivée du Train",
"desc": "Les joueurs piochent 1 carte supplémentaire"
},
"Sete": {
"name": "Soif",
"desc": "Les joueurs ne piochent qu'une seule carte au début de leur tour"
},
"Nuova Identita": {
"name": "Nouvelle Identité",
"desc": "Au début de leur tour, chaque joueur peut choisir de changer de personnage avec celui montré au début du jeu. S'il le fait, il repart avec 2 PV"
},
"Manette": {
"name": "Menottes",
"desc": "Après avoir pioché en phase 1, le joueur déclare une couleur. Il ne pourra utiliser que des cartes de cette couleur pour ce tour"
},
"Mezzogiorno di Fuoco": {
"name": "Midi de Feu",
"desc": "Chaque joueur perd 1 PV lorsqu'il commence son tour"
},
"Per Un Pugno Di Carte": {
"name": "Pour une Poignée de Cartes",
"desc": "Au début de son tour, le joueur est la cible de Bang aussi nombreux que le nombre de cartes qu'il a en main"
},
"Pepita": {
"name": "Pépite d'Or"
},
"Bicchierino": {
"name": "Verre",
"desc": "Vous pouvez choisir un joueur qui regagne 1 PV (même vous)"
},
"Bottiglia": {
"name": "Bouteille",
"desc": "Peut être jouée comme Panique !, Bière ou Bang !"
},
"Complice": {
"name": "Complice",
"desc": "Peut être joué comme Magasin Général, Duel ou Cat Balou."
},
"Corsa All Oro_gr": {
"name": "Ruée vers l'Or",
"desc": "Vous terminez votre tour, regagnez tous vos PV et commencez un nouveau tour."
},
"Rum": {
"name": "Rhum",
"desc": "Retournez 4 cartes, pour chaque couleur différente, vous gagnez 1 PV."
},
"Union Pacific": {
"name": "Union Pacific",
"desc": "Piochez 4 cartes du paquet."
},
"Calumet": {
"name": "Calumet",
"desc": "Les cartes de carreaux jouées par les autres joueurs n'ont aucun effet sur vous."
},
"Cinturone": {
"name": "Ceinture",
"desc": "Vous pouvez garder jusqu'à 8 cartes lorsque vous terminez votre tour."
},
"Ferro di Cavallo": {
"name": "Fer à Cheval",
"desc": "Chaque fois que vous devez retourner une carte, vous retournez deux fois !"
},
"Piccone": {
"name": "Pioche",
"desc": "Vous obtenez 1 carte supplémentaire lorsque vous piochez au début du tour."
},
"Ricercato": {
"name": "Recherché",
"desc": "Jouez-le sur un autre joueur, qui tue la cible obtient 2 cartes supplémentaires et 1 pépite d'or."
},
"Setaccio": {
"name": "Pan d'Or",
"desc": "Payez 1 pépite d'or pour piocher 1 carte du paquet, jusqu'à deux fois par tour. (Cliquez sur la carte équipée pour utiliser)"
},
"Stivali": {
"name": "Bottes",
"desc": "Chaque fois que vous perdez 1 PV, vous piochez 1 carte du paquet."
},
"Talismano": {
"name": "Porte-Bonheur",
"desc": "Chaque fois que vous perdez 1 PV, vous obtenez 1 pépite d'or."
},
"Zaino": {
"name": "Sac à Dos",
"desc": "Payez 2 pépites d'or pour soigner 1 PV. (Cliquez sur la carte équipée pour utiliser)"
},
"Don Bell": {
"name": "Don Bell",
"desc": "Lorsqu'il termine son tour, il retourne une carte, si elle retourne Cœur ❤️ ou Carreau ♦️, il joue à nouveau."
},
"Dutch Will": {
"name": "Dutch Will",
"desc": "Il pioche 2 cartes, en défausse 1 et prend 1 pépite d'or."
},
"Jacky Murieta": {
"name": "Jacky Murieta",
"desc": "Pendant son tour, il peut payer 2 pépites d'or pour avoir la capacité de tirer un autre Bang !"
},
"Josh McCloud": {
"name": "Josh McCloud",
"desc": "Il peut payer 2 pépites d'or pour obtenir la première carte du paquet de ruée vers l'or."
},
"Madam Yto": {
"name": "Madam Yto",
"desc": "Elle pioche 1 carte du paquet chaque fois qu'une bière est jouée."
},
"Pretty Luzena": {
"name": "Pretty Luzena",
"desc": "Une fois par tour, elle peut bénéficier d'une réduction de 1 sur la boutique des cartes ruée vers l'or."
},
"Raddie Snake": {
"name": "Raddie Snake",
"desc": "Il peut jeter 1 pépite d'or pour piocher 1 carte du paquet pendant son tour (2 fois max par tour)."
},
"Simeon Picos": {
"name": "Simeon Picos",
"desc": "Il obtient 1 pépite d'or chaque fois qu'il perd 1 PV."
},
"Fantasma": {
"name": "Fantôme",
"desc": "Jouez sur un joueur éliminé : Ce joueur revient dans le jeu, mais ne peut ni gagner ni perdre des points de vie."
},
"Lemat": {
"name": "Lemat",
"desc": "Pendant votre tour, vous pouvez utiliser n'importe quelle carte comme Bang ! (Cliquez sur la carte une fois équipée)."
},
"SerpenteASonagli": {
"name": "Serpent à Sonnettes",
"desc": "Jouez sur n'importe quel joueur. Au début de son tour. si ce joueur pioche sur Piques, il perd 1 point de vie."
},
"Shotgun": {
"name": "Fusil",
"desc": "Chaque fois que vous blessez un joueur, il doit défausser une carte de sa main."
},
"Taglia": {
"name": "Prime",
"desc": "Jouez sur n'importe qui. Si ce joueur est touché par Bang !, la personne qui a tiré obtient une carte du paquet."
},
"Mira": {
"name": "Viser",
"desc": "Jouez cette carte avec une carte Bang!. Si la cible est touchée, elle perd 2 points de vie."
},
"RitornoDiFiamma": {
"name": "Retour de Flamme",
"desc": "Compte comme une carte Raté!. Le joueur qui a tiré est la cible d'un Bang!."
},
"Bandidos": {
"name": "Bandidos",
"desc": "Chaque joueur choisit de défausser 2 cartes de sa main (ou 1 s'il n'en a qu'une) ou de perdre 1 point de vie."
},
"Fuga": {
"name": "Fuite",
"desc": "Peut être jouée hors tour. Évite l'effet d'une carte brune (pas Bang!) dont vous êtes la cible."
},
"Sventagliata": {
"name": "Ventilateur",
"desc": "Compte comme le seul Bang! du tour. Un joueur de votre choix à une distance de 1 de la cible (s'il y en a un, à l'exclusion de vous-même) est également une cible d'un Bang."
},
"UltimoGiro": {
"name": "Dernière Tournée",
"desc": "Récupère 1 point de vie"
},
"Poker": {
"name": "Poker",
"desc": "Tous les autres joueurs défaussent une carte de leur main, en même temps. S'il n'y a pas d'As, piochez jusqu'à 2 de ces cartes."
},
"Salvo": {
"name": "Sauvé !",
"desc": "Peut être jouée hors tour. Empêche un autre joueur de perdre 1 point de vie. S'il survit, il pioche 2 cartes de sa main ou du paquet (votre choix)."
},
"Tomahawk": {
"name": "Tomahawk",
"desc": "Attaque un joueur jusqu'à une distance de 2."
},
"Tornado": {
"name": "Tornade",
"desc": "Tout le monde défausse une carte de leur main (si possible), puis pioche 2 cartes du paquet."
},
"Black Flower": {
"name": "Fleur Noire",
"desc": "Une fois par tour, vous pouvez utiliser une carte de Pique pour tirer un Bang ! supplémentaire"
},
"Colorado Bill": {
"name": "Colorado Bill",
"desc": "Chaque fois que vous jouez une carte Bang!, si vous piochez un Pique, le tir ne peut pas être évité."
},
"Der Spot Burst Ringer": {
"name": "Der Spot Burst Ringer",
"desc": "Une fois par tour, vous pouvez utiliser une carte Bang! comme une Gatling."
},
"Evelyn Shebang": {
"name": "Evelyn Shebang",
"desc": "Vous pouvez choisir de ne pas piocher de cartes pendant votre phase de pioche. Pour chaque carte non piochée, vous tirez un Bang! sur une cible atteignable, sur une cible différente."
},
"Henry Block": {
"name": "Henry Block",
"desc": "Quiconque pioche ou défausse une de vos cartes en jeu ou en main est la cible d'un Bang!."
},
"Lemonade Jim": {
"name": "Lemonade Jim",
"desc": "Chaque fois qu'un autre joueur joue une Bière, vous pouvez défausser une carte de votre main pour regagner également 1 point de vie."
},
"Mick Defender": {
"name": "Mick Defender",
"desc": "Si vous êtes la cible d'une carte brune (pas Bang!), vous pouvez utiliser une carte Raté! pour éviter un effet."
},
"Tuco Franziskaner": {
"name": "Tuco Franziskaner",
"desc": "Si vous n'avez pas de cartes bleues équipées, vous piochez 2 cartes supplémentaires."
},
"Big Spencer": {
"name": "Big Spencer",
"desc": "Commence avec 5 cartes. Ne peut pas jouer Raté!"
},
"Flint Westwood": {
"name": "Flint Westwood",
"desc": "Peut échanger une carte de sa main avec 2 cartes aléatoires de la main d'un autre joueur pendant son tour."
},
"Gary Looter": {
"name": "Gary Looter",
"desc": "Pioche toutes les cartes excédentaires défaussées par les autres joueurs à la fin de chaque tour."
},
"Greygory Deckard": {
"name": "Greygory Deckard",
"desc": "Au début de son tour, peut piocher 2 personnages aléatoires et gagne toutes leurs capacités."
},
"John Pain": {
"name": "John Pain",
"desc": "S'il a moins de 6 cartes en main, lorsqu'un joueur « pioche! », John ajoute la carte piochée à sa main."
},
"Lee Van Kliff": {
"name": "Lee Van Kliff",
"desc": "Pendant son tour, peut défausser une carte Bang! pour répéter l'effet d'une carte à bordure brune qu'il vient de jouer."
},
"Teren Kill": {
"name": "Teren Kill",
"desc": "Chaque fois qu'il serait éliminé, « piochez! » : si ce n'est pas une carte de Pique, Teren reste à 1 point de vie et pioche 1 carte."
},
"Youl Grinner": {
"name": "Youl Grinner",
"desc": "Avant de piocher, les joueurs ayant plus de cartes en main que Youl doivent lui donner une carte de leur choix."
},
"Camposanto": {
"name": "Cimetière",
"desc": "Au début de leur tour, chaque joueur éliminé revient dans le jeu avec 1 point de vie. Piochez une carte de rôle au hasard parmi les joueurs éliminés."
},
"Darling Valentine": {
"name": "Darling Valentine",
"desc": "Au début de leur tour, chaque joueur défausse sa main et pioche le même nombre de cartes du paquet."
},
"Dorothy Rage": {
"name": "Dorothy Rage",
"desc": "Pendant leur tour, chaque joueur peut forcer un autre joueur à jouer une carte."
},
"Helena Zontero": {
"name": "Helena Zontero",
"desc": "Quand Helena entre dans le jeu, « pioche! » : si un Cœur ou un Carreau est pioché, mélangez les cartes de rôle actives sauf le Shérif, et redistribuez-les au hasard."
},
"Lady Rosa del Texas": {
"name": "Lady Rose of Texas",
"desc": "Pendant leur tour, chaque joueur peut échanger sa place avec le joueur à sa droite, qui passe son prochain tour. (Cliquez sur la carte)"
},
"Miss Susanna": {
"name": "Miss Susanna",
"desc": "Pendant leur tour, chaque joueur doit jouer au moins 3 cartes. S'ils ne le font pas, ils perdent 1 point de vie."
},
"Regolamento di Conti": {
"name": "Règlement de Comptes",
"desc": "Toutes les cartes peuvent être jouées comme si elles étaient Bang!. Les cartes Bang! peuvent être jouées comme si elles étaient Raté! (Cliquez sur la carte)"
},
"Sacagaway": {
"name": "Sacagaway",
"desc": "Tous les joueurs jouent avec leurs cartes face visible (sauf leur carte de rôle!)."
},
"Wild West Show": {
"name": "Wild West Show",
"desc": "Le but de chaque joueur devient : « Soyez le dernier debout ! »"
}
},
"help": {
"character": "Personnages",
"characters_special": "Chaque personnage a des capacités spéciales et un nombre de vies qui les rendent uniques. \nLes vies sont le nombre de points de vie que vous pouvez perdre avant de mourir et indiquent également le nombre maximum de cartes que vous pouvez garder en main.",
"deathnobeer": "Lorsque vous perdez votre dernier point de vie et que vous n'avez pas de bière 🍺️ en main, vous mourrez. \nVos cartes sont défaussées et votre rôle est révélé à tout le monde.",
"discard": "Défausser",
"distance": "Distance",
"distancecalc": "La distance est automatiquement calculée par le jeu et correspond à la distance minimale entre les joueurs à gauche et à droite.",
"drawinstructions": "Pour piocher des cartes, vous devez cliquer sur le paquet lorsque vous voyez cette animation.",
"drawthecards": "Piochez les cartes",
"endgame": "Fin du jeu",
"endgameconditions": "Le jeu se termine lorsque l'une des conditions suivantes est remplie :",
"endgamesheriffwin": "Tous les hors-la-loi 🐺️ et les renégats 🦅️ sont morts. \nDans ce cas, le shérif ⭐️ et les adjoints 🎖️ gagnent.",
"endgameshriffdeath": "Le shérif ⭐️ meurt. \nSi le renégat 🦅️ est le dernier joueur en vie, il gagne, sinon les hors-la-loi gagnent.",
"endingturn": "Lorsque vous avez fini de jouer vos cartes, c'est-à-dire lorsque vous ne voulez plus ou ne pouvez plus jouer de cartes, vous devez défausser les cartes qui dépassent votre nombre actuel de vies.\n\nEnsuite, vous passez le tour au joueur suivant en cliquant sur terminer le tour.",
"equipment": "ÉQUIPEMENT",
"justoneweapon": "Vous ne pouvez avoir qu'une seule arme équipée.",
"maxtwocardsequip": "Vous ne pouvez pas avoir 2 cartes avec le même nom équipées.",
"outlawreward": "Quiconque tue un hors-la-loi 🐺️ pioche 3 cartes du paquet (les autres hors-la-loi aussi 🐺️).",
"playerdeath": "La mort d'un joueur",
"playingcards": "Jouer les cartes",
"playingdmg": "Vous pouvez jouer vos cartes pour vous-même ou pour nuire à d'autres joueurs en essayant de les éliminer.",
"playingduringturn": "Vous ne pouvez jouer les cartes que pendant votre tour. Pour jouer des cartes, cliquez sur les cartes de votre main.\nÀ l'exception des cartes utilisées en réponse, comme Raté 😅️.",
"playingifyouwant": "Vous n'êtes pas obligé de jouer des cartes.",
"playlimit": "Il n'y a que 3 limitations :",
"playonlyonebang": "Vous ne pouvez jouer qu'un seul Bang! \npar tour (se réfère uniquement aux cartes nommées Bang!)",
"rewardspen": "Pénalités et récompenses",
"roles": "Rôles",
"sheriffkillsvice": "Si le shérif ⭐️ tue un adjoint, il perd toutes les cartes de sa main et en jeu devant lui.",
"thecards": "Cartes",
"title": "Comment jouer",
"turndiscard": "Défaussez toutes les cartes excédentaires",
"turndraw": "Piochez 2 cartes",
"turnplay": "Jouez autant de cartes que vous le souhaitez",
"turns": "Tours",
"turnstart": "Cela commence toujours par le Shérif ⭐️, et le jeu continue dans le sens des aiguilles d'une montre, les tours sont divisés en 3 phases.",
"weapon": "ARME",
"renegade": "Renégat",
"vice": "Adjoint",
"outlaw": "Hors-la-loi",
"sheriff": "Shérif",
"allcharacters": "Tous les personnages",
"gotoallcharacters": "Aller à tous les personnages",
"gotocards": "Aller aux cartes",
"gotohighnoon": "Aller à Midi",
"gotofoc": "Aller à Une Poignée de Cartes",
"gotogoldrush": "Aller à Ruée vers l'Or",
"highnooncards": "Midi - Cartes Événements",
"foccards": "Une Poignée de Cartes - Cartes Événements",
"goldrushcards": "Cartes Ruée vers l'Or",
"valleyofshadowscards": "Cartes Vallée des Ombres"
},
"theme": {
"sepia": "Sépia",
"light": "Clair",
"dark": "Sombre",
"grayscale": "Gris",
"black": "Noir"
}
}

View File

@ -1,9 +1,6 @@
import it from './it.json'
import en from './en.json'
import cs from './cs.json'
import fr from './fr.json'
import es from './es.json'
import ru from './ru.json'
export const defaultLocale = 'it'
@ -12,8 +9,5 @@ export const fallbackLocale = 'en'
export const languages = {
it: it,
en: en,
cs: cs,
fr: fr,
es: es,
ru: ru,
cs: cs
}

View File

@ -19,7 +19,6 @@
"spectate_lobbies": "Osserva le partite in corso:",
"no_lobby_available": "Nessuna stanza disponibile",
"create_lobby": "Crea una stanza:",
"characters_to_distribute": "Personaggi da distribuire: ",
"lobby_name": "Nome:",
"leave_room": "Esci dalla stanza",
"warning": "Attenzione!",
@ -103,48 +102,44 @@
"chat": {
"spectators": " | Uno spettatore sta guardando la partita | {n} spettatori stanno guardando la partita",
"chat": "Chat",
"joined": "👋 ;{0}; è entrato nella stanza",
"died": "☠️ ;{0}; è morto",
"died_role": "☠️ ;{0}; era ;{1};!",
"won": "🏆 ;{0}; ha vinto! Il suo ruolo era ;{1};",
"joined": ";{0}; è entrato nella stanza",
"died": ";{0}; è morto",
"died_role": ";{0}; era ;{1};!",
"won": ";{0}; ha vinto! Il suo ruolo era ;{1};",
"choose_character": ";{0}; ha come personaggio ;{1};, la sua abilità speciale è: ;{2};!",
"starting": "La partita sta iniziando!",
"sheriff": ";{0}; è lo sceriffo!",
"sheriff": ";{0}; è lo sceriffo!",
"did_choose_character": ";{0}; ha scelto il personaggio.",
"turn": "È il turno di ;{0};.",
"turn": "È il turno di ;{0};.",
"draw_from_scrap": ";{0}; ha pescato la prima carta dalla pila delle carte scartate.",
"draw_from_player": ";{0}; ha pescato la prima carta dalla mano di ;{1};.",
"flipped": ";{0}; ha estratto ;{1}; ;{2};.",
"scrapped": ";{0}; ha scartato ;{1}; ;{2};.",
"explode": "💥 ;{0}; ha fatto esplodere la dinamite.",
"explode": ";{0}; ha fatto esplodere la dinamite.",
"beer_save": ";{0}; ha usato una birra per recuperare una vita.",
"get_nugget": ";{0}; ha ottenuto una pepita d'oro usando una Birra.",
"play_card": ";{0}; ha giocato ;{1};.",
"play_card_green": ";{0}; ha messo in gioco ;{1};.",
"play_card_with": ";{0}; ha giocato ;{1};, scartando ;{2};.",
"purchase_card": ";{0}; ha comprato ;{1};.",
"play_card_against": ";{0}; ha giocato ;{1}; contro ;{2};.",
"play_card_against_with": ";{0}; ha giocato ;{1}; contro ;{2};, scartando ;{3};.",
"play_card_for": ";{0}; ha giocato ;{1}; per ;{2};.",
"spilled_beer": ";{0}; ha rovesciato una ;{1};.",
"diligenza": ";{0}; ha giocato ;{1}; e ha pescato 2 carte.",
"wellsfargo": ";{0}; ha giocato ;{1}; e ha pescato 3 carte.",
"saloon": "🍻 ;{0}; ha giocato ;{1}; e ha curato 1 punto vita a tutti.",
"saloon": ";{0}; ha giocato ;{1}; e ha curato 1 punto vita a tutti.",
"special_bart_cassidy": ";{0}; ha ricevuto un risarcimento perchè è stato ferito.",
"special_el_gringo": ";{0}; rubato una carta a ;{1}; mentre veniva colpito.",
"special_calamity": ";{0}; ha giocato ;{1}; come un Bang! contro ;{2};.",
"allroles3": "Nella partita ci sono: ;{1}; ;{0};, ;{3}; ;{2}; e ;{5}; ;{4};.",
"allroles4": "Nella partita ci sono: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4}; e ;{7}; ;{6};.",
"guess": "🤔 ;{0}; pensa sia ;{1};.",
"guess": ";{0}; pensa sia ;{1};.",
"guess_right": ";{0}; ha indovinato.",
"guess_wrong": ";{0}; ha sbagliato.",
"fratelli_sangue": ";{0}; ha donato una delle sue vite a ;{1};.",
"doctor_heal": ";{0}; è stato curato dal dottore.",
"respond": "↩️ ;{0}; ha risposto con ;{1};.",
"change_username": "✏️ ;{0}; ha cambiato nome in ;{1};.",
"respond": ";{0}; ha risposto con ;{1};.",
"change_username": ";{0}; ha cambiato nome in ;{1};.",
"lobby_reset": "Si ritorna alla stanza in ;{0}; secondi...",
"prison_free": "🆓 ;{0}; è uscito di prigione",
"prison_turn": "🔐 ;{0}; rimane in prigione questo turno",
"prison_free": ";{0}; è uscito di prigione",
"prison_turn": ";{0}; rimane in prigione questo turno",
"flip_event": "🎴 EVENTO: ;{0}; 🎴",
"choose_manette": ";{0}; si è impegnato ad usare solo carte di seme ;{1}; in questo turno.",
"UnionPacific": ";{0}; ha giocato Union Pacific e ha pescato 4 carte.",
@ -152,9 +147,8 @@
"gold_rush_pay_discard": ";{0}; ha fatto scartare ;{2}; a ;{1};.",
"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.",
"in_private_car": "🚋💁🏻 ;{0}; è in una carrozza privata e non è stato attaccato da ;{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"
@ -856,130 +850,6 @@
"Wild West Show": {
"name": "Wild West Show",
"desc": "L'obiettivo di ogni giocatore diventa: \"Rimani l'ultimo in gioco!\""
},
"Boom Town": {
"name": "Boom Town",
"desc": "Scarta una Bang! per derubare il treno"
},
"Caticor": {
"name": "Caticor",
"desc": "Scarta una Cat Balou o un Panico per derubare il treno"
},
"Creepy Creek": {
"name": "Creepy Creek",
"desc": "Scarta una carta di picche per derubare il treno"
},
"Crowns Hole": {
"name": "Crown's Hole",
"desc": "Scarta una birra per derubare il treno"
},
"Deadwood": {
"name": "Deadwood",
"desc": "Scarta una carta equipaggiamento per derubare il treno"
},
"Dodgeville": {
"name": "Dodgeville",
"desc": "Scarta un Mancato! per derubare il treno"
},
"Fort Worth": {
"name": "Fort Worth",
"desc": "Scarta una carta con il numero 10, J, Q, K, A per derubare il treno"
},
"Frisco": {
"name": "Frisco",
"desc": "Scarta una carta di fiori per derubare il treno"
},
"Miners Oath": {
"name": "Miner's Oath",
"desc": "Scarta una carta di quadri per derubare il treno"
},
"San Tafe": {
"name": "San Tafe",
"desc": "Scarta una carta di cuori per derubare il treno"
},
"Tombrock": {
"name": "Tombrock",
"desc": "Per derubare il treno, perdi 1 punto vita"
},
"Yooma": {
"name": "Yooma",
"desc": "Scarta una carta con un numero compreso tra 2 e 9 per derubare il treno"
},
"Virginia Town": {
"name": "Virginia Town",
"desc": "Scarta due carte per derubare il treno"
},
"Ironhorse": {
"name": "Ironhorse",
"desc": "LOCOMOTIVA: Ogni giocatore, incluso colui che ha attivato l'effetto, è bersaglio di un BANG!\nNessun giocatore è responsabile dell'eventuale perdita di punti vita.\nSe tutti i giocatori vengono eliminati allo stesso tempo, i Fuorilegge vincono."
},
"Leland": {
"name": "Leland",
"desc": "LOCOMOTIVA: svolgi l'effetto dell'Emporio, cominciando dal giocatore di turno e procedendo in senso orario."
},
"Baggage Car": {
"name": "Baggage Car",
"desc": "Scartalo: ottieni l'effetto di un Mancato!, Panico!, Cat Balou o di un BANG! extra.\nDiscard this for a Missed!Panic!, Cat Balou, or an extra BANG!"
},
"Caboose": {
"name": "Caboose",
"desc": "Pro scartare un aura tua carta bordo bin incuso un vagone come se fosse un Mancato!"
},
"Cattle Truck": {
"name": "Cattle Truck",
"desc": "Scartalo: guarda le 3 carte in cima agli scarti e pescane I"
},
"Circus Wagon": {
"name": "Circus Wagon",
"desc": "Scartalo: ogni altro giocatore deve scartare una carta che ha in gioco."
},
"Coal Hopper": {
"name": "Coal Hopper",
"desc": "Scartalo: pesca una carta e scarta un vagone in gioco davanti a un giocatore a ma scelta."
},
"Dining Car": {
"name": "Dining Car",
"desc": "A inizio turno, \"estrai!\": se è Cuori, recuperi I punto vita."
},
"Express Car": {
"name": "Express Car",
"desc": "Scarta tutte le carte in mano, poi gioca un altro turno"
},
"Ghost Car": {
"name": "Ghost Car",
"desc": "Giocalo su chiunque tranne lo Sceritfo. Se vieni eliminato, invece resti in gioco, ma non puol guada nare ne perdere punk vita."
},
"Lounge Car": {
"name": "Lounge Car",
"desc": "Scartalo: pesca 2 vagoni dal mazzo, mettine I in gioco di fronte a te e 1 di fronte a un altro giocatore."
},
"Lumber Flatcar": {
"name": "Lumber Flatcar",
"desc": "Giocalo su un qualsiasi giocatore (compreso te). Finché questa carta è in gioco, quel giocatore vede gli altri giocatori a distanza aumentata di 1."
},
"Mail Car": {
"name": "Mail Car",
"desc": "Scartalo: pesca 3 carte e dai 1 di esse a un altro giocatore a tua scelta."
},
"Observation Car": {
"name": "Observation Car",
"desc": "Tu vedi gli altri a distanza -1. Gli altri a vedono a distanza +1."
},
"Passenger Car": {
"name": "Passenger Car",
"desc": "Scartalo: pesca una carta (in mano o in gioco) da un altro giocatore"
},
"Prisoner Car": {
"name": "Prisoner Car",
"desc": "Le carte Duello e Indiani! giocate dagli altri giocatori non hanno effetto su di te."
},
"Private Car": {
"name": "Private Car",
"desc": "se non hai carte in mano. non puoi essere bersaelio di carte BANG"
},
"Sleeper Car": {
"name": "Sleeper Car",
"desc": "Una volta per turno, puoi scartare un'altra tua carta a bordo blu incluso."
}
},
"help": {

View File

@ -1,918 +0,0 @@
{
"trademark": "Bang! является торговой маркой, принадлежащей DVGiochi",
"tip_1": "Лучше с друзьями!",
"tip_2": "Иконка 🤖️ означает, что игрок - бот!",
"tip_3": "Вы можете нажать на карты персонажей и оборудования других игроков, если хотите узнать больше!",
"tip_4": "Включает Dodge City, High Noon, Fistful Of Cards!",
"tip_5": "Нашли ошибку? Напишите нам в Discord!",
"tip_6": "Теперь у нас есть сервер Discord!",
"tip_7": "Если вы хотите помочь нам перевести игру на ваш язык, свяжитесь с нами в Discord!",
"tip_8": "Если вы отключитесь во время игры, вас заменит бот (пока вы отсутствуете)!",
"tip_9": "Если вы на мобильном устройстве, удерживайте карту, чтобы прочитать описание!",
"tip_10": "Если сомневаетесь, стреляйте во всех других игроков!",
"online_players": "Онлайн-игроки: ",
"shuffle_players": "Перемешать игроков",
"choose_username": "Выберите имя пользователя:",
"report_bug": "Опишите ошибку",
"report": "Сообщить об ошибке",
"available_lobbies": "Доступные лобби:",
"spectate_lobbies": "Наблюдать за играми:",
"no_lobby_available": "Нет доступных лобби",
"create_lobby": "Создать лобби:",
"characters_to_distribute": "Персонажи для распределения: ",
"lobby_name": "Название:",
"leave_room": "Покинуть лобби",
"warning": "Внимание!",
"connection_error": "Не удается подключиться к серверу.",
"end_turn": "Конец хода!",
"start_game": "Начать!",
"expansions": "Дополнения",
"click_to_toggle": "Нажмите, чтобы переключить",
"details": "Детали",
"ok": "OK",
"you": "ВЫ",
"owner": "ВЛАДЕЛЕЦ",
"cancel": "ОТМЕНА",
"send": "ОТПРАВИТЬ",
"password": "Пароль: ",
"room_password_prompt": "Пароль для лобби: ",
"private_room": "Частное лобби",
"room": "Лобби: ",
"room_players": "Игроки (вы - {username})",
"choose_character": "Выберите своего персонажа",
"choose_card": "Выберите карту",
"choose_card_from": " из ",
"flip_card": "↙️ Перевернуть карту",
"draw_cards": "⏬ Возьмите свои карты из колоды",
"play_cards": "▶️ Сыграйте свои карты",
"respond_card": "↩️ Ответить на карту",
"wait": "⏸ Подождите",
"choose_cards": "🔽 Выберите карту",
"take_dmg": "Получить урон",
"choose_response": "Выберите ваш ответ ",
"choose_response_to": "на ",
"choose_response_needed": "ТРЕБУЕТСЯ ",
"choose_manette": "Выберите масть, вы сможете играть только карты с этой мастью в этом ходу.",
"hand": "РУКА",
"card_against": "Против кого вы сыграете карту?",
"choose_sid_scrap": "Сбросьте 2 карты, чтобы не умереть",
"choose_card_to_get": "Выберите карту",
"choose_guess": "Угадайте цвет масти",
"choose_ranch": "Выберите карты для замены",
"choose_dalton": "Выберите, какое оборудование сбросить",
"choose_fratelli_di_sangue": "Выберите, кому вы хотите пожертвовать одну из своих жизней",
"choose_fantasma": "Выберите, кого воскресить",
"choose_sventagliata": "Выберите другую цель для попадания с Fanning",
"choose_tornado": "Выберите карту для сброса в торнадо",
"choose_bandidos": "Выберите между сбросом 2 карт или потерей жизни (1 карта, если у вас только 1)",
"choose_poker": "Все сбрасывают 1 карту (если нет туза, игрок, который сыграл покер, выбирает 2 карты)",
"choose_from_poker;2": "Выберите карты для получения (макс. 2)",
"choose_from_poker;1": "Выберите карту для получения",
"choose_cecchino": "Выберите, в кого стрелять",
"choose_rimbalzo_player": "Выберите цель для рикошета",
"choose_rimbalzo_card": "Выберите карту для сброса рикошета",
"choose_nuova_identita": "Выберите, хотите ли вы сохранить текущего персонажа или изменить его и начать с 2 жизнями",
"choose_bicchierino": "Игрок по вашему выбору восстанавливает 1 жизнь",
"choose_bottiglia": "Выберите, как играть Бутылку!",
"choose_complice": "Выберите, как играть Подельника!",
"choose_ricercato": "Выберите, против кого играть Разыскивается.",
"choose_birra_function": "Выберите между получением 1 золотого самородка, сбросив пиво, или игрой пива.",
"choose_play_as_bang": "Выберите, какую карту сыграть как Bang!",
"choose_flint_special": "Выберите, какую карту обменять.",
"emporio_others": "{0} выбирает, какую карту получить из Общего магазина",
"you_died": "ВЫ УМЕРЛИ",
"spectate": "НАБЛЮДАТЬ",
"you_win": "ВЫ ВЫИГРАЛИ",
"you_lose": "ВЫ ПРОИГРАЛИ",
"special_ability": "ОСОБАЯ СПОСОБНОСТЬ",
"gold_rush_discard": "Сбросьте оборудование золотой лихорадки другого игрока (уплачивая цену + 1)",
"gold_rush_discard_2": "Сбросьте оборудование золотой лихорадки другого игрока (уплачивая цену + 1)",
"discard": "СБРОСИТЬ",
"to_regain_1_hp": "ВОССТАНОВИТЬ 1 ЖИЗНЬ",
"play_your_turn": "СЫГРАЙТЕ СВОЙ ХОД",
"you_are": "Вы",
"did_pick_as": "выбрал это как вторую карту",
"blackjack_special": "Если карта - бубны или черви, он берет еще одну карту.",
"choose_scarp_card_to": "ВЫБЕРИТЕ КАРТУ ДЛЯ СБРОСА, ЧТОБЫ ИСПОЛЬЗОВАТЬ",
"pick_a_card": "ПЕРЕВЕРНИТЕ КАРТУ",
"to_defend_from": "ЗАЩИЩАЙТЕСЬ ОТ",
"submit": "Отправить",
"copy": "Скопировать приглашение",
"no_players_in_range": "Вы не видите других игроков, экипируйте оружие или прицел!",
"cantplaycard": "Вы не можете играть эту карту таким образом!",
"chat": {
"spectators": " | Один зритель смотрит игру | {n} зрителей смотрят игру",
"chat": "Чат",
"joined": "👋 ;{0}; присоединился к лобби",
"died": "☠️ ;{0}; умер",
"died_role": "☠️ ;{0}; был ;{1};!",
"won": "🏆 ;{0}; выиграл! Их роль была ;{1};",
"choose_character": ";{0}; имеет ;{1}; как персонажа, его особая способность: ;{2};!",
"starting": "Игра начинается!",
"sheriff": "⭐ ;{0}; шериф!",
"did_choose_character": ";{0}; выбрал персонажа.",
"turn": "⏩ Сейчас ход ;{0};.",
"draw_from_scrap": ";{0}; взял первую карту из кучи сброса.",
"draw_from_player": ";{0}; взял первую карту из руки ;{1};.",
"flipped": ";{0}; перевернул ;{1}; ;{2};.",
"scrapped": ";{0}; сбросил ;{1}; ;{2};.",
"explode": "💥 ;{0}; взорвал динамит.",
"beer_save": ";{0}; использовал пиво, чтобы спасти свою жизнь.",
"get_nugget": ";{0}; получил золотой самородок, используя Пиво.",
"play_card": ";{0}; сыграл ;{1};.",
"play_card_green": ";{0}; ввел в игру ;{1};.",
"play_card_with": ";{0}; сыграл ;{1};, сбросив ;{2};.",
"purchase_card": ";{0}; купил ;{1};.",
"play_card_against": ";{0}; сыграл ;{1}; против ;{2};.",
"play_card_against_with": ";{0}; сыграл ;{1}; против ;{2};, сбросив ;{3};.",
"play_card_for": ";{0}; сыграл ;{1}; для ;{2};.",
"spilled_beer": ";{0}; пролил ;{1};.",
"diligenza": ";{0}; сыграл ;{1}; и взял 2 карты.",
"wellsfargo": ";{0}; сыграл ;{1}; и взял 3 карты.",
"saloon": "🍻 ;{0}; сыграл ;{1}; и исцелил 1 HP всем живым.",
"special_bart_cassidy": ";{0}; получил компенсацию, потому что был ранен.",
"special_el_gringo": ";{0}; украл карту у ;{1};, когда был ранен.",
"special_calamity": ";{0}; сыграл ;{1}; как Bang! против ;{2};.",
"allroles3": "В игре есть: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};.",
"allroles4": "В игре есть: ;{1}; ;{0};, ;{3}; ;{2};, ;{5}; ;{4};, ;{7}; ;{6};.",
"guess": "🤔 ;{0}; угадывает ;{1};.",
"guess_right": ";{0}; был прав.",
"guess_wrong": ";{0}; ошибся.",
"fratelli_sangue": ";{0}; дал одну из своих жизней ;{1};.",
"doctor_heal": ";{0}; был исцелен доктором.",
"respond": "↩️ ;{0}; ответил ;{1};.",
"change_username": "✏️ ;{0}; теперь ;{1};.",
"lobby_reset": "Возвращение в лобби через ;{0}; секунд...",
"prison_free": "🆓 ;{0}; вышел из тюрьмы",
"prison_turn": "🔐 ;{0}; остался в тюрьме в этот ход",
"flip_event": "🎴 СОБЫТИЕ: ;{0}; 🎴",
"choose_manette": ";{0}; обязался играть только карты масти ;{1}; в этом ходу.",
"UnionPacific": ";{0}; сыграл Union Pacific и взял 4 карты из колоды",
"use_special": ";{0}; использовал особую способность своего персонажа (;{1};)",
"gold_rush_pay_discard": ";{0}; сбросил ;{2}; у ;{1};.",
"choose_emporio": ";{0}; выбрал ;{1}; из Общего магазина.",
"shotgun_scrap": "Когда дробовик попал в ;{0};, карта улетела из его руки (;{1};)",
"taglia_reward": "💰 ;{1}; получил карту за награду за ;{0};",
"snake_bit": "🐍 ;{0}; был укушен гремучей змеей."
},
"foc": {
"leggedelwest": "Он должен сыграть эту карту в этот ход, если возможно."
},
"mods": "Модификаторы",
"bots": "Боты",
"add_bot": "Добавить бота",
"remove_bot": "Удалить бота",
"minimum_players": "Для начала игры нужно как минимум 3 игрока",
"mod_comp": "Соревновательный режим (отключает автоматическое получение урона)",
"disconnect_bot": "Заменить отключившихся игроков ботами",
"your_turn": "Сыграйте свой ход!",
"your_response": "Ответьте!",
"your_choose": "Выберите карту!",
"cards": {
"Barile": {
"name": "Бочка",
"desc": "Когда кто-то играет Bang против вас. Вы можете перевернуть первую карту из колоды, если масть - червы, то это считается картой Промах"
},
"Dinamite": {
"name": "Динамит",
"desc": "Когда играете Динамит, положите его перед собой, он будет безвреден в течение всего раунда. В начале следующего хода перед взятием карт и перед любым переворотом карты (например, Тюрьма), переверните карту с верха колоды. Если карта между 2 и 9 пиками (включительно), динамит взрывается: вы теряете 3 жизни и сбрасываете карту, иначе передайте динамит следующему игроку, который возьмет карту после окончания вашего хода"
},
"Mirino": {
"name": "Прицел",
"desc": "Вы видите других игроков на расстоянии -1"
},
"Mustang": {
"name": "Мустанг",
"desc": "Другие игроки видят вас на расстоянии +1"
},
"Prigione": {
"name": "Тюрьма",
"desc": "Экипируйте эту карту другому игроку, кроме шерифа. Выбранный игрок в начале своего хода должен перевернуть карту перед взятием: если это червы, сбросьте эту карту и играйте ход нормально, иначе сбросьте эту карту и пропустите ход"
},
"Remington": {
"name": "Ремингтон",
"desc": "Вы можете стрелять в другого игрока на расстоянии 3 или меньше"
},
"Rev Carabine": {
"name": "Карабин Рев.",
"desc": "Вы можете стрелять в другого игрока на расстоянии 4 или меньше"
},
"Schofield": {
"name": "Шофилд",
"desc": "Вы можете стрелять в другого игрока на расстоянии 2 или меньше"
},
"Volcanic": {
"name": "Вулканик",
"desc": "Вы можете стрелять в другого игрока на расстоянии 1 или меньше, однако у вас больше нет ограничения в 1 Bang"
},
"Winchester": {
"name": "Винчестер",
"desc": "Вы можете стрелять в другого игрока на расстоянии 5 или меньше"
},
"Bang!": {
"name": "Bang!",
"desc": "Стреляйте в игрока на виду. Если у вас нет оружия, ваша дальность - 1"
},
"Birra": {
"name": "Пиво",
"desc": "Сыграйте эту карту, чтобы восстановить одно очко жизни. Вы не можете исцелиться более чем на максимальный предел вашего персонажа. Если вы собираетесь потерять свою последнюю жизнь, вы также можете сыграть эту карту в ход вашего противника. Пиво больше не действует, если осталось только два игрока"
},
"Cat Balou": {
"name": "Кэт Баллоу",
"desc": "Выберите и сбросьте карту любого другого игрока."
},
"Diligenza": {
"name": "Дилижанс",
"desc": "Возьмите 2 карты из колоды."
},
"Duello": {
"name": "Дуэль",
"desc": "Сыграйте эту карту против любого игрока. По очереди, начиная с вашего противника, вы можете сбросить карту Bang!, первый игрок, который этого не сделает, теряет 1 жизнь."
},
"Emporio": {
"name": "Общий магазин",
"desc": "Положите на стол N карт из колоды, где N - количество живых игроков, по очереди, начиная с вас, выберите карту и добавьте ее в свою руку"
},
"Gatling": {
"name": "Гатлинг",
"desc": "Стреляйте во всех остальных игроков"
},
"Indiani!": {
"name": "Индейцы!",
"desc": "Все остальные игроки должны сбросить карту Bang! или потерять 1 очко здоровья"
},
"Mancato!": {
"name": "Промах!",
"desc": "Используйте эту карту, чтобы отменить эффект Bang"
},
"Panico!": {
"name": "Паника!",
"desc": "Украдите карту у игрока на расстоянии 1"
},
"Saloon": {
"name": "Салун",
"desc": "Все восстанавливают 1 очко здоровья"
},
"WellsFargo": {
"name": "Веллс Фарго",
"desc": "Возьмите 3 карты из колоды"
},
"Binocolo": {
"name": "Бинокль",
"desc": "Вы видите других игроков на расстоянии -1"
},
"Riparo": {
"name": "Укрытие",
"desc": "Другие игроки видят вас на расстоянии +1"
},
"Pugno!": {
"name": "Удар!",
"desc": "Стреляйте в игрока на расстоянии 1"
},
"Rag Time": {
"name": "Регтайм",
"desc": "Украдите карту у другого игрока на любом расстоянии"
},
"Rissa": {
"name": "Драка",
"desc": "Выберите карту для сброса из руки/оборудования всех остальных игроков"
},
"Schivata": {
"name": "Уклонение",
"desc": "Используйте эту карту, чтобы отменить эффект Bang и затем взять карту."
},
"Springfield": {
"name": "Спрингфилд",
"desc": "Стреляйте в игрока на любом расстоянии"
},
"Tequila": {
"name": "Текила",
"desc": "Исцелите 1 HP игроку по вашему выбору (можете быть вы)"
},
"Whisky": {
"name": "Виски",
"desc": "Исцелите 2 HP"
},
"Bibbia": {
"name": "Библия",
"desc": "Используйте эту карту, чтобы отменить эффект Bang и затем взять карту."
},
"Cappello": {
"name": "Шляпа Десяти Галлонов",
"desc": "Используйте эту карту, чтобы отменить эффект Bang"
},
"Placca Di Ferro": {
"name": "Железная Пластина",
"desc": "Используйте эту карту, чтобы отменить эффект Bang"
},
"Sombrero": {
"name": "Сомбреро",
"desc": "Используйте эту карту, чтобы отменить эффект Bang"
},
"Pugnale": {
"name": "Нож",
"desc": "Стреляйте в игрока на расстоянии 1"
},
"Derringer": {
"name": "Дерринджер",
"desc": "Стреляйте в игрока на расстоянии 1 и затем возьмите карту."
},
"Borraccia": {
"name": "Фляга",
"desc": "Восстановите 1 HP"
},
"Can Can": {
"name": "Канкан",
"desc": "Выберите и сбросьте карту любого другого игрока."
},
"Conestoga": {
"name": "Конесто",
"desc": "Украдите карту у другого игрока на любом расстоянии"
},
"Fucile Da Caccia": {
"name": "Охотничий Ружьё",
"desc": "Стреляйте в игрока на любом расстоянии"
},
"Pony Express": {
"name": "Пони Экспресс",
"desc": "Возьмите 3 карты из колоды"
},
"Pepperbox": {
"name": "Пеппербокс",
"desc": "Стреляйте в игрока на виду. Если у вас нет оружия, ваша дальность - 1"
},
"Howitzer": {
"name": "Гаубица",
"desc": "Стреляйте во всех остальных игроков"
},
"Bart Cassidy": {
"name": "Барт Кэссиди",
"desc": "Каждый раз, когда он ранен, он берет карту."
},
"Black Jack": {
"name": "Блэк Джек",
"desc": "В начале своего хода, когда он должен взять карту, он показывает всем вторую карту, если это червы или бубны, он берет третью карту, не показывая ее."
},
"Calamity Janet": {
"name": "Каламити Джанет",
"desc": "Она может использовать карты Промах! как Bang! и наоборот."
},
"El Gringo": {
"name": "Эль Гринго",
"desc": "Каждый раз, когда он ранен, он берет карту из руки атакующего игрока."
},
"Jesse Jones": {
"name": "Джесси Джонс",
"desc": "Когда он должен взять свои карты, он может взять первую карту из руки другого игрока. (нажмите на карты вражеского игрока, если хотите использовать эту способность)"
},
"Jourdonnais": {
"name": "Журдонэ",
"desc": "Он играет, как если бы у него всегда была активная Бочка, если он экипирует другую Бочку, он может перевернуть 2 карты."
},
"Kit Carlson": {
"name": "Кит Карлсон",
"desc": "Когда он должен взять карту, он смотрит 3 карты и выбирает 2, положив другую карту на верх колоды."
},
"Lucky Duke": {
"name": "Счастливчик Дюк",
"desc": "Каждый раз, когда он должен перевернуть карту, он может перевернуть 2 раза."
},
"Paul Regret": {
"name": "Пол Регрет",
"desc": "Другие игроки видят его на расстоянии +1."
},
"Pedro Ramirez": {
"name": "Педро Рамирес",
"desc": "Когда он должен взять карту, он может взять первую карту из кучи сброса. (нажмите на карты в куче сброса, чтобы использовать эту способность)"
},
"Rose Doolan": {
"name": "Роуз Дулан",
"desc": "Она видит других игроков на расстоянии -1."
},
"Sid Ketchum": {
"name": "Сид Кетчум",
"desc": "Он может сбросить 2 карты, чтобы восстановить 1 HP."
},
"Slab The Killer": {
"name": "Слэб Убийца",
"desc": "Чтобы уклониться от его Bang! карт, другим игрокам нужны 2 Промах!"
},
"Suzy Lafayette": {
"name": "Сьюзи Лафайет",
"desc": "Каждый раз, когда у нее пустая рука, она берет карту."
},
"Vulture Sam": {
"name": "Волчер Сэм",
"desc": "Когда игрок умирает, он получает все карты из руки и экипировку умершего."
},
"Willy The Kid": {
"name": "Вилли Кид",
"desc": "У него нет ограничений на количество Bang, которые он может использовать."
},
"Pixie Pete": {
"name": "Пикси Пит",
"desc": "Он берет 3 карты вместо 2."
},
"Tequila Joe": {
"name": "Текила Джо",
"desc": "Когда он играет Пиво, он восстанавливает 2 HP."
},
"Greg Digger": {
"name": "Грег Диггер",
"desc": "Каждый раз, когда игрок умирает, он восстанавливает до 2 жизней."
},
"Herb Hunter": {
"name": "Херб Хантер",
"desc": "Каждый раз, когда игрок умирает, он берет 2 карты."
},
"Elena Fuente": {
"name": "Елена Фуэнте",
"desc": "Она может использовать любую карту из своей руки как Промах."
},
"Bill Noface": {
"name": "Билл Безликий",
"desc": "Он берет 1 карту + 1 карту за каждую рану, которую он имеет."
},
"Molly Stark": {
"name": "Молли Старк",
"desc": "Когда она использует карту из своей руки вне своего хода, она берет карту."
},
"Apache Kid": {
"name": "Апачи Кид",
"desc": "Карты бубен ♦️, сыгранные против него, не имеют эффекта (не работает в дуэлях)."
},
"Sean Mallory": {
"name": "Шон Мэллори",
"desc": "Он может держать до 10 карт в руке, заканчивая ход."
},
"Belle Star": {
"name": "Белль Стар",
"desc": "Во время ее хода зеленые и синие карты других игроков не работают."
},
"Vera Custer": {
"name": "Вера Кастер",
"desc": "Перед взятием карт она может выбрать особую способность другого живого игрока. Эта способность используется до следующего хода."
},
"Chuck Wengam": {
"name": "Чак Венгам",
"desc": "Во время своего хода он может решить потерять 1 HP, чтобы взять 2 карты из колоды."
},
"Pat Brennan": {
"name": "Пэт Бреннан",
"desc": "Вместо того чтобы брать карты, он может украсть карту из экипировки другого игрока. (нажмите на вражеского игрока, если хотите использовать эту способность)"
},
"José Delgado": {
"name": "Хосе Дельгадо",
"desc": "Во время своего хода он может сбросить синюю карту, чтобы взять 2 карты, до двух раз за ход."
},
"Doc Holyday": {
"name": "Док Холидей",
"desc": "Он может сбросить 2 карты, чтобы сыграть Bang."
},
"Fuorilegge": {
"name": "Бандит"
},
"Rinnegato": {
"name": "Ренегат"
},
"Sceriffo": {
"name": "Шериф"
},
"Vice": {
"name": "Помощник"
},
"Miniera Abbandonata": {
"name": "Заброшенная Шахта",
"desc": "Игроки берут карты из кучи сброса в фазе 1 и сбрасывают на верх колоды в фазе 3 (если куча сброса заканчивается, они должны брать и сбрасывать на верх колоды)"
},
"Il Giudice": {
"name": "Судья",
"desc": "Вы не можете экипировать карты на себя или других игроков"
},
"Agguato": {
"name": "Засада",
"desc": "Базовое расстояние между двумя игроками - 1"
},
"Rimbalzo": {
"name": "Рикошет",
"desc": "Игрок может играть Bang против карт, экипированных другими игроками, если они не играют Промах, они сбрасываются (нажмите на карту события)"
},
"Cecchino": {
"name": "Снайпер",
"desc": "Во время своего хода игроки могут сбросить 2 Bang!, чтобы стрелять Bang!, который требует 2 Промах (нажмите на карту)"
},
"Lazo": {
"name": "Лассо",
"desc": "Карты в слоте экипировки не работают"
},
"Ranch": {
"name": "Ранчо",
"desc": "После взятия карт игрок может сбросить любое количество карт из руки и взять столько же из колоды"
},
"Dead Man": {
"name": "Мертвец",
"desc": "Первый игрок, который умрет, вернется к жизни с 2 HP и 2 картами"
},
"Liquore Forte": {
"name": "Крепкий Ликёр",
"desc": "Игроки могут пропустить взятие карт, чтобы восстановить 1 HP (нажмите на карту события, чтобы использовать)"
},
"Vendetta": {
"name": "Месть",
"desc": "Заканчивая ход, игрок переворачивает карту из колоды, если это ❤️, он играет еще один ход (но не переворачивает другую карту)"
},
"Roulette Russa": {
"name": "Русская Рулетка",
"desc": "Начиная с шерифа, каждый игрок сбрасывает 1 Промах, первый, кто этого не сделает, теряет 2 HP"
},
"Legge Del West": {
"name": "Закон Запада",
"desc": "Каждый игрок показывает вторую карту, которую он берет, и должен использовать ее в этом раунде (если это возможно)"
},
"Peyote": {
"name": "Пейот",
"desc": "Вместо взятия карт игрок пытается угадать цвет масти, если он прав, добавляет карту в руку и продолжает пытаться угадать следующую карту"
},
"Fratelli Di Sangue": {
"name": "Кровные Братья",
"desc": "В начале своего хода игроки могут потерять 1 HP (кроме последнего), чтобы отдать его другому игроку"
},
"I Dalton": {
"name": "Дальтоны",
"desc": "Игроки, у которых экипированы синие карты, сбрасывают одну из этих карт по своему выбору"
},
"Sermone": {
"name": "Проповедь",
"desc": "Игроки не могут играть Bang! во время своего хода"
},
"Città Fantasma": {
"name": "Город Призрак",
"desc": "Все умершие игроки возвращаются к жизни в свой ход, они не могут умереть и берут 3 карты вместо 2. Когда они заканчивают ход, они умирают."
},
"Il Reverendo": {
"name": "Преподобный",
"desc": "Пиво не может быть сыграно"
},
"Sbornia": {
"name": "Похмелье",
"desc": "Персонажи теряют свои особые способности"
},
"Il Dottore": {
"name": "Доктор",
"desc": "Игрок с наименьшим количеством HP получает 1 HP"
},
"Corsa All Oro": {
"name": "Золотая Лихорадка",
"desc": "Ходы играются против часовой стрелки"
},
"Maledizione": {
"name": "Проклятие",
"desc": "Все карты - пики ♠️"
},
"Sparatoria": {
"name": "Перестрелка",
"desc": "Лимит Bang! за ход - 2"
},
"Benedizione": {
"name": "Благословение",
"desc": "Все карты - червы ❤️"
},
"Il Treno": {
"name": "Прибытие Поезда",
"desc": "Игроки берут 1 дополнительную карту"
},
"Sete": {
"name": "Жажда",
"desc": "Игроки берут только 1 карту в начале своего хода"
},
"Nuova Identita": {
"name": "Новая Личность",
"desc": "В начале своего хода каждый игрок может выбрать сменить персонажа на другого, показанного в начале игры. Если он это делает, он начинает с 2 HP"
},
"Manette": {
"name": "Наручники",
"desc": "После взятия карт в фазе 1 игрок объявляет масть. Он сможет использовать только карты этой масти в этом ходу"
},
"Mezzogiorno di Fuoco": {
"name": "Полдень",
"desc": "Каждый игрок теряет 1 HP, когда начинается его ход"
},
"Per Un Pugno Di Carte": {
"name": "За Горсть Карт",
"desc": "В начале своего хода игрок становится целью стольких Bang, сколько карт у него в руке"
},
"Pepita": {
"name": "Золотой Самородок"
},
"Bicchierino": {
"name": "Стаканчик",
"desc": "Вы можете выбрать игрока, который восстанавливает 1 HP (даже себя)"
},
"Bottiglia": {
"name": "Бутылка",
"desc": "Можно сыграть как Паника!, Пиво или Bang!"
},
"Complice": {
"name": "Подельник",
"desc": "Можно сыграть как Общий магазин, Дуэль или Кэт Баллоу."
},
"Corsa All Oro_gr": {
"name": "Золотая Лихорадка",
"desc": "Вы заканчиваете ход, восстанавливаете все HP и начинаете новый ход."
},
"Rum": {
"name": "Ром",
"desc": "Переверните 4 карты, за каждую масть вы получаете 1 HP."
},
"Union Pacific": {
"name": "Union Pacific",
"desc": "Возьмите 4 карты из колоды."
},
"Calumet": {
"name": "Калюмет",
"desc": "Карты бубен, сыгранные другими игроками, не имеют на вас эффекта."
},
"Cinturone": {
"name": "Ремень",
"desc": "Вы можете держать до 8 карт, когда заканчиваете ход."
},
"Ferro di Cavallo": {
"name": "Подкова",
"desc": "Каждый раз, когда вы должны перевернуть карту, переворачивайте дважды!"
},
"Piccone": {
"name": "Кирка",
"desc": "Вы получаете 1 карту больше, когда берете в начале хода."
},
"Ricercato": {
"name": "Разыскивается",
"desc": "Сыграйте на другого игрока, кто убьет цель, получит 2 дополнительные карты и 1 золотой самородок."
},
"Setaccio": {
"name": "Золотодобытчик",
"desc": "Заплатите 1 золотой самородок, чтобы взять 1 карту из колоды, до двух раз за ход. (Нажмите на экипированную карту, чтобы использовать)"
},
"Stivali": {
"name": "Сапоги",
"desc": "Каждый раз, когда вы теряете 1 HP, вы берете 1 карту из колоды."
},
"Talismano": {
"name": "Амулет Удачи",
"desc": "Каждый раз, когда вы теряете 1 HP, вы получаете 1 золотой самородок."
},
"Zaino": {
"name": "Рюкзак",
"desc": "Заплатите 2 золотых самородка, чтобы исцелить 1 HP. (Нажмите на экипированную карту, чтобы использовать)"
},
"Don Bell": {
"name": "Дон Белл",
"desc": "Когда он заканчивает ход, он переворачивает карту, если это Червы ❤️ или Бубны ♦️, он играет снова."
},
"Dutch Will": {
"name": "Датч Вилл",
"desc": "Он берет 2 карты, сбрасывает 1 и берет 1 золотой самородок."
},
"Jacky Murieta": {
"name": "Джекки Мурьета",
"desc": "Во время своего хода он может заплатить 2 золотых самородка, чтобы иметь возможность стрелять еще раз!"
},
"Josh McCloud": {
"name": "Джош МакКлауд",
"desc": "Он может заплатить 2 золотых самородка, чтобы получить первую карту из колоды золотой лихорадки."
},
"Madam Yto": {
"name": "Мадам Ито",
"desc": "Она берет 1 карту из колоды каждый раз, когда играется Пиво."
},
"Pretty Luzena": {
"name": "Красотка Лузена",
"desc": "Один раз за ход она может получить скидку 1 на магазине карт золотой лихорадки."
},
"Raddie Snake": {
"name": "Рэдди Снейк",
"desc": "Он может выбросить 1 золотой самородок, чтобы взять 1 карту из колоды во время своего хода (максимум 2 раза за ход)."
},
"Simeon Picos": {
"name": "Симеон Пикос",
"desc": "Он получает 1 золотой самородок каждый раз, когда теряет 1 HP."
},
"Fantasma": {
"name": "Призрак",
"desc": "Сыграйте на любом выбывшем игроке: этот игрок возвращается в игру, но не может получить или потерять очки жизни."
},
"Lemat": {
"name": "Лемат",
"desc": "Во время своего хода вы можете использовать любую карту как Bang! (Нажмите на карту после экипировки)."
},
"SerpenteASonagli": {
"name": "Гремучая Змея",
"desc": "Сыграйте на любом игроке. В начале своего хода, если этот игрок берет в Пики, он теряет 1 HP."
},
"Shotgun": {
"name": "Дробовик",
"desc": "Каждый раз, когда вы раните игрока, он должен сбросить карту из руки."
},
"Taglia": {
"name": "Награда",
"desc": "Сыграйте на любом игроке. Если этот игрок попадает под Bang!, тот, кто стрелял, получает карту из колоды."
},
"Mira": {
"name": "Прицел",
"desc": "Сыграйте эту карту вместе с Bang!. Если цель поражена, она теряет 2 HP."
},
"RitornoDiFiamma": {
"name": "Отдача",
"desc": "Считается как Промах!. Игрок, который стрелял, становится целью Bang!."
},
"Bandidos": {
"name": "Бандитос",
"desc": "Каждый игрок выбирает между сбросом 2 карт из руки (или 1, если у него только 1) или потерей 1 HP."
},
"Fuga": {
"name": "Побег",
"desc": "Может быть сыграно вне хода. Избегает эффекта коричневой карты (не Bang!), направленной на вас."
},
"Sventagliata": {
"name": "Стрельба В Ряд",
"desc": "Считается единственным Bang! за ход. Игрок по вашему выбору на расстоянии 1 от цели (если есть, кроме вас) также становится целью Bang."
},
"UltimoGiro": {
"name": "Последний Вздох",
"desc": "Восстанавливает 1 HP"
},
"Poker": {
"name": "Покер",
"desc": "Все остальные игроки сбрасывают карту из руки одновременно. Если нет тузов, возьмите до 2 из этих карт."
},
"Salvo": {
"name": "Спасение!",
"desc": "Может быть сыграно вне хода. Предотвращает потерю 1 HP другим игроком. Если он выживает, возьмите 2 карты из его руки или из колоды (по вашему выбору)."
},
"Tomahawk": {
"name": "Томагавк",
"desc": "Атакует игрока на расстоянии до 2."
},
"Tornado": {
"name": "Торнадо",
"desc": "Все сбрасывают карту из руки (если возможно), затем берут 2 карты из колоды."
},
"Black Flower": {
"name": "Черный Цветок",
"desc": "Один раз за ход вы можете использовать карту Пик, чтобы стрелять еще раз Bang!"
},
"Colorado Bill": {
"name": "Колорадо Билл",
"desc": "Каждый раз, когда вы играете карту Bang!, если вы берете Пики, выстрел не может быть уклонен."
},
"Der Spot Burst Ringer": {
"name": "Дер Спот Бурст Рингер",
"desc": "Один раз за ход вы можете использовать карту Bang! как Гатлинг."
},
"Evelyn Shebang": {
"name": "Эвелин Шебанг",
"desc": "Вы можете выбрать не брать карты во время фазы взятия. За каждую не взятую карту вы стреляете Bang! в достижимую цель, в разных целях."
},
"Henry Block": {
"name": "Генри Блок",
"desc": "Любой, кто берет или сбрасывает одну из ваших карт в игре или в руке, становится целью Bang!."
},
"Lemonade Jim": {
"name": "Лимонад Джим",
"desc": "Каждый раз, когда другой игрок играет Пиво, вы можете сбросить карту из руки, чтобы также восстановить 1 HP."
},
"Mick Defender": {
"name": "Мик Защитник",
"desc": "Если вы становитесь целью коричневой карты (не Bang!), вы можете использовать карту Промах!, чтобы избежать 1 эффекта."
},
"Tuco Franziskaner": {
"name": "Туко Францисканер",
"desc": "Если у вас нет экипированных синих карт, вы берете 2 дополнительные карты."
},
"Big Spencer": {
"name": "Большой Спенсер",
"desc": "Начинает с 5 картами. Не может играть Промах!"
},
"Flint Westwood": {
"name": "Флинт Вествуд",
"desc": "Он может обменять карту из своей руки на 2 случайные карты из руки другого игрока во время своего хода."
},
"Gary Looter": {
"name": "Гэри Лутер",
"desc": "Он берет все избыточные карты, сброшенные другими игроками в конце каждого хода."
},
"Greygory Deckard": {
"name": "Грейгори Декард",
"desc": "В начале своего хода он может взять 2 случайных персонажа и получает все их способности."
},
"John Pain": {
"name": "Джон Пейн",
"desc": "Если у него меньше 6 карт в руке, когда игрок \"берет!\", Джон добавляет взятую карту в свою руку."
},
"Lee Van Kliff": {
"name": "Ли Ван Клифф",
"desc": "Во время своего хода он может сбросить карту Bang!, чтобы повторить эффект коричневой карты, которую он только что сыграл."
},
"Teren Kill": {
"name": "Терен Килл",
"desc": "Каждый раз, когда он должен быть устранен, \"берет!\": если это не Пики, Терен остается с 1 HP и берет 1 карту."
},
"Youl Grinner": {
"name": "Юл Гриннер",
"desc": "Перед взятием карт игроки с большим количеством карт в руке, чем у Юла, должны дать ему карту по своему выбору."
},
"Camposanto": {
"name": "Кладбище",
"desc": "В начале своего хода каждый устраненный игрок возвращается в игру с 1 HP. Возьмите карту роли случайным образом из устраненных игроков."
},
"Darling Valentine": {
"name": "Дарлинг Валентайн",
"desc": "В начале своего хода каждый игрок сбрасывает свою руку и берет столько же карт из колоды."
},
"Dorothy Rage": {
"name": "Дороти Рейдж",
"desc": "Во время своего хода каждый игрок может заставить другого игрока сыграть карту."
},
"Helena Zontero": {
"name": "Хелена Зонтеро",
"desc": "Когда Хелена вступает в игру, \"берет!\": если берутся Червы или Бубны, перетасуйте активные карты ролей, кроме шерифа, и перераспределите их случайным образом."
},
"Lady Rosa del Texas": {
"name": "Леди Роза из Техаса",
"desc": "Во время своего хода каждый игрок может поменяться местами с игроком справа, который пропускает свой следующий ход. (Нажмите на карту)"
},
"Miss Susanna": {
"name": "Мисс Сюзанна",
"desc": "Во время своего хода каждый игрок должен сыграть как минимум 3 карты. Если они этого не сделают, они теряют 1 HP."
},
"Regolamento di Conti": {
"name": "Сворачивание Счетов",
"desc": "Все карты могут быть сыграны, как если бы они были Bang!. Карты Bang! могут быть сыграны, как если бы они были Промах! (Нажмите на карту)"
},
"Sacagaway": {
"name": "Сакаджавея",
"desc": "Все игроки играют с картами лицом вверх (кроме карты роли!)."
},
"Wild West Show": {
"name": "Шоу Дикого Запада",
"desc": "Цель каждого игрока становится: \"Будь последним стоящим!\""
}
},
"help": {
"character": "Персонажи",
"characters_special": "У каждого персонажа есть особые способности и количество жизней, которые делают их уникальными. \nЖизни - это количество очков жизни, которые вы можете потерять до смерти, а также указывают на максимальное количество карт, которые вы можете держать в руке.",
"deathnobeer": "Когда вы теряете последнее очко жизни и у вас нет пива 🍺️ в руке, вы умираете. \nВаши карты сбрасываются, и ваша роль раскрывается всем.",
"discard": "Сбросить",
"distance": "Расстояние",
"distancecalc": "Расстояние автоматически рассчитывается игрой и соответствует минимальному расстоянию между игроками слева и справа.",
"drawinstructions": "Чтобы взять карты, вам нужно нажать на колоду, когда вы видите эту анимацию.",
"drawthecards": "Возьмите карты",
"endgame": "Конец игры",
"endgameconditions": "Игра заканчивается, когда выполняется одно из следующих условий:",
"endgamesheriffwin": "Все бандиты 🐺️ и ренегаты 🦅️ мертвы. \nВ этом случае шериф ⭐️ и помощники 🎖️ выигрывают.",
"endgameshriffdeath": "Шериф ⭐️ умирает. \nЕсли ренегат 🦅️ - последний оставшийся в живых игрок, он выигрывает, иначе выигрывают бандиты.",
"endingturn": "Когда вы закончили играть свои карты, то есть, когда вы не хотите или не можете играть больше карт, вы должны сбросить карты, превышающие ваше текущее количество жизней.\n\nЗатем вы передаете ход следующему игроку, нажав на конец хода.",
"equipment": "ОБОРУДОВАНИЕ",
"justoneweapon": "Вы можете иметь только одно оружие экипировано.",
"maxtwocardsequip": "Вы не можете иметь 2 карты с одинаковым названием экипированы.",
"outlawreward": "Кто убьет бандита 🐺️ берет 3 карты из колоды (другие бандиты тоже 🐺️).",
"playerdeath": "Смерть игрока",
"playingcards": "Играйте карты",
"playingdmg": "Вы можете играть свои карты для себя или для нанесения вреда другим игрокам, пытаясь их устранить.",
"playingduringturn": "Вы можете играть карты только в свой ход. Чтобы играть карты, нажмите на карты из вашей руки.\nЗа исключением карт, используемых как ответ, таких как Промах 😅️.",
"playingifyouwant": "Вы не обязаны играть карты.",
"playlimit": "Существуют только 3 ограничения:",
"playonlyonebang": "Вы можете играть только 1 Bang! \nза ход (относится только к картам, названным Bang!)",
"rewardspen": "Наказания и награды",
"roles": "Роли",
"sheriffkillsvice": "Если шериф ⭐️ убивает помощника, он теряет все карты в руке и в игре перед ним.",
"thecards": "Карты",
"title": "Как играть",
"turndiscard": "Сбросьте лишние карты",
"turndraw": "Возьмите 2 карты",
"turnplay": "Играйте любое количество карт",
"turns": "Ходы",
"turnstart": "Всегда начинается с шерифа ⭐️, и игра продолжается по часовой стрелке, ходы делятся на 3 фазы.",
"weapon": "ОРУЖИЕ",
"renegade": "Ренегат",
"vice": "Помощник",
"outlaw": "Бандит",
"sheriff": "Шериф",
"allcharacters": "Все персонажи",
"gotoallcharacters": "Перейти ко всем персонажам",
"gotocards": "Перейти к картам",
"gotohighnoon": "Перейти к Полдню",
"gotofoc": "Перейти к Горсти Карт",
"gotogoldrush": "Перейти к Золотой Лихорадке",
"highnooncards": "Полдень - Карты Событий",
"foccards": "Горсть Карт - Карты Событий",
"goldrushcards": "Карты Золотой Лихорадки",
"valleyofshadowscards": "Карты Долины Теней"
},
"theme": {
"sepia": "Сепия",
"light": "Светлый",
"dark": "Темный",
"grayscale": "Черно-белый",
"black": "Черный"
}
}

View File

@ -5,5 +5,4 @@ export const emojiMap = {
'fistful_of_cards': '🎴',
'the_valley_of_shadows': '👻',
'wild_west_show': '🎪',
'train_robbery': '🚂',
}

View File

@ -28,19 +28,13 @@ export const expansionsMap = {
icon: '👻',
back: true,
expansion: 'the-valley-of-shadows',
status: 'beta',
},
'wild_west_show': {
name: 'Wild West Show',
icon: '🎪',
back: true,
expansion: 'wild-west-show',
status: 'beta',
},
'train_robbery': {
name: 'The Great Train Robbery',
icon: '🚂',
back: true,
expansion: 'train-roobbery',
status: 'wip',
status: 'alpha',
}
}

File diff suppressed because it is too large Load Diff