Merge pull request #179 from albertoxamin/dev
Add gold rush and replay capabilities
This commit is contained in:
commit
ae941ba2af
35
.github/workflows/dev-image.yml
vendored
35
.github/workflows/dev-image.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: crazy-max/ghaction-docker-buildx@v3
|
uses: crazy-max/ghaction-docker-buildx@v3
|
||||||
@ -23,12 +23,32 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
-
|
-
|
||||||
name: Docker Buildx (build)
|
name: Docker Buildx (build amd64)
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
--platform linux/amd64,linux/arm/v7,linux/arm64 \
|
--platform linux/amd64 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:dev \
|
||||||
|
--file ./Dockerfile ./
|
||||||
|
-
|
||||||
|
name: Docker Buildx (build arm64)
|
||||||
|
run: |
|
||||||
|
docker buildx build \
|
||||||
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
|
--platform linux/arm64 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:dev \
|
||||||
|
--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" \
|
--output "type=image,push=false" \
|
||||||
--tag albertoxamin/bang:dev \
|
--tag albertoxamin/bang:dev \
|
||||||
--file ./Dockerfile ./
|
--file ./Dockerfile ./
|
||||||
@ -51,3 +71,12 @@ jobs:
|
|||||||
name: Inspect image
|
name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect albertoxamin/bang:dev
|
docker buildx imagetools inspect albertoxamin/bang:dev
|
||||||
|
|
||||||
|
- 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 :dev"
|
||||||
|
GITHUB_JOB_STATUS: ${{ job.status }}
|
||||||
|
29
.github/workflows/docker-image.yml
vendored
29
.github/workflows/docker-image.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: crazy-max/ghaction-docker-buildx@v3
|
uses: crazy-max/ghaction-docker-buildx@v3
|
||||||
@ -23,12 +23,22 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
-
|
-
|
||||||
name: Docker Buildx (build)
|
name: Docker Buildx (build amd64 arm64)
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
--platform linux/amd64,linux/arm/v7 \
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:latest \
|
||||||
|
--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" \
|
--output "type=image,push=false" \
|
||||||
--tag albertoxamin/bang:latest \
|
--tag albertoxamin/bang:latest \
|
||||||
--file ./Dockerfile ./
|
--file ./Dockerfile ./
|
||||||
@ -39,11 +49,11 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
-
|
-
|
||||||
name: Docker Buildx (push)
|
name: Docker Buildx (push all)
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
--platform linux/amd64,linux/arm/v7 \
|
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||||
--output "type=image,push=true" \
|
--output "type=image,push=true" \
|
||||||
--tag albertoxamin/bang:latest \
|
--tag albertoxamin/bang:latest \
|
||||||
--file ./Dockerfile ./
|
--file ./Dockerfile ./
|
||||||
@ -51,3 +61,12 @@ jobs:
|
|||||||
name: Inspect image
|
name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect albertoxamin/bang:latest
|
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 }}
|
||||||
|
37
.github/workflows/test-backend.yml
vendored
Normal file
37
.github/workflows/test-backend.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
name: Python package
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./backend
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pytest
|
||||||
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
- name: Test with pytest
|
||||||
|
run: |
|
||||||
|
python -m pytest -p no:warnings
|
||||||
|
|
||||||
|
- 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: "Backend tests"
|
||||||
|
GITHUB_JOB_STATUS: ${{ job.status }}
|
53
.github/workflows/tests.yaml
vendored
53
.github/workflows/tests.yaml
vendored
@ -1,12 +1,53 @@
|
|||||||
name: Test Pull requests
|
name: Test Pull requests
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test_build:
|
buildx:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
DOCKER_BUILDKIT: '1'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
-
|
||||||
- name: Build the Unified Docker image
|
name: Checkout
|
||||||
run: docker build . --file Dockerfile
|
uses: actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: crazy-max/ghaction-docker-buildx@v3
|
||||||
|
-
|
||||||
|
name: Cache Docker layers
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-buildx-
|
||||||
|
-
|
||||||
|
name: Docker Buildx (test build amd64)
|
||||||
|
run: |
|
||||||
|
docker buildx build \
|
||||||
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
|
--platform linux/arm64 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:test \
|
||||||
|
--file ./Dockerfile ./
|
||||||
|
-
|
||||||
|
name: Docker Buildx (test build arm64)
|
||||||
|
run: |
|
||||||
|
docker buildx build \
|
||||||
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
|
--platform linux/arm64 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:test \
|
||||||
|
--file ./Dockerfile ./
|
||||||
|
-
|
||||||
|
name: Docker Buildx (test build armv-7)
|
||||||
|
run: |
|
||||||
|
docker buildx build \
|
||||||
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
|
--platform linux/arm/v7 \
|
||||||
|
--output "type=image,push=false" \
|
||||||
|
--tag albertoxamin/bang:test \
|
||||||
|
--file ./Dockerfile ./
|
||||||
|
22
Dockerfile
22
Dockerfile
@ -1,11 +1,25 @@
|
|||||||
|
# build Vue frontend
|
||||||
FROM node:lts-alpine as builder
|
FROM node:lts-alpine as builder
|
||||||
COPY ./frontend .
|
COPY ./frontend .
|
||||||
RUN npm install
|
RUN npm install
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
FROM python:3.7.10-stretch
|
# now we should have a dist folder containing the static website
|
||||||
|
|
||||||
|
FROM python:3.7.10-stretch as pybuilder
|
||||||
|
WORKDIR /code
|
||||||
|
COPY ./backend /code/
|
||||||
|
RUN pip install --user -r requirements.txt
|
||||||
|
# We get the dependencies with the full python image so we can compile the one with missing binaries
|
||||||
|
ENV UseRobots=false
|
||||||
|
|
||||||
|
FROM python:3.7.10-slim-stretch as app
|
||||||
|
# copy the dependencies from the pybuilder
|
||||||
|
COPY --from=pybuilder /root/.local /root/.local
|
||||||
|
# copy the backend python files from the pybuilder
|
||||||
|
COPY --from=pybuilder /code /dist
|
||||||
|
# copy the frontend static files from the builder
|
||||||
COPY --from=builder ./dist /dist/
|
COPY --from=builder ./dist /dist/
|
||||||
COPY ./backend /dist/
|
|
||||||
WORKDIR /dist
|
WORKDIR /dist
|
||||||
RUN pip install -r requirements.txt
|
|
||||||
EXPOSE 5001
|
EXPOSE 5001
|
||||||
ENTRYPOINT ["python", "/dist/__init__.py"]
|
|
||||||
|
ENTRYPOINT ["python", "/dist/server.py"]
|
||||||
|
@ -1 +1,8 @@
|
|||||||
# bang
|
# bang
|
||||||
|
|
||||||
|
This is the repo for the PewPew! game, which is a replica of BANG!.
|
||||||
|
BANG! is a trademark owned by DVGiochi.
|
||||||
|
|
||||||
|
[Frontend Readme](./frontend/README.md)
|
||||||
|
|
||||||
|
[Backend Readme](./backend/Readme.md)
|
@ -1,494 +0,0 @@
|
|||||||
import os
|
|
||||||
import json
|
|
||||||
import random
|
|
||||||
from typing import List
|
|
||||||
import eventlet
|
|
||||||
import socketio
|
|
||||||
|
|
||||||
from bang.game import Game
|
|
||||||
from bang.players import Player, PendingAction
|
|
||||||
|
|
||||||
import sys
|
|
||||||
sys.setrecursionlimit(10**6) # this should prevents bots from stopping
|
|
||||||
|
|
||||||
sio = socketio.Server(cors_allowed_origins="*")
|
|
||||||
static_files={
|
|
||||||
'/': {'content_type': 'text/html', 'filename': 'index.html'},
|
|
||||||
'/game': {'content_type': 'text/html', 'filename': 'index.html'},
|
|
||||||
'/help': {'content_type': 'text/html', 'filename': 'index.html'},
|
|
||||||
'/status': {'content_type': 'text/html', 'filename': 'index.html'},
|
|
||||||
# '/robots.txt': {'content_type': 'text/html', 'filename': 'robots.txt'},
|
|
||||||
'/favicon.ico': {'filename': 'favicon.ico'},
|
|
||||||
'/img/icons': './img/icons',
|
|
||||||
'/manifest.json': {'filename': 'manifest.json'},
|
|
||||||
'/css': './css',
|
|
||||||
'/media': './media',
|
|
||||||
'/js': './js',
|
|
||||||
}
|
|
||||||
for file in [f for f in os.listdir('.') if '.js' in f or '.map' in f or '.html' in f]:
|
|
||||||
static_files[f'/{file}'] = f'./{file}'
|
|
||||||
|
|
||||||
app = socketio.WSGIApp(sio, static_files=static_files)
|
|
||||||
games: List[Game] = []
|
|
||||||
online_players = 0
|
|
||||||
blacklist: List[str] = []
|
|
||||||
|
|
||||||
def advertise_lobbies():
|
|
||||||
sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'password': g.password} for g in games if not g.started and len(g.players) < 10 and not g.is_hidden])
|
|
||||||
sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if g.started])
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def connect(sid, environ):
|
|
||||||
global online_players
|
|
||||||
online_players += 1
|
|
||||||
print('connect ', sid)
|
|
||||||
sio.enter_room(sid, 'lobby')
|
|
||||||
sio.emit('players', room='lobby', data=online_players)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_online_players(sid):
|
|
||||||
global online_players
|
|
||||||
sio.emit('players', room='lobby', data=online_players)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def set_username(sid, username):
|
|
||||||
ses = sio.get_session(sid)
|
|
||||||
if not isinstance(ses, Player):
|
|
||||||
sio.save_session(sid, Player(username, sid, sio))
|
|
||||||
print(f'{sid} is now {username}')
|
|
||||||
advertise_lobbies()
|
|
||||||
elif ses.game == None or not ses.game.started:
|
|
||||||
print(f'{sid} changed username to {username}')
|
|
||||||
prev = ses.name
|
|
||||||
if len([p for p in ses.game.players if p.name == username]) > 0:
|
|
||||||
ses.name = f"{username}_{random.randint(0,100)}"
|
|
||||||
else:
|
|
||||||
ses.name = username
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data=f'_change_username|{prev}|{ses.name}')
|
|
||||||
sio.emit('me', data=ses.name, room=sid)
|
|
||||||
ses.game.notify_room()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_me(sid, room):
|
|
||||||
if isinstance(sio.get_session(sid), Player):
|
|
||||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
|
||||||
if sio.get_session(sid).game:
|
|
||||||
sio.get_session(sid).game.notify_room()
|
|
||||||
else:
|
|
||||||
sio.save_session(sid, Player('player', sid, sio))
|
|
||||||
de_games = [g for g in games if g.name == room['name']]
|
|
||||||
if len(de_games) == 1 and not de_games[0].started:
|
|
||||||
join_room(sid, room)
|
|
||||||
elif len(de_games) == 1 and de_games[0].started:
|
|
||||||
print('room exists')
|
|
||||||
if room['username'] != None and any([p.name == room['username'] for p in de_games[0].players if p.is_bot]):
|
|
||||||
print('getting inside the bot')
|
|
||||||
bot = [p for p in de_games[0].players if p.is_bot and p.name == room['username'] ][0]
|
|
||||||
bot.sid = sid
|
|
||||||
bot.is_bot = False
|
|
||||||
sio.enter_room(sid, de_games[0].name)
|
|
||||||
sio.save_session(sid, bot)
|
|
||||||
de_games[0].notify_room(sid)
|
|
||||||
eventlet.sleep(0.1)
|
|
||||||
de_games[0].notify_all()
|
|
||||||
sio.emit('role', room=sid, data=json.dumps(bot.role, default=lambda o: o.__dict__))
|
|
||||||
bot.notify_self()
|
|
||||||
if len(bot.available_characters) > 0:
|
|
||||||
bot.set_available_character(bot.available_characters)
|
|
||||||
else: #spectate
|
|
||||||
de_games[0].spectators.append(sio.get_session(sid))
|
|
||||||
sio.get_session(sid).game = de_games[0]
|
|
||||||
sio.enter_room(sid, de_games[0].name)
|
|
||||||
de_games[0].notify_room(sid)
|
|
||||||
de_games[0].notify_event_card(sid)
|
|
||||||
de_games[0].notify_scrap_pile(sid)
|
|
||||||
de_games[0].notify_all()
|
|
||||||
de_games[0].notify_event_card()
|
|
||||||
else:
|
|
||||||
create_room(sid, room['name'])
|
|
||||||
if sio.get_session(sid).game == None:
|
|
||||||
sio.emit('me', data={'error':'Wrong password/Cannot connect'}, room=sid)
|
|
||||||
else:
|
|
||||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
|
||||||
if room['username'] == None or any([p.name == room['username'] for p in sio.get_session(sid).game.players]):
|
|
||||||
sio.emit('change_username', room=sid)
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sio.get_session(sid).game.name, data=f"_change_username|{sio.get_session(sid).name}|{room['username']}")
|
|
||||||
sio.get_session(sid).name = room['username']
|
|
||||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
|
||||||
if not sio.get_session(sid).game.started:
|
|
||||||
sio.get_session(sid).game.notify_room()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def disconnect(sid):
|
|
||||||
global online_players
|
|
||||||
online_players -= 1
|
|
||||||
if sio.get_session(sid):
|
|
||||||
sio.emit('players', room='lobby', data=online_players)
|
|
||||||
if sio.get_session(sid).game and sio.get_session(sid).disconnect():
|
|
||||||
sio.close_room(sio.get_session(sid).game.name)
|
|
||||||
games.pop(games.index(sio.get_session(sid).game))
|
|
||||||
print('disconnect ', sid)
|
|
||||||
advertise_lobbies()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def create_room(sid, room_name):
|
|
||||||
if sio.get_session(sid).game == None:
|
|
||||||
while len([g for g in games if g.name == room_name]):
|
|
||||||
room_name += f'_{random.randint(0,100)}'
|
|
||||||
sio.leave_room(sid, 'lobby')
|
|
||||||
sio.enter_room(sid, room_name)
|
|
||||||
g = Game(room_name, sio)
|
|
||||||
g.add_player(sio.get_session(sid))
|
|
||||||
if room_name in blacklist:
|
|
||||||
g.is_hidden = True
|
|
||||||
games.append(g)
|
|
||||||
print(f'{sid} created a room named {room_name}')
|
|
||||||
advertise_lobbies()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def private(sid):
|
|
||||||
g = sio.get_session(sid).game
|
|
||||||
g.set_private()
|
|
||||||
advertise_lobbies()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def toggle_expansion(sid, expansion_name):
|
|
||||||
g = sio.get_session(sid).game
|
|
||||||
g.toggle_expansion(expansion_name)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def toggle_comp(sid):
|
|
||||||
sio.get_session(sid).game.toggle_competitive()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def toggle_replace_with_bot(sid):
|
|
||||||
sio.get_session(sid).game.toggle_disconnect_bot()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def join_room(sid, room):
|
|
||||||
room_name = room['name']
|
|
||||||
i = [g.name for g in games].index(room_name)
|
|
||||||
if games[i].password != '' and games[i].password != room['password'].upper():
|
|
||||||
return
|
|
||||||
if not games[i].started:
|
|
||||||
print(f'{sid} joined a room named {room_name}')
|
|
||||||
sio.leave_room(sid, 'lobby')
|
|
||||||
sio.enter_room(sid, room_name)
|
|
||||||
while len([p for p in games[i].players if p.name == sio.get_session(sid).name]):
|
|
||||||
sio.get_session(sid).name += f'_{random.randint(0,100)}'
|
|
||||||
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
|
||||||
games[i].add_player(sio.get_session(sid))
|
|
||||||
advertise_lobbies()
|
|
||||||
else:
|
|
||||||
games[i].spectators.append(sio.get_session(sid))
|
|
||||||
sio.get_session(sid).game = games[i]
|
|
||||||
sio.get_session(sid).pending_action = PendingAction.WAIT
|
|
||||||
sio.enter_room(sid, games[0].name)
|
|
||||||
games[i].notify_room(sid)
|
|
||||||
eventlet.sleep(0.5)
|
|
||||||
games[i].notify_room(sid)
|
|
||||||
games[i].notify_all()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def chat_message(sid, msg):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
if len(msg) > 0:
|
|
||||||
if msg[0] == '/':
|
|
||||||
if '/addbot' in msg and not ses.game.started:
|
|
||||||
if len(msg.split()) > 1:
|
|
||||||
# for _ in range(int(msg.split()[1])):
|
|
||||||
# ses.game.add_player(Player(f'AI_{random.randint(0,1000)}', 'bot', sio, bot=True))
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'Only 1 bot at the time'})
|
|
||||||
else:
|
|
||||||
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
|
||||||
while any([p for p in ses.game.players if p.name == bot.name]):
|
|
||||||
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
|
||||||
ses.game.add_player(bot)
|
|
||||||
bot.bot_spin()
|
|
||||||
return
|
|
||||||
elif '/removebot' in msg and not ses.game.started:
|
|
||||||
if any([p.is_bot for p in ses.game.players]):
|
|
||||||
[p for p in ses.game.players if p.is_bot][-1].disconnect()
|
|
||||||
return
|
|
||||||
elif '/togglecomp' in msg and ses.game:
|
|
||||||
ses.game.toggle_competitive()
|
|
||||||
return
|
|
||||||
if '/debug' in msg:
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 2 and 'DEPLOY_KEY' in os.environ and cmd[1] == os.environ['DEPLOY_KEY']: # solo chi ha la deploy key può attivare la modalità debug
|
|
||||||
ses.game.debug = not ses.game.debug
|
|
||||||
ses.game.notify_room()
|
|
||||||
elif ses == ses.game.players[0]: # solo l'owner può attivare la modalità debug
|
|
||||||
ses.game.debug = not ses.game.debug
|
|
||||||
ses.game.notify_room()
|
|
||||||
if ses.game.debug:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'red','text':f'debug mode is now active, only the owner of the room can disable it with /debug'})
|
|
||||||
return
|
|
||||||
if not ses.game.debug:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'debug mode is not active, only the owner of the room can enable it with /debug'})
|
|
||||||
elif '/set_chars' in msg and not ses.game.started:
|
|
||||||
cmd = msg.split()
|
|
||||||
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()
|
|
||||||
elif '/nextevent' in msg and ses.game.started:
|
|
||||||
ses.game.deck.flip_event()
|
|
||||||
elif '/notify' in msg and ses.game.started:
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) >= 3:
|
|
||||||
if cmd[1] in ses.game.players_map:
|
|
||||||
ses.game.get_player_named(cmd[1]).notify_card(ses, {
|
|
||||||
'name': ' '.join(cmd[2:]),
|
|
||||||
'icon': '🚨',
|
|
||||||
'suit': 4,
|
|
||||||
'number': ' '.join(cmd[2:])
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
|
||||||
elif '/show_cards' in msg and ses.game.started:
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 2:
|
|
||||||
if cmd[1] in ses.game.players_map:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and is looking at {cmd[1]} hand'})
|
|
||||||
for c in ses.game.get_player_named(cmd[1]).hand:
|
|
||||||
ses.notify_card(ses, c)
|
|
||||||
eventlet.sleep(0.3)
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
|
||||||
elif '/ddc' in msg and ses.game.started: # debug destroy cards usage: [/ddc *] [/ddc username]
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 2:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode destroyed {cmd[1]} cards'})
|
|
||||||
if cmd[1] == "*":
|
|
||||||
for p in ses.game.players_map:
|
|
||||||
ses.game.get_player_named(p).hand = []
|
|
||||||
ses.game.get_player_named(p).equipment = []
|
|
||||||
ses.game.get_player_named(p).notify_self()
|
|
||||||
elif cmd[1] in ses.game.players_map:
|
|
||||||
ses.game.get_player_named(cmd[1]).hand = []
|
|
||||||
ses.game.get_player_named(cmd[1]).equipment = []
|
|
||||||
ses.game.get_player_named(cmd[1]).notify_self()
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
|
||||||
elif '/dsh' in msg and ses.game.started: #debug set health usage [/dsh * hp] [/dsh username hp]
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 3:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and is changing {cmd[1]} health'})
|
|
||||||
if cmd[1] == "*":
|
|
||||||
for p in ses.game.players_map:
|
|
||||||
ses.game.get_player_named(p).lives = int(cmd[2])
|
|
||||||
ses.game.get_player_named(p).notify_self()
|
|
||||||
elif cmd[1] in ses.game.players_map:
|
|
||||||
ses.game.get_player_named(cmd[1]).lives = int(cmd[2])
|
|
||||||
ses.game.get_player_named(cmd[1]).notify_self()
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
|
||||||
elif '/togglebot' in msg and ses.game:
|
|
||||||
ses.game.toggle_disconnect_bot()
|
|
||||||
elif '/cancelgame' in msg and ses.game.started:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} stopped the current game'})
|
|
||||||
ses.game.reset()
|
|
||||||
elif '/startgame' in msg and not ses.game.started:
|
|
||||||
ses.game.start_game()
|
|
||||||
elif '/setbotspeed' in msg:
|
|
||||||
ses.game.bot_speed = float(msg.split()[1])
|
|
||||||
elif '/addex' in msg and not ses.game.started:
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 2:
|
|
||||||
cmd[1] = cmd[1].replace('foc', 'fistful_of_cards')
|
|
||||||
if cmd[1] not in ses.game.available_expansions:
|
|
||||||
ses.game.available_expansions.append(cmd[1])
|
|
||||||
ses.game.notify_room()
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
|
||||||
elif '/setcharacter' in msg:
|
|
||||||
import bang.characters as characters
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) >= 2:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and changed character'})
|
|
||||||
chs = characters.all_characters(ses.game.expansions)
|
|
||||||
ses.character = [c for c in chs if c.name == ' '.join(cmd[1:])][0]
|
|
||||||
ses.real_character = ses.character
|
|
||||||
ses.notify_self()
|
|
||||||
elif '/setevent' in msg and ses.game and ses.game.deck: #add event before the position /setevent (position) 0 (name) Peyote
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) >= 3:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and changed event'})
|
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
|
||||||
chs = []
|
|
||||||
chs.extend(ce.get_all_events())
|
|
||||||
chs.append(ce.get_endgame_card())
|
|
||||||
chs.extend(ceh.get_all_events())
|
|
||||||
chs.append(ceh.get_endgame_card())
|
|
||||||
ses.game.deck.event_cards.insert(int(cmd[1]), [c for c in chs if c!=None and c.name == ' '.join(cmd[2:])][0])
|
|
||||||
ses.game.notify_event_card()
|
|
||||||
elif '/removecard' in msg:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and removed a card'})
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) == 2:
|
|
||||||
if int(cmd[1]) < len(ses.hand):
|
|
||||||
ses.hand.pop(int(cmd[1]))
|
|
||||||
else:
|
|
||||||
ses.equipment.pop(int(cmd[1])-len(ses.hand))
|
|
||||||
ses.notify_self()
|
|
||||||
elif '/getcard' in msg:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'})
|
|
||||||
import bang.cards as cs
|
|
||||||
cmd = msg.split()
|
|
||||||
if len(cmd) >= 2:
|
|
||||||
cards = cs.get_starting_deck(ses.game.expansions)
|
|
||||||
card_names = ' '.join(cmd[1:]).split(',')
|
|
||||||
for cn in card_names:
|
|
||||||
ses.hand.append([c for c in cards if c.name == cn][0])
|
|
||||||
ses.notify_self()
|
|
||||||
elif '/gameinfo' in msg:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.game.__dict__}'})
|
|
||||||
elif '/meinfo' in msg:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'})
|
|
||||||
elif '/mebot' in msg:
|
|
||||||
ses.is_bot = not ses.is_bot
|
|
||||||
ses.bot_spin()
|
|
||||||
elif '/arcadekick' in msg and ses.game.started:
|
|
||||||
if len([p for p in ses.game.players if p.pending_action != PendingAction.WAIT]) == 0:
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'','text':f'KICKING THE ARCADE CABINET'})
|
|
||||||
ses.game.next_turn()
|
|
||||||
else:
|
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
|
|
||||||
else:
|
|
||||||
color = sid.encode('utf-8').hex()[-3:]
|
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'#{color}','text':f'[{ses.name}]: {msg}'})
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_all_rooms(sid, deploy_key):
|
|
||||||
if 'DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']:
|
|
||||||
sio.emit('all_rooms', room=sid, data=[{
|
|
||||||
'name': g.name,
|
|
||||||
'hidden': g.is_hidden,
|
|
||||||
'players': [{'name':p.name, 'bot': p.is_bot, 'health': p.lives, 'sid': p.sid} for p in g.players],
|
|
||||||
'password': g.password,
|
|
||||||
'expansions': g.expansions,
|
|
||||||
'started': g.started,
|
|
||||||
'current_turn': g.turn,
|
|
||||||
'incremental_turn': g.incremental_turn,
|
|
||||||
'debug': g.debug,
|
|
||||||
'spectators': len(g.spectators)
|
|
||||||
} for g in games])
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def kick(sid, data):
|
|
||||||
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
|
||||||
sio.emit('kicked', room=data['sid'])
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def hide_toogle(sid, data):
|
|
||||||
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
|
||||||
game = [g for g in games if g.name==data['room']]
|
|
||||||
if len(games) > 0:
|
|
||||||
game[0].is_hidden = not game[0].is_hidden
|
|
||||||
if game[0].is_hidden:
|
|
||||||
if not data['room'] in blacklist:
|
|
||||||
blacklist.append(data['room'])
|
|
||||||
elif data['room'] in blacklist:
|
|
||||||
blacklist.remove(data['room'])
|
|
||||||
advertise_lobbies()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def start_game(sid):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.game.start_game()
|
|
||||||
advertise_lobbies()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def set_character(sid, name):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.set_character(name)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def refresh(sid):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.notify_self()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def draw(sid, pile):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.draw(pile)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def pick(sid):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.pick()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def end_turn(sid):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.end_turn()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def play_card(sid, data):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.play_card(data['index'], data['against'], data['with'])
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def respond(sid, data):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.respond(data)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def choose(sid, card_index):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.choose(card_index)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def scrap(sid, card_index):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.scrap(card_index)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def chuck_lose_hp_draw(sid):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.chuck_lose_hp_draw()
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def holyday_special(sid, data):
|
|
||||||
ses: Player = sio.get_session(sid)
|
|
||||||
ses.holyday_special(data)
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_cards(sid):
|
|
||||||
import bang.cards as c
|
|
||||||
cards = c.get_starting_deck(['dodge_city'])
|
|
||||||
cards_dict = {}
|
|
||||||
for ca in cards:
|
|
||||||
if ca.name not in cards_dict:
|
|
||||||
cards_dict[ca.name] = ca
|
|
||||||
cards = [cards_dict[i] for i in cards_dict]
|
|
||||||
sio.emit('cards_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_characters(sid):
|
|
||||||
import bang.characters as ch
|
|
||||||
cards = ch.all_characters(['dodge_city'])
|
|
||||||
sio.emit('characters_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_highnooncards(sid):
|
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
|
||||||
chs = []
|
|
||||||
chs.extend(ceh.get_all_events())
|
|
||||||
chs.append(ceh.get_endgame_card())
|
|
||||||
sio.emit('highnooncards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_foccards(sid):
|
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
|
||||||
chs = []
|
|
||||||
chs.extend(ce.get_all_events())
|
|
||||||
chs.append(ce.get_endgame_card())
|
|
||||||
sio.emit('foccards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
eventlet.wsgi.server(eventlet.listen(('', 5001)), app)
|
|
@ -42,7 +42,10 @@ class Card(ABC):
|
|||||||
self.must_be_used = False
|
self.must_be_used = False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)]
|
if str(self.suit).isnumeric():
|
||||||
|
char = ['♦️', '♣️', '♥️', '♠️'][int(self.suit)]
|
||||||
|
else:
|
||||||
|
char = self.suit
|
||||||
return f'{self.name} {char}{self.number}'
|
return f'{self.name} {char}{self.number}'
|
||||||
return super().__str__()
|
return super().__str__()
|
||||||
|
|
||||||
@ -71,6 +74,7 @@ class Card(ABC):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
player.equipment.append(self)
|
player.equipment.append(self)
|
||||||
|
self.can_be_used_now = False
|
||||||
if against:
|
if against:
|
||||||
player.sio.emit('chat_message', room=player.game.name,
|
player.sio.emit('chat_message', room=player.game.name,
|
||||||
data=f'_play_card_against|{player.name}|{self.name}|{against}')
|
data=f'_play_card_against|{player.name}|{self.name}|{against}')
|
||||||
@ -84,7 +88,7 @@ class Card(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def is_duplicate_card(self, player):
|
def is_duplicate_card(self, player):
|
||||||
return self.name in [c.name for c in player.equipment]
|
return self.name in [c.name for c in player.equipment] or self.name in [c.name for c in player.gold_rush_equipment]
|
||||||
|
|
||||||
def check_suit(self, game, accepted):
|
def check_suit(self, game, accepted):
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
@ -207,15 +211,16 @@ class Bang(Card):
|
|||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
if player.game.check_event(ceh.Sermone):
|
if player.game.check_event(ceh.Sermone) and not self.number == 42: # 42 gold rush
|
||||||
return False
|
return False
|
||||||
if player.has_played_bang and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)) and against != None:
|
if ((player.has_played_bang and not self.number == 42) and (not any([isinstance(c, Volcanic) for c in player.equipment]) or player.game.check_event(ce.Lazo)) and against != None): # 42 gold rush:
|
||||||
return False
|
return False
|
||||||
elif against != None:
|
elif against != None:
|
||||||
import bang.characters as chars
|
import bang.characters as chars
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.bang_used += 1
|
if not self.number == 42: # 42 gold rush
|
||||||
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1
|
player.bang_used += 1
|
||||||
|
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1
|
||||||
if player.character.check(player.game, chars.WillyTheKid):
|
if player.character.check(player.game, chars.WillyTheKid):
|
||||||
player.has_played_bang = False
|
player.has_played_bang = False
|
||||||
player.game.attack(player, against, double=player.character.check(player.game, chars.SlabTheKiller))
|
player.game.attack(player, against, double=player.character.check(player.game, chars.SlabTheKiller))
|
||||||
@ -230,18 +235,36 @@ class Birra(Card):
|
|||||||
# self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori"
|
# self.desc = "Gioca questa carta per recuperare un punto vita. Non puoi andare oltre al limite massimo del tuo personaggio. Se stai per perdere l'ultimo punto vita puoi giocare questa carta anche nel turno dell'avversario. La birra non ha più effetto se ci sono solo due giocatori"
|
||||||
# self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players"
|
# self.desc_eng = "Play this card to regain a life point. You cannot heal more than your character's maximum limit. If you are about to lose your last life point, you can also play this card on your opponent's turn. Beer no longer takes effect if there are only two players"
|
||||||
|
|
||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against=None, _with=None, skipChecks=False):
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
if player.game.check_event(ceh.IlReverendo):
|
if player.game.check_event(ceh.IlReverendo):
|
||||||
return False
|
return False
|
||||||
if len(player.game.get_alive_players()) != 2:
|
if not skipChecks:
|
||||||
|
import bang.expansions.gold_rush.characters as grch
|
||||||
|
madamYto = [p for p in player.game.get_alive_players() if p.character.check(player.game, grch.MadamYto) and self.number != 42]
|
||||||
|
for p in madamYto:
|
||||||
|
p.hand.append(player.game.deck.draw(True))
|
||||||
|
p.notify_self()
|
||||||
|
if 'gold_rush' in player.game.expansions and self.number != 42:
|
||||||
|
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 (len(player.game.get_alive_players()) != 2 or self.number == 42) and player.lives < player.max_lives:
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.lives = min(player.lives+1, player.max_lives)
|
player.lives = min(player.lives+1, player.max_lives)
|
||||||
import bang.expansions.dodge_city.characters as chd
|
import bang.expansions.dodge_city.characters as chd
|
||||||
if player.character.check(player.game, chd.TequilaJoe):
|
if player.character.check(player.game, chd.TequilaJoe):
|
||||||
player.lives = min(player.lives+1, player.max_lives)
|
player.lives = min(player.lives+1, player.max_lives)
|
||||||
return True
|
return True
|
||||||
elif len(player.game.get_alive_players()) == 2:
|
elif len(player.game.get_alive_players()) == 2 or player.lives == player.max_lives:
|
||||||
player.sio.emit('chat_message', room=player.game.name,
|
player.sio.emit('chat_message', room=player.game.name,
|
||||||
data=f'_spilled_beer|{player.name}|{self.name}')
|
data=f'_spilled_beer|{player.name}|{self.name}')
|
||||||
return True
|
return True
|
||||||
@ -255,9 +278,10 @@ class CatBalou(Card):
|
|||||||
# self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
|
# self.desc = "Fai scartare una carta a un qualsiasi giocatore, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
|
||||||
# self.desc_eng = "Choose and discard a card from any other player."
|
# self.desc_eng = "Choose and discard a card from any other player."
|
||||||
self.need_target = True
|
self.need_target = True
|
||||||
|
self.can_target_self = True
|
||||||
|
|
||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0:
|
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0 and (player.name != against or len(player.equipment) > 0):
|
||||||
if self.name == 'Cat Balou':
|
if self.name == 'Cat Balou':
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
from bang.players import PendingAction
|
from bang.players import PendingAction
|
||||||
@ -281,7 +305,7 @@ class Diligenza(Card):
|
|||||||
player.sio.emit('chat_message', room=player.game.name,
|
player.sio.emit('chat_message', room=player.game.name,
|
||||||
data=f'_diligenza|{player.name}|{self.name}')
|
data=f'_diligenza|{player.name}|{self.name}')
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
player.hand.append(player.game.deck.draw())
|
player.hand.append(player.game.deck.draw(True))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -371,11 +395,12 @@ class Panico(Card):
|
|||||||
super().__init__(suit, 'Panico!', number, range=1)
|
super().__init__(suit, 'Panico!', number, range=1)
|
||||||
self.icon = '😱'
|
self.icon = '😱'
|
||||||
self.need_target = True
|
self.need_target = True
|
||||||
|
self.can_target_self = True
|
||||||
# self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
|
# self.desc = "Pesca una carta da un giocatore a distanza 1, scegli a caso dalla mano, oppure fra quelle che ha in gioco"
|
||||||
# self.desc_eng = "Steal a card from a player at distance 1"
|
# self.desc_eng = "Steal a card from a player at distance 1"
|
||||||
|
|
||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0:
|
if against != None and (len(player.game.get_player_named(against).hand) + len(player.game.get_player_named(against).equipment)) > 0 and (player.name != against or len(player.equipment) > 0):
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
from bang.players import PendingAction
|
from bang.players import PendingAction
|
||||||
player.pending_action = PendingAction.CHOOSE
|
player.pending_action = PendingAction.CHOOSE
|
||||||
@ -415,7 +440,7 @@ class WellsFargo(Card):
|
|||||||
player.sio.emit('chat_message', room=player.game.name,
|
player.sio.emit('chat_message', room=player.game.name,
|
||||||
data=f'_wellsfargo|{player.name}|{self.name}')
|
data=f'_wellsfargo|{player.name}|{self.name}')
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
player.hand.append(player.game.deck.draw())
|
player.hand.append(player.game.deck.draw(True))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from bang.expansions import *
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
class Character(ABC):
|
class Character(ABC):
|
||||||
@ -19,6 +20,13 @@ class Character(ABC):
|
|||||||
return False
|
return False
|
||||||
return isinstance(self, character)
|
return isinstance(self, character)
|
||||||
|
|
||||||
|
def special(self, player, data):
|
||||||
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
if player.game.check_event(ceh.Sbornia):
|
||||||
|
return False
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_use_special|{player.name}|{self.name}')
|
||||||
|
return True
|
||||||
|
|
||||||
class BartCassidy(Character):
|
class BartCassidy(Character):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("Bart Cassidy", max_lives=4)
|
super().__init__("Bart Cassidy", max_lives=4)
|
||||||
@ -158,4 +166,6 @@ def all_characters(expansions: List[str]):
|
|||||||
]
|
]
|
||||||
if 'dodge_city' in expansions:
|
if 'dodge_city' in expansions:
|
||||||
base_chars.extend(DodgeCity.get_characters())
|
base_chars.extend(DodgeCity.get_characters())
|
||||||
|
if 'gold_rush' in expansions:
|
||||||
|
base_chars.extend(GoldRush.get_characters())
|
||||||
return base_chars
|
return base_chars
|
@ -3,6 +3,7 @@ import random
|
|||||||
import bang.cards as cs
|
import bang.cards as cs
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
import bang.expansions.gold_rush.shop_cards as grc
|
||||||
|
|
||||||
class Deck:
|
class Deck:
|
||||||
def __init__(self, game):
|
def __init__(self, game):
|
||||||
@ -21,20 +22,27 @@ class Deck:
|
|||||||
self.all_cards_str.append(c.name)
|
self.all_cards_str.append(c.name)
|
||||||
self.game = game
|
self.game = game
|
||||||
self.event_cards: List[ce.CardEvent] = []
|
self.event_cards: List[ce.CardEvent] = []
|
||||||
endgame_cards = []
|
endgame_cards: List[ce.CardEvent] = []
|
||||||
if 'fistful_of_cards' in game.expansions:
|
if 'fistful_of_cards' in game.expansions:
|
||||||
self.event_cards.extend(ce.get_all_events())
|
self.event_cards.extend(ce.get_all_events(game.rng))
|
||||||
endgame_cards.append(ce.get_endgame_card())
|
endgame_cards.append(ce.get_endgame_card())
|
||||||
if 'high_noon' in game.expansions:
|
if 'high_noon' in game.expansions:
|
||||||
self.event_cards.extend(ceh.get_all_events())
|
self.event_cards.extend(ceh.get_all_events(game.rng))
|
||||||
endgame_cards.append(ceh.get_endgame_card())
|
endgame_cards.append(ceh.get_endgame_card())
|
||||||
if len(self.event_cards) > 0:
|
if len(self.event_cards) > 0:
|
||||||
random.shuffle(self.event_cards)
|
game.rng.shuffle(self.event_cards)
|
||||||
self.event_cards = self.event_cards[:12]
|
self.event_cards = self.event_cards[:12]
|
||||||
self.event_cards.insert(0, None)
|
self.event_cards.insert(0, None)
|
||||||
self.event_cards.insert(0, None) # 2 perchè iniziale, e primo flip dallo sceriffo
|
self.event_cards.insert(0, None) # 2 perchè iniziale, e primo flip dallo sceriffo
|
||||||
self.event_cards.append(random.choice(endgame_cards))
|
self.event_cards.append(game.rng.choice(endgame_cards))
|
||||||
random.shuffle(self.cards)
|
game.rng.shuffle(self.cards)
|
||||||
|
self.shop_deck: List[grc.ShopCard] = []
|
||||||
|
self.shop_cards: List[grc.ShopCard] = []
|
||||||
|
if 'gold_rush' in game.expansions:
|
||||||
|
self.shop_cards = [None, None, None]
|
||||||
|
self.shop_deck = grc.get_cards()
|
||||||
|
game.rng.shuffle(self.shop_deck)
|
||||||
|
self.fill_gold_rush_shop()
|
||||||
self.scrap_pile: List[cs.Card] = []
|
self.scrap_pile: List[cs.Card] = []
|
||||||
print(f'Deck initialized with {len(self.cards)} cards')
|
print(f'Deck initialized with {len(self.cards)} cards')
|
||||||
|
|
||||||
@ -43,6 +51,16 @@ class Deck:
|
|||||||
self.event_cards.append(self.event_cards.pop(0))
|
self.event_cards.append(self.event_cards.pop(0))
|
||||||
self.game.notify_event_card()
|
self.game.notify_event_card()
|
||||||
|
|
||||||
|
def fill_gold_rush_shop(self):
|
||||||
|
if not any([c == None for c in self.shop_cards]):
|
||||||
|
return
|
||||||
|
for i in range(3):
|
||||||
|
if self.shop_cards[i] == None:
|
||||||
|
print(f'replacing gr-card {i}')
|
||||||
|
self.shop_cards[i] = self.shop_deck.pop(0)
|
||||||
|
self.shop_cards[i].reset_card()
|
||||||
|
self.game.notify_gold_rush_shop()
|
||||||
|
|
||||||
def peek(self, n_cards: int) -> list:
|
def peek(self, n_cards: int) -> list:
|
||||||
return self.cards[:n_cards]
|
return self.cards[:n_cards]
|
||||||
|
|
||||||
@ -73,7 +91,7 @@ class Deck:
|
|||||||
|
|
||||||
def reshuffle(self):
|
def reshuffle(self):
|
||||||
self.cards = self.scrap_pile[:-1].copy()
|
self.cards = self.scrap_pile[:-1].copy()
|
||||||
random.shuffle(self.cards)
|
self.game.rng.shuffle(self.cards)
|
||||||
self.scrap_pile = self.scrap_pile[-1:]
|
self.scrap_pile = self.scrap_pile[-1:]
|
||||||
|
|
||||||
def draw_from_scrap_pile(self) -> cs.Card:
|
def draw_from_scrap_pile(self) -> cs.Card:
|
||||||
@ -86,6 +104,7 @@ class Deck:
|
|||||||
return self.draw()
|
return self.draw()
|
||||||
|
|
||||||
def scrap(self, card: cs.Card, ignore_event = False):
|
def scrap(self, card: cs.Card, ignore_event = False):
|
||||||
|
if card.number == 42: return
|
||||||
card.reset_card()
|
card.reset_card()
|
||||||
if self.game.check_event(ce.MinieraAbbandonata) and not ignore_event:
|
if self.game.check_event(ce.MinieraAbbandonata) and not ignore_event:
|
||||||
self.put_on_top(card)
|
self.put_on_top(card)
|
||||||
|
@ -7,3 +7,8 @@ class DodgeCity():
|
|||||||
def get_cards():
|
def get_cards():
|
||||||
from bang.expansions.dodge_city import cards
|
from bang.expansions.dodge_city import cards
|
||||||
return cards.get_starting_deck()
|
return cards.get_starting_deck()
|
||||||
|
|
||||||
|
class GoldRush():
|
||||||
|
def get_characters():
|
||||||
|
from bang.expansions.gold_rush import characters
|
||||||
|
return characters.all_characters()
|
||||||
|
@ -41,7 +41,7 @@ class Schivata(Mancato):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def use_card(self, player):
|
def use_card(self, player):
|
||||||
player.hand.append(player.game.deck.draw())
|
player.hand.append(player.game.deck.draw(True))
|
||||||
player.notify_self()
|
player.notify_self()
|
||||||
|
|
||||||
class RagTime(Panico):
|
class RagTime(Panico):
|
||||||
@ -225,7 +225,7 @@ class Derringer(Pugnale):
|
|||||||
|
|
||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
if self.can_be_used_now:
|
if self.can_be_used_now:
|
||||||
player.hand.append(player.game.deck.draw())
|
player.hand.append(player.game.deck.draw(True))
|
||||||
return super().play_card(player, against=against)
|
return super().play_card(player, against=against)
|
||||||
else:
|
else:
|
||||||
if not self.is_duplicate_card(player):
|
if not self.is_duplicate_card(player):
|
||||||
@ -236,7 +236,7 @@ class Derringer(Pugnale):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def use_card(self, player):
|
def use_card(self, player):
|
||||||
player.hand.append(player.game.deck.draw())
|
player.hand.append(player.game.deck.draw(True))
|
||||||
player.notify_self()
|
player.notify_self()
|
||||||
|
|
||||||
class Borraccia(Card):
|
class Borraccia(Card):
|
||||||
@ -440,4 +440,5 @@ def get_starting_deck() -> List[Card]:
|
|||||||
]
|
]
|
||||||
for c in cards:
|
for c in cards:
|
||||||
c.expansion_icon = '🐄️'
|
c.expansion_icon = '🐄️'
|
||||||
|
c.expansion = 'dodge_city'
|
||||||
return cards
|
return cards
|
||||||
|
@ -85,6 +85,21 @@ class ChuckWengam(Character):
|
|||||||
# self.desc_eng = "On his turn he may decide to lose 1 HP to draw 2 cards from the deck."
|
# self.desc_eng = "On his turn he may decide to lose 1 HP to draw 2 cards from the deck."
|
||||||
self.icon = '💰'
|
self.icon = '💰'
|
||||||
|
|
||||||
|
def special(self, player, data):
|
||||||
|
if super().special(player, data):
|
||||||
|
if player.lives > 1 and player.is_my_turn:
|
||||||
|
import bang.expansions.gold_rush.shop_cards as grc
|
||||||
|
player.lives -= 1
|
||||||
|
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
player.gold_nuggets += 1
|
||||||
|
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
player.notify_self()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
class PatBrennan(Character):
|
class PatBrennan(Character):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("Pat Brennan", max_lives=4)
|
super().__init__("Pat Brennan", max_lives=4)
|
||||||
@ -106,6 +121,19 @@ class DocHolyday(Character):
|
|||||||
# self.desc_eng = "He can discard 2 cards to play a bang."
|
# self.desc_eng = "He can discard 2 cards to play a bang."
|
||||||
self.icon = '✌🏻'
|
self.icon = '✌🏻'
|
||||||
|
|
||||||
|
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:
|
||||||
|
player.special_use_count += 1
|
||||||
|
cards = sorted(data['cards'], reverse=True)
|
||||||
|
for c in cards:
|
||||||
|
player.game.deck.scrap(player.hand.pop(c), True)
|
||||||
|
player.notify_self()
|
||||||
|
player.game.attack(player, data['against'])
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def all_characters() -> List[Character]:
|
def all_characters() -> List[Character]:
|
||||||
cards = [
|
cards = [
|
||||||
PixiePete(),
|
PixiePete(),
|
||||||
@ -126,6 +154,7 @@ def all_characters() -> List[Character]:
|
|||||||
]
|
]
|
||||||
for c in cards:
|
for c in cards:
|
||||||
c.expansion_icon = '🐄️'
|
c.expansion_icon = '🐄️'
|
||||||
|
c.expansion = 'dodge_city'
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
#Apache Kid: il suo effetto non conta nei duelli
|
#Apache Kid: il suo effetto non conta nei duelli
|
||||||
|
@ -101,7 +101,7 @@ def get_endgame_card():
|
|||||||
end_game.expansion = 'fistful-of-cards'
|
end_game.expansion = 'fistful-of-cards'
|
||||||
return end_game
|
return end_game
|
||||||
|
|
||||||
def get_all_events():
|
def get_all_events(rng=random):
|
||||||
cards = [
|
cards = [
|
||||||
Agguato(),
|
Agguato(),
|
||||||
Cecchino(),
|
Cecchino(),
|
||||||
@ -118,7 +118,7 @@ def get_all_events():
|
|||||||
RouletteRussa(),
|
RouletteRussa(),
|
||||||
Vendetta(),
|
Vendetta(),
|
||||||
]
|
]
|
||||||
random.shuffle(cards)
|
rng.shuffle(cards)
|
||||||
for c in cards:
|
for c in cards:
|
||||||
c.expansion = 'fistful-of-cards'
|
c.expansion = 'fistful-of-cards'
|
||||||
return cards
|
return cards
|
98
backend/bang/expansions/gold_rush/characters.py
Normal file
98
backend/bang/expansions/gold_rush/characters.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
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 = '🔔️'
|
||||||
|
|
||||||
|
class DutchWill(Character):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Dutch Will", max_lives=4)
|
||||||
|
# Pesca 2 ne scarta 1 e prende 1 pepita
|
||||||
|
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 = '💆♂️️'
|
||||||
|
|
||||||
|
def special(self, player, data):
|
||||||
|
if super().special(player, data):
|
||||||
|
if player.gold_nuggets >= 2 and player.is_my_turn:
|
||||||
|
player.gold_nuggets -= 2
|
||||||
|
player.has_played_bang = False
|
||||||
|
player.bang_used -= 1
|
||||||
|
player.notify_self()
|
||||||
|
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 = '⛅️'
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 = '💃️'
|
||||||
|
|
||||||
|
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 = '👛️'
|
||||||
|
|
||||||
|
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 = '🐍️'
|
||||||
|
|
||||||
|
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:
|
||||||
|
player.gold_nuggets -= 1
|
||||||
|
player.special_use_count += 1
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
player.notify_self()
|
||||||
|
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 = '🏇️'
|
||||||
|
|
||||||
|
|
||||||
|
def all_characters() -> List[Character]:
|
||||||
|
cards = [
|
||||||
|
DonBell(),
|
||||||
|
DutchWill(),
|
||||||
|
JackyMurieta(),
|
||||||
|
JoshMcCloud(),
|
||||||
|
MadamYto(),
|
||||||
|
PrettyLuzena(),
|
||||||
|
RaddieSnake(),
|
||||||
|
SimeonPicos(),
|
||||||
|
]
|
||||||
|
for c in cards:
|
||||||
|
c.expansion_icon = '🤑️'
|
||||||
|
c.expansion = 'gold_rush'
|
||||||
|
return cards
|
261
backend/bang/expansions/gold_rush/shop_cards.py
Normal file
261
backend/bang/expansions/gold_rush/shop_cards.py
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
from bang.cards import *
|
||||||
|
import bang.roles as r
|
||||||
|
import bang.players as pl
|
||||||
|
|
||||||
|
class ShopCardKind(IntEnum):
|
||||||
|
BROWN = 0 # Se l’equipaggiamento ha il bordo marrone, applicane subito l’effetto e poi scartalo.
|
||||||
|
BLACK = 1 # Se l’equipaggiamento ha il bordo nero, tienilo scoperto di fronte a te.
|
||||||
|
|
||||||
|
class ShopCard(Card):
|
||||||
|
def __init__(self, name:str, cost:int, kind:ShopCardKind):
|
||||||
|
super().__init__(suit='💵', number=cost, name=name)
|
||||||
|
self.kind = kind
|
||||||
|
self.expansion_icon = '🤑️'
|
||||||
|
self.expansion = 'gold_rush'
|
||||||
|
|
||||||
|
def play_card(self, player, against, _with=None):
|
||||||
|
if self.kind == ShopCardKind.BROWN:
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}')
|
||||||
|
return True
|
||||||
|
elif self.kind == ShopCardKind.BLACK: # equip it
|
||||||
|
if not self.is_duplicate_card(player):
|
||||||
|
self.reset_card()
|
||||||
|
self.can_be_used_now = True
|
||||||
|
player.gold_rush_equipment.append(self)
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}')
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reset_card(self):
|
||||||
|
if self.kind == ShopCardKind.BLACK:
|
||||||
|
self.can_be_used_now = False
|
||||||
|
|
||||||
|
class Bicchierino(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('Bicchierino', 1, ShopCardKind.BROWN)
|
||||||
|
self.icon = '🍸️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
player.available_cards = [{
|
||||||
|
'name': p.name,
|
||||||
|
'icon': p.role.icon if(player.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||||
|
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
||||||
|
'is_character': True,
|
||||||
|
'noDesc': True
|
||||||
|
} for p in player.game.get_alive_players()]
|
||||||
|
player.choose_text = 'choose_bicchierino'
|
||||||
|
player.pending_action = pl.PendingAction.CHOOSE
|
||||||
|
player.notify_self()
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class Bottiglia(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('Bottiglia', 2, ShopCardKind.BROWN)
|
||||||
|
self.icon = '🍾️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
# bang, birra, panico
|
||||||
|
player.available_cards = [Bang(1,42), Birra(1,42), Panico(1,42)]
|
||||||
|
for i in range(len(player.available_cards)):
|
||||||
|
player.available_cards[i].must_be_used = True
|
||||||
|
player.choose_text = 'choose_bottiglia'
|
||||||
|
player.pending_action = pl.PendingAction.CHOOSE
|
||||||
|
player.notify_self()
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class Complice(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('Complice', 2, ShopCardKind.BROWN)
|
||||||
|
self.icon = '😉️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
# emporio, duello, Cat balou
|
||||||
|
player.available_cards = [Emporio(1,42), Duello(1,42), CatBalou(1,42)]
|
||||||
|
for i in range(len(player.available_cards)):
|
||||||
|
player.available_cards[i].must_be_used = True
|
||||||
|
player.choose_text = 'choose_complice'
|
||||||
|
player.pending_action = pl.PendingAction.CHOOSE
|
||||||
|
player.notify_self()
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class CorsaAllOro(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Corsa All Oro_gr", 5, ShopCardKind.BROWN)
|
||||||
|
self.icon = '🤑️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
player.lives = player.max_lives
|
||||||
|
player.play_turn()
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class Rum(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Rum", 3, ShopCardKind.BROWN)
|
||||||
|
self.icon = '🍷️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
# Estrai 4 carte e ottieni 1 hp per ogni seme diverso
|
||||||
|
import bang.characters as c
|
||||||
|
suits = set()
|
||||||
|
num = 5 if player.character.check(player.game, c.LuckyDuke) else 4
|
||||||
|
for i in range(num):
|
||||||
|
c = player.game.deck.pick_and_scrap()
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_flipped|{player.name}|{c.name}|{c.num_suit()}')
|
||||||
|
suits.add(c.suit)
|
||||||
|
player.lives = min(player.lives+len(suits), player.max_lives)
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class UnionPacific(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Union Pacific", 4, ShopCardKind.BROWN)
|
||||||
|
self.icon = '🚆️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
player.sio.emit('chat_message', room=player.game.name,
|
||||||
|
data=f'_UnionPacific|{player.name}|{self.name}')
|
||||||
|
for i in range(4):
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
|
||||||
|
class Calumet(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Calumet", 3, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🚭️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# ti rende immuni ai quadri
|
||||||
|
|
||||||
|
class Cinturone(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Cinturone", 2, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🥡'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# max carte a fine turno 8
|
||||||
|
|
||||||
|
class FerroDiCavallo(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Ferro di Cavallo", 2, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🎠'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# estrai come luky duke
|
||||||
|
|
||||||
|
class Piccone(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Piccone", 4, ShopCardKind.BLACK)
|
||||||
|
self.icon = '⛏️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# peschi una carta in piu a inizio turno
|
||||||
|
|
||||||
|
class Ricercato(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Ricercato", 2, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🤠️'
|
||||||
|
self.can_target_self = True
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_purchase_card|{player.name}|{self.name}')
|
||||||
|
player.available_cards = [{
|
||||||
|
'name': p.name,
|
||||||
|
'icon': p.role.icon if(player.game.initial_players == 3) else '🤠',
|
||||||
|
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
||||||
|
'is_character': True,
|
||||||
|
'noDesc': True
|
||||||
|
} 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 = 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
|
||||||
|
|
||||||
|
class Setaccio(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Setaccio", 3, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🥘️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
if not self.can_be_used_now:
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
else:
|
||||||
|
if player.gold_nuggets >= 1 and player.setaccio_count < 2:
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}')
|
||||||
|
player.gold_nuggets -= 1
|
||||||
|
player.setaccio_count += 1
|
||||||
|
player.hand.append(player.game.deck.draw(True))
|
||||||
|
player.notify_self()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
# paghi 1 pepita per pescare 1 carta durante il tuo turno (max 2 volte per turno)
|
||||||
|
|
||||||
|
class Stivali(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Stivali", 3, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🥾️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# peschi una carta ogni volta che vieni ferito
|
||||||
|
|
||||||
|
class Talismano(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Talismano", 3, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🧿'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
# ottieni una pepita ogni volta che vieni ferito
|
||||||
|
|
||||||
|
class Zaino(ShopCard):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Zaino", 3, ShopCardKind.BLACK)
|
||||||
|
self.icon = '🎒️'
|
||||||
|
|
||||||
|
def play_card(self, player, against=None, _with=None):
|
||||||
|
if not self.can_be_used_now:
|
||||||
|
return super().play_card(player, against, _with)
|
||||||
|
else:
|
||||||
|
if player.gold_nuggets >= 2:
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_play_card|{player.name}|{self.name}')
|
||||||
|
player.gold_nuggets -= 2
|
||||||
|
player.lives = min(player.lives + 1, player.max_lives)
|
||||||
|
player.notify_self()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
# paga 2 pepite per recuperare 1 vita
|
||||||
|
|
||||||
|
def get_cards() -> List[Card]:
|
||||||
|
cards = [
|
||||||
|
Bicchierino(),
|
||||||
|
Bicchierino(),
|
||||||
|
Bicchierino(),
|
||||||
|
Bottiglia(),
|
||||||
|
Bottiglia(),
|
||||||
|
Bottiglia(),
|
||||||
|
Complice(),
|
||||||
|
Complice(),
|
||||||
|
Complice(),
|
||||||
|
CorsaAllOro(),
|
||||||
|
Rum(),
|
||||||
|
Rum(),
|
||||||
|
UnionPacific(),
|
||||||
|
Calumet(),
|
||||||
|
Cinturone(),
|
||||||
|
FerroDiCavallo(),
|
||||||
|
Piccone(),
|
||||||
|
Ricercato(),
|
||||||
|
Ricercato(),
|
||||||
|
Ricercato(),
|
||||||
|
Setaccio(),
|
||||||
|
Stivali(),
|
||||||
|
Talismano(),
|
||||||
|
Zaino(),
|
||||||
|
]
|
||||||
|
return cards
|
@ -96,7 +96,7 @@ def get_endgame_card():
|
|||||||
end_game.expansion = 'high-noon'
|
end_game.expansion = 'high-noon'
|
||||||
return end_game
|
return end_game
|
||||||
|
|
||||||
def get_all_events():
|
def get_all_events(rng=random):
|
||||||
cards = [
|
cards = [
|
||||||
Benedizione(),
|
Benedizione(),
|
||||||
Maledizione(),
|
Maledizione(),
|
||||||
@ -113,7 +113,7 @@ def get_all_events():
|
|||||||
Manette(),
|
Manette(),
|
||||||
NuovaIdentita(),
|
NuovaIdentita(),
|
||||||
]
|
]
|
||||||
random.shuffle(cards)
|
rng.shuffle(cards)
|
||||||
for c in cards:
|
for c in cards:
|
||||||
c.expansion = 'high-noon'
|
c.expansion = 'high-noon'
|
||||||
return cards
|
return cards
|
@ -7,11 +7,13 @@ import eventlet
|
|||||||
|
|
||||||
import bang.players as pl
|
import bang.players as pl
|
||||||
import bang.characters as characters
|
import bang.characters as characters
|
||||||
|
import bang.expansions.dodge_city.characters as chd
|
||||||
from bang.deck import Deck
|
from bang.deck import Deck
|
||||||
import bang.roles as roles
|
import bang.roles as roles
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
import bang.expansions.gold_rush.shop_cards as grc
|
||||||
|
import bang.expansions.gold_rush.characters as grch
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self, name, sio:socketio):
|
def __init__(self, name, sio:socketio):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -26,8 +28,8 @@ class Game:
|
|||||||
self.waiting_for = 0
|
self.waiting_for = 0
|
||||||
self.initial_players = 0
|
self.initial_players = 0
|
||||||
self.password = ''
|
self.password = ''
|
||||||
self.expansions = []
|
self.expansions: List[str] = []
|
||||||
self.available_expansions = ['dodge_city', 'fistful_of_cards', 'high_noon']
|
self.available_expansions = ['dodge_city', 'fistful_of_cards', 'high_noon', 'gold_rush']
|
||||||
self.shutting_down = False
|
self.shutting_down = False
|
||||||
self.is_competitive = False
|
self.is_competitive = False
|
||||||
self.disconnect_bot = True
|
self.disconnect_bot = True
|
||||||
@ -38,22 +40,28 @@ class Game:
|
|||||||
self.incremental_turn = 0
|
self.incremental_turn = 0
|
||||||
self.did_resuscitate_deadman = False
|
self.did_resuscitate_deadman = False
|
||||||
self.is_handling_death = False
|
self.is_handling_death = False
|
||||||
self.pending_winners = []
|
self.pending_winners: List[pl.Player] = []
|
||||||
self.someone_won = False
|
self.someone_won = False
|
||||||
self.attack_in_progress = False
|
self.attack_in_progress = False
|
||||||
self.characters_to_distribute = 2 # personaggi da dare a inizio partita
|
self.characters_to_distribute = 2 # personaggi da dare a inizio partita
|
||||||
self.debug = self.name == 'debug'
|
self.debug = self.name == 'debug'
|
||||||
self.is_changing_pwd = False
|
self.is_changing_pwd = False
|
||||||
self.is_hidden = False
|
self.is_hidden = False
|
||||||
|
self.rng = random.Random()
|
||||||
|
self.rpc_log = []
|
||||||
|
self.is_replay = False
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
print('resetting lobby')
|
print(f'{self.name}: resetting lobby')
|
||||||
self.players.extend(self.spectators)
|
self.players.extend(self.spectators)
|
||||||
self.spectators = []
|
self.spectators = []
|
||||||
for bot in [p for p in self.players if p.is_bot]:
|
for bot in [p for p in self.players if p.is_bot]:
|
||||||
bot.game = None
|
if bot.was_player:
|
||||||
|
bot.is_bot = False
|
||||||
|
else:
|
||||||
|
bot.game = None
|
||||||
self.players = [p for p in self.players if not p.is_bot]
|
self.players = [p for p in self.players if not p.is_bot]
|
||||||
print(self.players)
|
print(f'{self.name}: players: {self.players}')
|
||||||
self.started = False
|
self.started = False
|
||||||
self.is_handling_death = False
|
self.is_handling_death = False
|
||||||
self.waiting_for = 0
|
self.waiting_for = 0
|
||||||
@ -66,6 +74,54 @@ class Game:
|
|||||||
eventlet.sleep(0.5)
|
eventlet.sleep(0.5)
|
||||||
self.notify_room()
|
self.notify_room()
|
||||||
|
|
||||||
|
def replay(self, log):
|
||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
self.players = []
|
||||||
|
self.is_hidden = True
|
||||||
|
self.is_replay = True
|
||||||
|
self.replay_speed = 1
|
||||||
|
for i in range(len(log)):
|
||||||
|
print('replay:', i, 'of', len(log))
|
||||||
|
cmd = log[i].split(';')
|
||||||
|
if cmd[1] == 'players':
|
||||||
|
self.expansions = json.loads(cmd[4].replace("'",'"'))
|
||||||
|
pnames = json.loads(cmd[3].replace("'",'"'))
|
||||||
|
for p in pnames:
|
||||||
|
self.add_player(pl.Player(p, p, DummySocket(self.sio), bot=False))
|
||||||
|
continue
|
||||||
|
if cmd[1] == 'start_game':
|
||||||
|
self.start_game(int(cmd[2]))
|
||||||
|
continue
|
||||||
|
player = [p for p in self.players if p.name == cmd[0]][0]
|
||||||
|
if cmd[1] == 'set_character':
|
||||||
|
if player.character != None and isinstance(player.real_character, chd.VeraCuster):
|
||||||
|
player.set_available_character([p.character for p in self.get_alive_players() if p != player])
|
||||||
|
player.set_character(cmd[2])
|
||||||
|
if cmd[1] == 'draw':
|
||||||
|
player.draw(cmd[2])
|
||||||
|
if cmd[1] == 'pick':
|
||||||
|
player.pick()
|
||||||
|
if cmd[1] == 'end_turn':
|
||||||
|
player.end_turn()
|
||||||
|
if cmd[1] == 'play_card':
|
||||||
|
data = json.loads(cmd[2])
|
||||||
|
player.play_card(data['index'], data['against'], data['with'])
|
||||||
|
if cmd[1] == 'respond':
|
||||||
|
player.respond(int(cmd[2]))
|
||||||
|
if cmd[1] == 'choose':
|
||||||
|
player.choose(int(cmd[2]))
|
||||||
|
if cmd[1] == 'scrap':
|
||||||
|
player.scrap(int(cmd[2]))
|
||||||
|
if cmd[1] == 'special':
|
||||||
|
player.special(json.loads(cmd[2]))
|
||||||
|
if cmd[1] == 'gold_rush_discard':
|
||||||
|
player.gold_rush_discard()
|
||||||
|
if cmd[1] == 'buy_gold_rush_card':
|
||||||
|
player.buy_gold_rush_card(int(cmd[2]))
|
||||||
|
# if cmd[1] == 'chat_message':
|
||||||
|
# chat_message(None, cmd[2], player)
|
||||||
|
eventlet.sleep(max(self.replay_speed, 0.1))
|
||||||
|
|
||||||
def notify_room(self, sid=None):
|
def notify_room(self, sid=None):
|
||||||
if len([p for p in self.players if p.character == None]) != 0 or sid:
|
if len([p for p in self.players if p.character == None]) != 0 or sid:
|
||||||
self.sio.emit('room', room=self.name if not sid else sid, data={
|
self.sio.emit('room', room=self.name if not sid else sid, data={
|
||||||
@ -80,15 +136,37 @@ class Game:
|
|||||||
})
|
})
|
||||||
self.sio.emit('debug', room=self.name, data=self.debug)
|
self.sio.emit('debug', room=self.name, data=self.debug)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
commands = ['/debug', '/set_chars', '/suicide', '/nextevent', '/notify', '/show_cards', '/ddc', '/dsh', '/togglebot', '/cancelgame', '/startgame', '/setbotspeed', '/addex', '/setcharacter', '/setevent', '/removecard', '/getcard', '/meinfo', '/gameinfo', '/mebot']
|
commands = [
|
||||||
|
{'cmd':'/debug', 'help':'Toggles the debug mode'},
|
||||||
|
{'cmd':'/set_chars', 'help':'Set how many characters to distribute - sample /set_chars 3'},
|
||||||
|
{'cmd':'/suicide', 'help':'Kills you'},
|
||||||
|
{'cmd':'/nextevent', 'help':'Flip the next event card'},
|
||||||
|
{'cmd':'/notify', 'help':'Send a message to a player - sample /notify player hi!'},
|
||||||
|
{'cmd':'/show_cards', 'help':'View the hand of another - sample /show_cards player'},
|
||||||
|
{'cmd':'/ddc', 'help':'Destroy all cards - sample /ddc player'},
|
||||||
|
{'cmd':'/dsh', 'help':'Set health - sample /dsh player'},
|
||||||
|
# {'cmd':'/togglebot', 'help':''},
|
||||||
|
{'cmd':'/cancelgame', 'help':'Stops the current game'},
|
||||||
|
{'cmd':'/startgame', 'help':'Force starts the game'},
|
||||||
|
{'cmd':'/setbotspeed', 'help':'Changes the bot response time - sample /setbotspeed 0.5'},
|
||||||
|
# {'cmd':'/addex', 'help':''},
|
||||||
|
{'cmd':'/setcharacter', 'help':'Changes your current character - sample /setcharacter Willy The Kid'},
|
||||||
|
{'cmd':'/setevent', 'help':'Changes the event deck - sample /setevent 0 Manette'},
|
||||||
|
{'cmd':'/removecard', 'help':'Remove a card from hand/equip - sample /removecard 0'},
|
||||||
|
{'cmd':'/getcard', 'help':'Get a brand new card - sample /getcard Birra'},
|
||||||
|
{'cmd':'/meinfo', 'help':'Get player data'},
|
||||||
|
{'cmd':'/gameinfo', 'help':'Get game data'},
|
||||||
|
{'cmd':'/mebot', 'help':'Toggles bot mode'},
|
||||||
|
{'cmd':'/getnuggets', 'help':'Adds nuggets to yourself - sample /getnuggets 5'},
|
||||||
|
{'cmd':'/startwithseed', 'help':'start the game with custom seed'}]
|
||||||
self.sio.emit('commands', room=self.name, data=commands)
|
self.sio.emit('commands', room=self.name, data=commands)
|
||||||
else:
|
else:
|
||||||
self.sio.emit('commands', room=self.name, data=['/debug'])
|
self.sio.emit('commands', room=self.name, data=[{'cmd':'/debug', 'help':'Toggles the debug mode'}])
|
||||||
self.sio.emit('spectators', room=self.name, data=len(self.spectators))
|
self.sio.emit('spectators', room=self.name, data=len(self.spectators))
|
||||||
|
|
||||||
def toggle_expansion(self, expansion_name):
|
def toggle_expansion(self, expansion_name):
|
||||||
if not self.started:
|
if not self.started:
|
||||||
print('toggling', expansion_name)
|
print(f'{self.name}: toggling', expansion_name)
|
||||||
if expansion_name in self.expansions:
|
if expansion_name in self.expansions:
|
||||||
self.expansions.remove(expansion_name)
|
self.expansions.remove(expansion_name)
|
||||||
else:
|
else:
|
||||||
@ -113,7 +191,7 @@ class Game:
|
|||||||
self.expansions.append('dodge_city')
|
self.expansions.append('dodge_city')
|
||||||
player.join_game(self)
|
player.join_game(self)
|
||||||
self.players.append(player)
|
self.players.append(player)
|
||||||
print(f'Added player {player.name} to game')
|
print(f'{self.name}: Added player {player.name} to game')
|
||||||
self.notify_room()
|
self.notify_room()
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_joined|{player.name}')
|
self.sio.emit('chat_message', room=self.name, data=f'_joined|{player.name}')
|
||||||
|
|
||||||
@ -133,16 +211,14 @@ class Game:
|
|||||||
self.notify_room()
|
self.notify_room()
|
||||||
if len([p for p in self.players if p.character == None]) == 0:
|
if len([p for p in self.players if p.character == None]) == 0:
|
||||||
for i in range(len(self.players)):
|
for i in range(len(self.players)):
|
||||||
print(self.name)
|
print(self.name, self.players[i].name, self.players[i].character)
|
||||||
print(self.players[i].name)
|
|
||||||
print(self.players[i].character)
|
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}')
|
self.sio.emit('chat_message', room=self.name, data=f'_choose_character|{self.players[i].name}|{self.players[i].character.name}')
|
||||||
self.players[i].prepare()
|
self.players[i].prepare()
|
||||||
for k in range(self.players[i].max_lives):
|
for k in range(self.players[i].max_lives):
|
||||||
self.players[i].hand.append(self.deck.draw())
|
self.players[i].hand.append(self.deck.draw())
|
||||||
self.players[i].notify_self()
|
self.players[i].notify_self()
|
||||||
current_roles = [x.role.name for x in self.players]
|
current_roles = [x.role.name for x in self.players]
|
||||||
random.shuffle(current_roles)
|
self.rng.shuffle(current_roles)
|
||||||
cr = ''
|
cr = ''
|
||||||
for x in current_roles:
|
for x in current_roles:
|
||||||
if (x not in cr):
|
if (x not in cr):
|
||||||
@ -152,14 +228,21 @@ class Game:
|
|||||||
|
|
||||||
def choose_characters(self):
|
def choose_characters(self):
|
||||||
n = self.characters_to_distribute
|
n = self.characters_to_distribute
|
||||||
char_cards = random.sample(characters.all_characters(self.expansions), len(self.players)*n)
|
char_cards = self.rng.sample(characters.all_characters(self.expansions), len(self.players)*n)
|
||||||
for i in range(len(self.players)):
|
for i in range(len(self.players)):
|
||||||
self.players[i].set_available_character(char_cards[i * n : i * n + n])
|
self.players[i].set_available_character(char_cards[i * n : i * n + n])
|
||||||
|
|
||||||
def start_game(self):
|
def start_game(self, SEED=None):
|
||||||
print('GAME IS STARING')
|
|
||||||
if self.started:
|
if self.started:
|
||||||
return
|
return
|
||||||
|
print(f'{self.name}: GAME IS STARING')
|
||||||
|
if SEED == None:
|
||||||
|
import time
|
||||||
|
SEED = int(time.time())
|
||||||
|
print(f'{self.name}: SEED IS {SEED}')
|
||||||
|
self.SEED = SEED
|
||||||
|
self.rpc_log = [f';players;{len(self.players)};{[p.name for p in self.players]};{self.expansions}', f';start_game;{SEED}']
|
||||||
|
self.rng = random.Random(SEED)
|
||||||
self.players_map = {c.name: i for i, c in enumerate(self.players)}
|
self.players_map = {c.name: i for i, c in enumerate(self.players)}
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_starting')
|
self.sio.emit('chat_message', room=self.name, data=f'_starting')
|
||||||
self.sio.emit('start', room=self.name)
|
self.sio.emit('start', room=self.name)
|
||||||
@ -170,6 +253,8 @@ class Game:
|
|||||||
self.initial_players = len(self.players)
|
self.initial_players = len(self.players)
|
||||||
self.distribute_roles()
|
self.distribute_roles()
|
||||||
self.choose_characters()
|
self.choose_characters()
|
||||||
|
if 'gold_rush' in self.expansions:
|
||||||
|
self.notify_gold_rush_shop()
|
||||||
|
|
||||||
def distribute_roles(self):
|
def distribute_roles(self):
|
||||||
available_roles: List[roles.Role] = []
|
available_roles: List[roles.Role] = []
|
||||||
@ -184,7 +269,7 @@ class Game:
|
|||||||
available_roles = available_roles[:len(self.players)]
|
available_roles = available_roles[:len(self.players)]
|
||||||
else:
|
else:
|
||||||
available_roles = [roles.Renegade(), roles.Renegade()]
|
available_roles = [roles.Renegade(), roles.Renegade()]
|
||||||
random.shuffle(available_roles)
|
self.rng.shuffle(available_roles)
|
||||||
for i in range(len(self.players)):
|
for i in range(len(self.players)):
|
||||||
self.players[i].set_role(available_roles[i])
|
self.players[i].set_role(available_roles[i])
|
||||||
if isinstance(available_roles[i], roles.Sheriff) or (len(available_roles) == 3 and isinstance(available_roles[i], roles.Vice)):
|
if isinstance(available_roles[i], roles.Sheriff) or (len(available_roles) == 3 and isinstance(available_roles[i], roles.Vice)):
|
||||||
@ -208,7 +293,7 @@ class Game:
|
|||||||
if self.waiting_for == 0:
|
if self.waiting_for == 0:
|
||||||
attacker.pending_action = pl.PendingAction.PLAY
|
attacker.pending_action = pl.PendingAction.PLAY
|
||||||
attacker.notify_self()
|
attacker.notify_self()
|
||||||
self.attack_in_progress = False
|
self.attack_in_progress = False
|
||||||
if self.pending_winners and not self.someone_won:
|
if self.pending_winners and not self.someone_won:
|
||||||
return self.announces_winners()
|
return self.announces_winners()
|
||||||
|
|
||||||
@ -226,7 +311,7 @@ class Game:
|
|||||||
if self.waiting_for == 0:
|
if self.waiting_for == 0:
|
||||||
attacker.pending_action = pl.PendingAction.PLAY
|
attacker.pending_action = pl.PendingAction.PLAY
|
||||||
attacker.notify_self()
|
attacker.notify_self()
|
||||||
self.attack_in_progress = False
|
self.attack_in_progress = False
|
||||||
if self.pending_winners and not self.someone_won:
|
if self.pending_winners and not self.someone_won:
|
||||||
return self.announces_winners()
|
return self.announces_winners()
|
||||||
|
|
||||||
@ -271,7 +356,13 @@ class Game:
|
|||||||
player.notify_self()
|
player.notify_self()
|
||||||
pls = self.get_alive_players()
|
pls = self.get_alive_players()
|
||||||
nextPlayer = pls[(pls.index(self.players[self.turn])+(len(pls)-len(self.available_cards))) % len(pls)]
|
nextPlayer = pls[(pls.index(self.players[self.turn])+(len(pls)-len(self.available_cards))) % len(pls)]
|
||||||
if nextPlayer == self.players[self.turn]:
|
if len(self.available_cards) == 1:
|
||||||
|
nextPlayer.hand.append(self.available_cards.pop())
|
||||||
|
nextPlayer.notify_self()
|
||||||
|
self.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}')
|
||||||
|
self.players[self.turn].pending_action = pl.PendingAction.PLAY
|
||||||
|
self.players[self.turn].notify_self()
|
||||||
|
elif nextPlayer == self.players[self.turn]:
|
||||||
self.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}')
|
self.sio.emit('emporio', room=self.name, data='{"name":"","cards":[]}')
|
||||||
self.players[self.turn].pending_action = pl.PendingAction.PLAY
|
self.players[self.turn].pending_action = pl.PendingAction.PLAY
|
||||||
self.players[self.turn].notify_self()
|
self.players[self.turn].notify_self()
|
||||||
@ -287,32 +378,38 @@ class Game:
|
|||||||
return self.players[self.players_map[name]]
|
return self.players[self.players_map[name]]
|
||||||
|
|
||||||
def responders_did_respond_resume_turn(self, did_lose=False):
|
def responders_did_respond_resume_turn(self, did_lose=False):
|
||||||
print('did_lose', did_lose)
|
print(f'{self.name}: did_lose', did_lose)
|
||||||
if self.player_bangs > 0 and self.check_event(ce.PerUnPugnoDiCarte):
|
if self.player_bangs > 0 and self.check_event(ce.PerUnPugnoDiCarte):
|
||||||
self.player_bangs -= 1
|
self.player_bangs -= 1
|
||||||
if self.player_bangs >= 1:
|
if self.player_bangs >= 1:
|
||||||
print('bang again')
|
print(f'{self.name}: bang again')
|
||||||
if self.players[self.turn].get_banged(self.deck.event_cards[0]):
|
if self.players[self.turn].get_banged(self.deck.event_cards[0]):
|
||||||
self.players[self.turn].notify_self()
|
self.players[self.turn].notify_self()
|
||||||
else:
|
else:
|
||||||
self.responders_did_respond_resume_turn()
|
self.responders_did_respond_resume_turn()
|
||||||
else:
|
else:
|
||||||
print('ok play turn now')
|
print(f'{self.name}: ok play turn now')
|
||||||
self.player_bangs = 0
|
self.player_bangs = 0
|
||||||
self.players[self.turn].play_turn()
|
self.players[self.turn].play_turn()
|
||||||
elif self.is_russian_roulette_on and self.check_event(ce.RouletteRussa):
|
elif self.is_russian_roulette_on and self.check_event(ce.RouletteRussa):
|
||||||
pls = self.get_alive_players()
|
pls = self.get_alive_players()
|
||||||
if did_lose:
|
if did_lose:
|
||||||
target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
|
target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
|
||||||
print('stop roulette')
|
print(f'{self.name}: stop roulette')
|
||||||
target_pl.lives -= 1
|
target_pl.lives -= 1
|
||||||
|
if len([c for c in target_pl.equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
target_pl.gold_nuggets += 1
|
||||||
|
if target_pl.character.check(self, grch.SimeonPicos):
|
||||||
|
target_pl.gold_nuggets += 1
|
||||||
|
if len([c for c in target_pl.equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
target_pl.hand.append(self.deck.draw(True))
|
||||||
target_pl.notify_self()
|
target_pl.notify_self()
|
||||||
self.is_russian_roulette_on = False
|
self.is_russian_roulette_on = False
|
||||||
self.players[self.turn].play_turn()
|
self.players[self.turn].play_turn()
|
||||||
else:
|
else:
|
||||||
self.player_bangs += 1
|
self.player_bangs += 1
|
||||||
target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
|
target_pl = pls[(pls.index(self.players[self.turn]) + self.player_bangs) % len(pls)]
|
||||||
print(f'next in line {target_pl.name}')
|
print(f'{self.name}: next in line {target_pl.name}')
|
||||||
if target_pl.get_banged(self.deck.event_cards[0]):
|
if target_pl.get_banged(self.deck.event_cards[0]):
|
||||||
target_pl.notify_self()
|
target_pl.notify_self()
|
||||||
else:
|
else:
|
||||||
@ -322,11 +419,12 @@ class Game:
|
|||||||
if self.ready_count == self.waiting_for:
|
if self.ready_count == self.waiting_for:
|
||||||
self.waiting_for = 0
|
self.waiting_for = 0
|
||||||
self.ready_count = 0
|
self.ready_count = 0
|
||||||
|
self.attack_in_progress = False
|
||||||
if self.pending_winners and not self.someone_won:
|
if self.pending_winners and not self.someone_won:
|
||||||
return self.announces_winners()
|
return self.announces_winners()
|
||||||
if self.dalton_on:
|
if self.dalton_on:
|
||||||
self.dalton_on = False
|
self.dalton_on = False
|
||||||
print(f'notifying {self.players[self.turn].name} about his turn')
|
print(f'{self.name}: notifying {self.players[self.turn].name} about his turn')
|
||||||
self.players[self.turn].play_turn()
|
self.players[self.turn].play_turn()
|
||||||
else:
|
else:
|
||||||
self.players[self.turn].pending_action = pl.PendingAction.PLAY
|
self.players[self.turn].pending_action = pl.PendingAction.PLAY
|
||||||
@ -334,19 +432,22 @@ class Game:
|
|||||||
|
|
||||||
def announces_winners(self, winners=None):
|
def announces_winners(self, winners=None):
|
||||||
if (winners is None):
|
if (winners is None):
|
||||||
print('WE HAVE A WINNER - pending winners')
|
print(f'{self.name}: WE HAVE A WINNER - pending winners')
|
||||||
else:
|
else:
|
||||||
print('WE HAVE A WINNER')
|
print(f'{self.name}: WE HAVE A WINNER')
|
||||||
for p in self.get_alive_players():
|
for p in self.players:
|
||||||
if winners is None:
|
if winners is None:
|
||||||
p.win_status = p in self.pending_winners
|
p.win_status = p in self.pending_winners
|
||||||
else:
|
else:
|
||||||
p.win_status = p in winners
|
p.win_status = p in winners
|
||||||
if p.win_status:
|
if p.win_status and not (isinstance(p.role, roles.Renegade) and p.is_dead):
|
||||||
if not self.someone_won:
|
if not self.someone_won:
|
||||||
self.someone_won = True
|
self.someone_won = True
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}')
|
self.sio.emit('chat_message', room=self.name, data=f'_won|{p.name}|{p.role.name}')
|
||||||
p.notify_self()
|
p.notify_self()
|
||||||
|
if hasattr(self.sio, 'is_fake'):
|
||||||
|
print('announces_winners(): Running for tests, you will have to call reset manually!')
|
||||||
|
return
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_lobby_reset|{5-i}')
|
self.sio.emit('chat_message', room=self.name, data=f'_lobby_reset|{5-i}')
|
||||||
eventlet.sleep(1)
|
eventlet.sleep(1)
|
||||||
@ -361,7 +462,7 @@ class Game:
|
|||||||
if self.players[self.turn].is_dead:
|
if self.players[self.turn].is_dead:
|
||||||
pl = sorted(self.get_dead_players(), key=lambda x:x.death_turn)[0]
|
pl = sorted(self.get_dead_players(), key=lambda x:x.death_turn)[0]
|
||||||
if self.check_event(ce.DeadMan) and not self.did_resuscitate_deadman and pl == self.players[self.turn]:
|
if self.check_event(ce.DeadMan) and not self.did_resuscitate_deadman and pl == self.players[self.turn]:
|
||||||
print(f'{self.players[self.turn]} is dead, revive')
|
print(f'{self.name}: {self.players[self.turn]} is dead, revive')
|
||||||
self.did_resuscitate_deadman = True
|
self.did_resuscitate_deadman = True
|
||||||
pl.is_dead = False
|
pl.is_dead = False
|
||||||
pl.is_ghost = False
|
pl.is_ghost = False
|
||||||
@ -370,16 +471,16 @@ class Game:
|
|||||||
pl.hand.append(self.deck.draw())
|
pl.hand.append(self.deck.draw())
|
||||||
pl.notify_self()
|
pl.notify_self()
|
||||||
elif self.check_event(ceh.CittaFantasma):
|
elif self.check_event(ceh.CittaFantasma):
|
||||||
print(f'{self.players[self.turn]} is dead, event ghost')
|
print(f'{self.name}: {self.players[self.turn]} is dead, event ghost')
|
||||||
self.players[self.turn].is_ghost = True
|
self.players[self.turn].is_ghost = True
|
||||||
else:
|
else:
|
||||||
print(f'{self.players[self.turn]} is dead, next turn')
|
print(f'{self.name}: {self.players[self.turn]} is dead, next turn')
|
||||||
return self.next_turn()
|
return self.next_turn()
|
||||||
self.player_bangs = 0
|
self.player_bangs = 0
|
||||||
if isinstance(self.players[self.turn].role, roles.Sheriff) or ((self.initial_players == 3 and isinstance(self.players[self.turn].role, roles.Vice) and not self.players[self.turn].is_ghost) or (self.initial_players == 3 and any([p for p in self.players if p.is_dead and p.role.name == 'Vice']) and isinstance(self.players[self.turn].role, roles.Renegade))):
|
if isinstance(self.players[self.turn].role, roles.Sheriff) or ((self.initial_players == 3 and isinstance(self.players[self.turn].role, roles.Vice) and not self.players[self.turn].is_ghost) or (self.initial_players == 3 and any([p for p in self.players if p.is_dead and p.role.name == 'Vice']) and isinstance(self.players[self.turn].role, roles.Renegade))):
|
||||||
self.deck.flip_event()
|
self.deck.flip_event()
|
||||||
if len(self.deck.event_cards) > 0 and self.deck.event_cards[0] != None:
|
if len(self.deck.event_cards) > 0 and self.deck.event_cards[0] != None:
|
||||||
print(f'flip new event {self.deck.event_cards[0].name}')
|
print(f'{self.name}: flip new event {self.deck.event_cards[0].name}')
|
||||||
self.sio.emit('chat_message', room=self.name, data={'color': f'orange','text':f'_flip_event|{self.deck.event_cards[0].name}'})
|
self.sio.emit('chat_message', room=self.name, data={'color': f'orange','text':f'_flip_event|{self.deck.event_cards[0].name}'})
|
||||||
if self.check_event(ce.DeadMan):
|
if self.check_event(ce.DeadMan):
|
||||||
self.did_resuscitate_deadman = False
|
self.did_resuscitate_deadman = False
|
||||||
@ -395,9 +496,10 @@ class Game:
|
|||||||
if len(most_hurt) > 0:
|
if len(most_hurt) > 0:
|
||||||
hurt_players = [p for p in self.players if p.lives == min(most_hurt)]
|
hurt_players = [p for p in self.players if p.lives == min(most_hurt)]
|
||||||
for p in hurt_players:
|
for p in hurt_players:
|
||||||
p.lives += 1
|
if p.lives != p.max_lives:
|
||||||
self.sio.emit('chat_message', room=self.name, data=f'_doctor_heal|{p.name}')
|
p.lives += 1
|
||||||
p.notify_self()
|
self.sio.emit('chat_message', room=self.name, data=f'_doctor_heal|{p.name}')
|
||||||
|
p.notify_self()
|
||||||
elif self.check_event(ceh.IDalton):
|
elif self.check_event(ceh.IDalton):
|
||||||
self.waiting_for = 0
|
self.waiting_for = 0
|
||||||
self.ready_count = 0
|
self.ready_count = 0
|
||||||
@ -417,14 +519,14 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
self.responders_did_respond_resume_turn()
|
self.responders_did_respond_resume_turn()
|
||||||
else:
|
else:
|
||||||
print(f'notifying {self.players[self.turn].name} about his turn')
|
print(f'{self.name}: notifying {self.players[self.turn].name} about his turn')
|
||||||
self.players[self.turn].play_turn()
|
self.players[self.turn].play_turn()
|
||||||
|
|
||||||
def next_turn(self):
|
def next_turn(self):
|
||||||
if self.shutting_down: return
|
if self.shutting_down: return
|
||||||
print(f'{self.players[self.turn].name} invoked next turn')
|
print(f'{self.name}: {self.players[self.turn].name} invoked next turn')
|
||||||
if self.pending_winners and not self.someone_won:
|
if self.pending_winners and not self.someone_won:
|
||||||
return self.announces_winners()
|
return self.announces_winners()
|
||||||
pls = self.get_alive_players()
|
pls = self.get_alive_players()
|
||||||
if len(pls) > 0:
|
if len(pls) > 0:
|
||||||
if self.check_event(ceh.CorsaAllOro):
|
if self.check_event(ceh.CorsaAllOro):
|
||||||
@ -441,8 +543,14 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
self.sio.emit('event_card', room=room, data=None)
|
self.sio.emit('event_card', room=room, data=None)
|
||||||
|
|
||||||
|
def notify_gold_rush_shop(self, sid=None):
|
||||||
|
if 'gold_rush' in self.expansions and self.deck and self.deck.shop_cards and len(self.deck.shop_cards) > 0:
|
||||||
|
room = self.name if sid == None else sid
|
||||||
|
print(f'{self.name}: gold_rush_shop room={room}, data={self.deck.shop_cards}')
|
||||||
|
self.sio.emit('gold_rush_shop', room=room, data=json.dumps(self.deck.shop_cards, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
def notify_scrap_pile(self, sid=None):
|
def notify_scrap_pile(self, sid=None):
|
||||||
print('scrap')
|
print(f'{self.name}: scrap')
|
||||||
room = self.name if sid == None else sid
|
room = self.name if sid == None else sid
|
||||||
if self.deck.peek_scrap_pile():
|
if self.deck.peek_scrap_pile():
|
||||||
self.sio.emit('scrap', room=room, data=self.deck.peek_scrap_pile().__dict__)
|
self.sio.emit('scrap', room=room, data=self.deck.peek_scrap_pile().__dict__)
|
||||||
@ -450,7 +558,7 @@ class Game:
|
|||||||
self.sio.emit('scrap', room=room, data=None)
|
self.sio.emit('scrap', room=room, data=None)
|
||||||
|
|
||||||
def handle_disconnect(self, player: pl.Player):
|
def handle_disconnect(self, player: pl.Player):
|
||||||
print(f'player {player.name} left the game {self.name}')
|
print(f'{self.name}: player {player.name} left the game')
|
||||||
if player in self.spectators:
|
if player in self.spectators:
|
||||||
self.spectators.remove(player)
|
self.spectators.remove(player)
|
||||||
self.sio.emit('spectators', room=self.name, data=len(self.spectators))
|
self.sio.emit('spectators', room=self.name, data=len(self.spectators))
|
||||||
@ -462,7 +570,7 @@ class Game:
|
|||||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||||
eventlet.sleep(5)
|
eventlet.sleep(5)
|
||||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||||
print(f'no players left in game {self.name}, shutting down')
|
print(f'{self.name}: no players left in game, shutting down')
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
self.players = []
|
self.players = []
|
||||||
self.spectators = []
|
self.spectators = []
|
||||||
@ -470,6 +578,7 @@ class Game:
|
|||||||
return True
|
return True
|
||||||
eventlet.sleep(15) # he may reconnect
|
eventlet.sleep(15) # he may reconnect
|
||||||
if player.is_bot:
|
if player.is_bot:
|
||||||
|
player.was_player = False
|
||||||
if len(player.available_characters) > 0:
|
if len(player.available_characters) > 0:
|
||||||
player.set_available_character(player.available_characters)
|
player.set_available_character(player.available_characters)
|
||||||
player.bot_spin()
|
player.bot_spin()
|
||||||
@ -479,7 +588,7 @@ class Game:
|
|||||||
# player.lives = 0
|
# player.lives = 0
|
||||||
# self.players.remove(player)
|
# self.players.remove(player)
|
||||||
if len([p for p in self.players if not p.is_bot]) == 0:
|
if len([p for p in self.players if not p.is_bot]) == 0:
|
||||||
print(f'no players left in game {self.name}, shutting down')
|
print(f'{self.name}: no players left in game, shutting down')
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
self.players = []
|
self.players = []
|
||||||
self.spectators = []
|
self.spectators = []
|
||||||
@ -491,18 +600,26 @@ class Game:
|
|||||||
if not player in self.players or player.is_ghost: return
|
if not player in self.players or player.is_ghost: return
|
||||||
self.is_handling_death = True
|
self.is_handling_death = True
|
||||||
import bang.expansions.dodge_city.characters as chd
|
import bang.expansions.dodge_city.characters as chd
|
||||||
print(player.attacker)
|
print(f'{self.name}: the killer is {player.attacker}')
|
||||||
|
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Ricercato)]) > 0 and player.attacker and player.attacker in self.players:
|
||||||
|
player.attacker.gold_nuggets += 1
|
||||||
|
player.attacker.hand.append(self.deck.draw(True))
|
||||||
|
player.attacker.hand.append(self.deck.draw(True))
|
||||||
|
player.attacker.notify_self()
|
||||||
|
# se lo sceriffo uccide il proprio vice
|
||||||
if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice):
|
if player.attacker and player.attacker in self.players and isinstance(player.attacker.role, roles.Sheriff) and isinstance(player.role, roles.Vice):
|
||||||
for i in range(len(player.attacker.hand)):
|
for i in range(len(player.attacker.hand)):
|
||||||
self.deck.scrap(player.attacker.hand.pop(), True)
|
self.deck.scrap(player.attacker.hand.pop(), True)
|
||||||
for i in range(len(player.attacker.equipment)):
|
for i in range(len(player.attacker.equipment)):
|
||||||
self.deck.scrap(player.attacker.equipment.pop(), True)
|
self.deck.scrap(player.attacker.equipment.pop(), True)
|
||||||
|
for i in range(len(player.attacker.gold_rush_equipment)):
|
||||||
|
self.deck.shop_deck.append(player.attacker.gold_rush_equipment.pop())
|
||||||
player.attacker.notify_self()
|
player.attacker.notify_self()
|
||||||
elif player.attacker and player.attacker in self.players and (isinstance(player.role, roles.Outlaw) or self.initial_players == 3):
|
elif player.attacker and player.attacker in self.players and (isinstance(player.role, roles.Outlaw) or self.initial_players == 3):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
player.attacker.hand.append(self.deck.draw(True))
|
player.attacker.hand.append(self.deck.draw(True))
|
||||||
player.attacker.notify_self()
|
player.attacker.notify_self()
|
||||||
print(f'player {player.name} died')
|
print(f'{self.name}: player {player.name} died')
|
||||||
if self.waiting_for > 0 and player.pending_action == pl.PendingAction.RESPOND:
|
if self.waiting_for > 0 and player.pending_action == pl.PendingAction.RESPOND:
|
||||||
self.responders_did_respond_resume_turn()
|
self.responders_did_respond_resume_turn()
|
||||||
player.pending_action = pl.PendingAction.WAIT
|
player.pending_action = pl.PendingAction.WAIT
|
||||||
@ -529,17 +646,22 @@ class Game:
|
|||||||
p.notify_self()
|
p.notify_self()
|
||||||
# self.players_map = {c.name: i for i, c in enumerate(self.players)}
|
# self.players_map = {c.name: i for i, c in enumerate(self.players)}
|
||||||
if self.started:
|
if self.started:
|
||||||
print('Check win status')
|
print(f'{self.name}: Check win status')
|
||||||
attacker_role = None
|
attacker_role = None
|
||||||
if player.attacker and player.attacker in self.players:
|
if player.attacker and player.attacker in self.players:
|
||||||
attacker_role = player.attacker.role
|
attacker_role = player.attacker.role
|
||||||
winners = [p for p in self.players if p.role != None and p.role.on_player_death(self.get_alive_players(), initial_players=self.initial_players, dead_role=player.role, attacker_role=attacker_role)]
|
winners = [p for p in self.players if p.role != None and p.role.on_player_death(self.get_alive_players(), initial_players=self.initial_players, dead_role=player.role, attacker_role=attacker_role)]
|
||||||
#print(f'win check: ready-{self.ready_count} waiting-{self.waiting_for} winners:{len(winners)}')
|
|
||||||
if not self.attack_in_progress and len(winners) > 0 and not self.someone_won:
|
if not self.attack_in_progress and len(winners) > 0 and not self.someone_won:
|
||||||
return self.announces_winners(winners)
|
return self.announces_winners(winners)
|
||||||
elif len(winners) > 0 and not self.someone_won: # non tutti hanno risposto, ma ci sono vincitori.
|
elif len(winners) > 0 and not self.someone_won: # non tutti hanno risposto, ma ci sono vincitori.
|
||||||
self.pending_winners = winners
|
self.pending_winners = winners
|
||||||
|
|
||||||
|
for i in range(len(player.gold_rush_equipment)):
|
||||||
|
self.deck.shop_deck.append(player.gold_rush_equipment.pop()) # vulture sam doesnt get these cards
|
||||||
|
|
||||||
|
#il giocatore quando muore perde tutte le pepite se non è pistolero ombra
|
||||||
|
player.gold_nuggets = 0
|
||||||
|
|
||||||
vulture = [p for p in self.get_alive_players() if p.character.check(self, characters.VultureSam)]
|
vulture = [p for p in self.get_alive_players() if p.character.check(self, characters.VultureSam)]
|
||||||
if len(vulture) == 0:
|
if len(vulture) == 0:
|
||||||
for i in range(len(player.hand)):
|
for i in range(len(player.hand)):
|
||||||
@ -578,26 +700,25 @@ class Game:
|
|||||||
herb[i].hand.append(self.deck.draw(True))
|
herb[i].hand.append(self.deck.draw(True))
|
||||||
herb[i].hand.append(self.deck.draw(True))
|
herb[i].hand.append(self.deck.draw(True))
|
||||||
herb[i].notify_self()
|
herb[i].notify_self()
|
||||||
|
|
||||||
self.is_handling_death = False
|
self.is_handling_death = False
|
||||||
if corpse.is_my_turn:
|
if corpse.is_my_turn:
|
||||||
corpse.is_my_turn = False
|
corpse.is_my_turn = False
|
||||||
corpse.notify_self()
|
corpse.notify_self()
|
||||||
self.next_turn()
|
self.next_turn()
|
||||||
|
|
||||||
|
|
||||||
def check_event(self, ev):
|
def check_event(self, ev):
|
||||||
if self.deck == None or len(self.deck.event_cards) == 0: return False
|
if self.deck == None or len(self.deck.event_cards) == 0: return False
|
||||||
return isinstance(self.deck.event_cards[0], ev)
|
return isinstance(self.deck.event_cards[0], ev)
|
||||||
|
|
||||||
def get_visible_players(self, player: pl.Player):
|
def get_visible_players(self, player: pl.Player): # returns a dictionary because we need to add the distance
|
||||||
pls = self.get_alive_players()
|
pls = self.get_alive_players()
|
||||||
if len(pls) == 0 or player not in pls: return []
|
if len(pls) == 0 or player not in pls: return []
|
||||||
i = pls.index(player)
|
i = pls.index(player)
|
||||||
sight = player.get_sight()
|
|
||||||
mindist = 99 if not self.check_event(ce.Agguato) else 1
|
mindist = 99 if not self.check_event(ce.Agguato) else 1
|
||||||
return [{
|
return [{
|
||||||
'name': pls[j].name,
|
'name': pls[j].name,
|
||||||
'dist': min([abs(i - j), (i+ abs(j-len(pls))), (j+ abs(i-len(pls))), mindist]) + pls[j].get_visibility() - (player.get_sight(countWeapon=False)-1),
|
'dist': min([abs(i - j), (i+ abs(j-len(pls))), (j+ abs(i-len(pls))), mindist]) + pls[j].get_visibility(),
|
||||||
'lives': pls[j].lives,
|
'lives': pls[j].lives,
|
||||||
'max_lives': pls[j].max_lives,
|
'max_lives': pls[j].max_lives,
|
||||||
'is_sheriff': isinstance(pls[j].role, roles.Sheriff),
|
'is_sheriff': isinstance(pls[j].role, roles.Sheriff),
|
||||||
@ -620,8 +741,10 @@ class Game:
|
|||||||
'name': p.name,
|
'name': p.name,
|
||||||
'ncards': len(p.hand),
|
'ncards': len(p.hand),
|
||||||
'equipment': [e.__dict__ for e in p.equipment],
|
'equipment': [e.__dict__ for e in p.equipment],
|
||||||
|
'gold_rush_equipment': [e.__dict__ for e in p.gold_rush_equipment],
|
||||||
'lives': p.lives,
|
'lives': p.lives,
|
||||||
'max_lives': p.max_lives,
|
'max_lives': p.max_lives,
|
||||||
|
'gold_nuggets': p.gold_nuggets,
|
||||||
'is_sheriff': isinstance(p.role, roles.Sheriff),
|
'is_sheriff': isinstance(p.role, roles.Sheriff),
|
||||||
'is_my_turn': p.is_my_turn,
|
'is_my_turn': p.is_my_turn,
|
||||||
'pending_action': p.pending_action,
|
'pending_action': p.pending_action,
|
||||||
|
@ -10,7 +10,10 @@ import bang.characters as chars
|
|||||||
import bang.expansions.dodge_city.characters as chd
|
import bang.expansions.dodge_city.characters as chd
|
||||||
import bang.expansions.fistful_of_cards.card_events as ce
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
import bang.expansions.high_noon.card_events as ceh
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
import bang.expansions.gold_rush.shop_cards as grc
|
||||||
|
import bang.expansions.gold_rush.characters as grch
|
||||||
import eventlet
|
import eventlet
|
||||||
|
from typing import List
|
||||||
|
|
||||||
class PendingAction(IntEnum):
|
class PendingAction(IntEnum):
|
||||||
PICK = 0
|
PICK = 0
|
||||||
@ -33,8 +36,8 @@ class Player:
|
|||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.hand: cs.Card = []
|
self.hand: List[cs.Card] = []
|
||||||
self.equipment: cs.Card = []
|
self.equipment: List[cs.Card] = []
|
||||||
self.role: r.Role = None
|
self.role: r.Role = None
|
||||||
self.character: chars.Character = None
|
self.character: chars.Character = None
|
||||||
self.real_character: chars.Character = None
|
self.real_character: chars.Character = None
|
||||||
@ -47,7 +50,7 @@ class Player:
|
|||||||
self.can_play_ranch = True
|
self.can_play_ranch = True
|
||||||
self.is_playing_ranch = False
|
self.is_playing_ranch = False
|
||||||
self.pending_action: PendingAction = None
|
self.pending_action: PendingAction = None
|
||||||
self.available_characters = []
|
self.available_characters: List[chars.Character] = []
|
||||||
self.was_shot = False
|
self.was_shot = False
|
||||||
self.on_pick_cb = None
|
self.on_pick_cb = None
|
||||||
self.on_failed_response_cb = None
|
self.on_failed_response_cb = None
|
||||||
@ -70,10 +73,15 @@ class Player:
|
|||||||
self.death_turn = 0
|
self.death_turn = 0
|
||||||
self.noStar = False
|
self.noStar = False
|
||||||
self.can_play_vendetta = True
|
self.can_play_vendetta = True
|
||||||
|
self.can_play_again_don_bell = True
|
||||||
self.is_giving_life = False
|
self.is_giving_life = False
|
||||||
self.choose_text = 'choose_card_to_get'
|
self.choose_text = 'choose_card_to_get'
|
||||||
self.using_rimbalzo = 0 # 0 no, 1 scegli giocatore, 2 scegli carta
|
self.using_rimbalzo = 0 # 0 no, 1 scegli giocatore, 2 scegli carta
|
||||||
self.bang_used = 0
|
self.bang_used = 0
|
||||||
|
self.gold_nuggets = 0
|
||||||
|
self.gold_rush_equipment: List[grc.ShopCard] = []
|
||||||
|
self.was_player = False
|
||||||
|
self.setaccio_count = 0
|
||||||
|
|
||||||
def join_game(self, game):
|
def join_game(self, game):
|
||||||
self.game = game
|
self.game = game
|
||||||
@ -91,7 +99,11 @@ class Player:
|
|||||||
def set_character(self, character: str):
|
def set_character(self, character: str):
|
||||||
print(self.available_characters, character)
|
print(self.available_characters, character)
|
||||||
if self.character == None:
|
if self.character == None:
|
||||||
self.character = next(x for x in self.available_characters if x.name == character)
|
try:
|
||||||
|
self.character = next(x for x in self.available_characters if x.name == character)
|
||||||
|
except:
|
||||||
|
# fix for wrong character encoding in the first part of some characters like Jose delgrado
|
||||||
|
self.character = next(x for x in self.available_characters if x.name.split()[1] == character.split()[1])
|
||||||
if 'high_noon' in self.game.expansions:
|
if 'high_noon' in self.game.expansions:
|
||||||
# questo viene utilizzato per la carta nuova identità
|
# questo viene utilizzato per la carta nuova identità
|
||||||
self.not_chosen_character = next(x for x in self.available_characters if x.name != character)
|
self.not_chosen_character = next(x for x in self.available_characters if x.name != character)
|
||||||
@ -128,7 +140,9 @@ class Player:
|
|||||||
self.sio.emit('characters', room=self.sid, data=json.dumps(
|
self.sio.emit('characters', room=self.sid, data=json.dumps(
|
||||||
available, default=lambda o: o.__dict__))
|
available, default=lambda o: o.__dict__))
|
||||||
else:
|
else:
|
||||||
self.set_character(available[randrange(0, len(available))].name)
|
char_name = available[randrange(0, len(available))].name
|
||||||
|
self.game.rpc_log.append(f'{self.name};set_character;{char_name}')
|
||||||
|
self.set_character(char_name)
|
||||||
|
|
||||||
def notify_card(self, player, card, message=''):
|
def notify_card(self, player, card, message=''):
|
||||||
try:
|
try:
|
||||||
@ -169,13 +183,21 @@ class Player:
|
|||||||
self.hand.append(self.game.deck.draw(True))
|
self.hand.append(self.game.deck.draw(True))
|
||||||
if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
|
if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
|
||||||
print('dying, attacker', self.attacker)
|
print('dying, attacker', self.attacker)
|
||||||
if self.character.check(self.game, chars.SidKetchum) and len(self.hand) > 1:
|
if self.gold_nuggets >= 2 and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Zaino)]) > 0:
|
||||||
|
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, chars.SidKetchum) and len(self.hand) > 1 and self.lives == 0:
|
||||||
|
if self.game.players[self.game.turn] != self:
|
||||||
|
self.game.players[self.game.turn].pending_action = PendingAction.WAIT
|
||||||
|
self.game.players[self.game.turn].notify_self()
|
||||||
|
self.scrapped_cards = 0
|
||||||
|
self.previous_pending_action = self.pending_action
|
||||||
|
self.pending_action = PendingAction.CHOOSE
|
||||||
|
self.choose_text = 'choose_sid_scrap'
|
||||||
|
self.available_cards = self.hand
|
||||||
self.lives += 1
|
self.lives += 1
|
||||||
#TODO Sid dovrebbe poter decidere cosa scartare
|
|
||||||
self.game.deck.scrap(self.hand.pop(
|
|
||||||
randrange(0, len(self.hand))), True)
|
|
||||||
self.game.deck.scrap(self.hand.pop(
|
|
||||||
randrange(0, len(self.hand))), True)
|
|
||||||
ser = self.__dict__.copy()
|
ser = self.__dict__.copy()
|
||||||
ser.pop('game')
|
ser.pop('game')
|
||||||
ser.pop('sio')
|
ser.pop('sio')
|
||||||
@ -186,6 +208,9 @@ class Player:
|
|||||||
if self.attacker:
|
if self.attacker:
|
||||||
ser['attacker'] = self.attacker.name
|
ser['attacker'] = self.attacker.name
|
||||||
ser['sight'] = self.get_sight()
|
ser['sight'] = self.get_sight()
|
||||||
|
ser['can_gold_rush_discard'] = len([p for p in self.game.get_alive_players() if p != self and len([e for e in p.gold_rush_equipment if e.number <= self.gold_nuggets + 1]) > 0]) > 0
|
||||||
|
if self.character:
|
||||||
|
ser['gold_rush_discount'] = 1 if self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1 else 0
|
||||||
ser['lives'] = max(ser['lives'], 0)
|
ser['lives'] = max(ser['lives'], 0)
|
||||||
|
|
||||||
if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
|
if self.lives <= 0 and self.max_lives > 0 and not self.is_dead:
|
||||||
@ -215,32 +240,49 @@ class Player:
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if self.pending_action == PendingAction.PICK:
|
if self.pending_action == PendingAction.PICK:
|
||||||
|
self.game.rpc_log.append(f'{self.name};pick;')
|
||||||
self.pick()
|
self.pick()
|
||||||
elif self.pending_action == PendingAction.DRAW:
|
elif self.pending_action == PendingAction.DRAW:
|
||||||
|
self.game.rpc_log.append(f'{self.name};draw;')
|
||||||
self.draw('')
|
self.draw('')
|
||||||
elif self.pending_action == PendingAction.PLAY:
|
elif self.pending_action == PendingAction.PLAY:
|
||||||
non_blocked_cards = [card for card in self.hand if (not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette)]
|
non_blocked_cards = [card for card in self.hand if (not self.game.check_event(ceh.Manette) or card.suit == self.committed_suit_manette)]
|
||||||
equippables = [c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not any([type(c) == type(x) for x in self.equipment])]
|
equippables = [c for c in non_blocked_cards if (c.is_equipment or c.usable_next_turn) and not isinstance(c, cs.Prigione) and not any([type(c) == type(x) and not (c.is_weapon and c.must_be_used) for x in self.equipment])]
|
||||||
misc = [c for c in non_blocked_cards if (isinstance(c, cs.WellsFargo) or isinstance(c, cs.Indiani) or isinstance(c, cs.Gatling) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or (isinstance(c, cs.Birra) and self.lives < self.max_lives and not self.game.check_event(ceh.IlReverendo)) or (c.need_with and len(self.hand) > 1 and not c.need_target and not (isinstance(c, csd.Whisky) and self.lives == self.max_lives)))
|
misc = [c for c in non_blocked_cards if not c.need_target and (isinstance(c, cs.WellsFargo) or isinstance(c, cs.Indiani) or isinstance(c, cs.Gatling) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or ((isinstance(c, cs.Birra) and self.lives < self.max_lives or c.must_be_used) and not self.game.check_event(ceh.IlReverendo)) or (c.need_with and len(self.hand) > 1 and not (isinstance(c, csd.Whisky) and self.lives == self.max_lives)))
|
||||||
and not (not c.can_be_used_now and self.game.check_event(ce.IlGiudice))]
|
and not (not c.can_be_used_now and self.game.check_event(ce.IlGiudice)) and not c.is_equipment]
|
||||||
need_target = [c for c in non_blocked_cards if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (
|
need_target = [c for c in non_blocked_cards if c.need_target and c.can_be_used_now and not (c.need_with and len(self.hand) < 2) and not (type(c) == type(cs.Bang) and (self.game.check_event(ceh.Sermone) or (self.has_played_bang and (not any([isinstance(c, cs.Volcanic) for c in self.equipment]) or self.game.check_event(ce.Lazo))))) and not (isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice)) or isinstance(c, cs.Duello) or isinstance(c, cs.CatBalou) or isinstance(c, csd.Pugno)]
|
||||||
(self.game.check_event(ceh.Sermone) or self.has_played_bang and not (any([isinstance(c, cs.Volcanic) for c in self.equipment]) and type(c) == type(cs.Bang)
|
|
||||||
) and not self.game.check_event(ce.Lazo))) and not ( isinstance(c, cs.Prigione) and self.game.check_event(ce.IlGiudice))]
|
|
||||||
green_cards = [c for c in self.equipment if not self.game.check_event(ce.Lazo) and not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now]
|
green_cards = [c for c in self.equipment if not self.game.check_event(ce.Lazo) and not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now]
|
||||||
|
if self.game.debug:
|
||||||
|
print(f'hand: {self.hand}')
|
||||||
|
print(f'non_blocked: {non_blocked_cards}')
|
||||||
|
print(f'equippables: {equippables}')
|
||||||
|
print(f'misc: {misc}')
|
||||||
|
print(f'need_target: {need_target}')
|
||||||
|
print(f'green_cards: {green_cards}')
|
||||||
|
if self.gold_nuggets > 0 and any([c.number <= self.gold_nuggets for c in self.game.deck.shop_cards]):
|
||||||
|
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
|
||||||
if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice):
|
if len(equippables) > 0 and not self.game.check_event(ce.IlGiudice):
|
||||||
for c in equippables:
|
for c in equippables:
|
||||||
if self.play_card(self.hand.index(c)):
|
if self.play_card(self.hand.index(c)):
|
||||||
return
|
return
|
||||||
elif len(misc) > 0:
|
elif len(misc) > 0:
|
||||||
for c in misc:
|
for c in misc:
|
||||||
if c.need_with and self.play_card(self.hand.index(c), _with=sample([j for j in range(len(self.hand)) if j != self.hand.index(c)], 1)[0]):
|
if c.need_with and len(self.hand) > 1 and self.play_card(self.hand.index(c), _with=sample([j for j in range(len(self.hand)) if j != self.hand.index(c)], 1)[0]):
|
||||||
return
|
return
|
||||||
elif self.play_card(self.hand.index(c)):
|
elif self.play_card(self.hand.index(c)):
|
||||||
return
|
return
|
||||||
elif len(need_target) > 0:
|
elif len(need_target) > 0:
|
||||||
for c in need_target:
|
for c in need_target:
|
||||||
_range = self.get_sight() if c.name == 'Bang!' or c.name == "Pepperbox" else c.range
|
_range = self.get_sight() if c.name == 'Bang!' or c.name == "Pepperbox" else c.range
|
||||||
others = [p for p in self.game.get_visible_players(self) if _range >= p['dist'] and not (isinstance(self.role, r.Vice) and p['is_sheriff']) and p['lives'] > 0 and not ((isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico)) and p['cards'] == 0) and not (p['is_sheriff'] and isinstance(c, cs.Prigione))]
|
others = [p for p in self.game.get_visible_players(self) if _range >= p['dist'] and not (isinstance(self.role, r.Vice) and p['is_sheriff'] and not c.must_be_used) and p['lives'] > 0 and not
|
||||||
|
((isinstance(c, cs.CatBalou) or isinstance(c, cs.Panico)) and p['cards'] == 0)
|
||||||
|
and not (p['is_sheriff'] and isinstance(c, cs.Prigione))]
|
||||||
|
if (isinstance(c, cs.Panico) or isinstance(c, cs.Panico))and len(self.equipment) > 0:
|
||||||
|
others.append({'name': self.name, 'is_sheriff': isinstance(self.role, r.Sheriff)})
|
||||||
if len(others) == 0 or c not in self.hand:
|
if len(others) == 0 or c not in self.hand:
|
||||||
continue
|
continue
|
||||||
target = others[randrange(0, len(others))]
|
target = others[randrange(0, len(others))]
|
||||||
@ -249,7 +291,7 @@ class Player:
|
|||||||
if not c.need_with:
|
if not c.need_with:
|
||||||
if self.play_card(self.hand.index(c), against=target['name']):
|
if self.play_card(self.hand.index(c), against=target['name']):
|
||||||
return
|
return
|
||||||
else:
|
elif len(self.hand) > 1:
|
||||||
if self.play_card(self.hand.index(c), against=target['name'], _with=sample([j for j in range(len(self.hand)) if j != self.hand.index(c)], 1)[0]):
|
if self.play_card(self.hand.index(c), against=target['name'], _with=sample([j for j in range(len(self.hand)) if j != self.hand.index(c)], 1)[0]):
|
||||||
return
|
return
|
||||||
elif len(green_cards) > 0:
|
elif len(green_cards) > 0:
|
||||||
@ -270,42 +312,60 @@ class Player:
|
|||||||
return
|
return
|
||||||
break
|
break
|
||||||
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
||||||
|
if maxcards == self.lives and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Cinturone)]) > 0:
|
||||||
|
maxcards = 8
|
||||||
if len(self.hand) > maxcards:
|
if len(self.hand) > maxcards:
|
||||||
|
self.game.rpc_log.append(f'{self.name};scrap;{0}')
|
||||||
self.scrap(0)
|
self.scrap(0)
|
||||||
else:
|
else:
|
||||||
|
self.game.rpc_log.append(f'{self.name};end_turn')
|
||||||
self.end_turn()
|
self.end_turn()
|
||||||
elif self.pending_action == PendingAction.RESPOND:
|
elif self.pending_action == PendingAction.RESPOND:
|
||||||
did_respond = False
|
did_respond = False
|
||||||
for i in range(len(self.hand)):
|
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)):
|
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)
|
self.respond(i)
|
||||||
did_respond = True
|
did_respond = True
|
||||||
break
|
break
|
||||||
for i in range(len(self.equipment)):
|
for i in range(len(self.equipment)):
|
||||||
if not self.game.check_event(ce.Lazo) and self.equipment[i].name in self.expected_response:
|
if not self.game.check_event(ce.Lazo) 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)
|
self.respond(len(self.hand)+i)
|
||||||
did_respond = True
|
did_respond = True
|
||||||
break
|
break
|
||||||
if not did_respond:
|
if not did_respond:
|
||||||
|
self.game.rpc_log.append(f'{self.name};respond;{-1}')
|
||||||
self.respond(-1)
|
self.respond(-1)
|
||||||
elif self.pending_action == PendingAction.CHOOSE:
|
elif self.pending_action == PendingAction.CHOOSE:
|
||||||
if not self.target_p:
|
if not self.target_p:
|
||||||
self.choose(randrange(0, len(self.available_cards)))
|
card_index = randrange(0, len(self.available_cards))
|
||||||
|
self.game.rpc_log.append(f'{self.name};choose;{card_index}')
|
||||||
|
self.choose(card_index)
|
||||||
else:
|
else:
|
||||||
target = self.game.get_player_named(self.target_p)
|
target = self.game.get_player_named(self.target_p)
|
||||||
if len(target.hand)+len(target.equipment) == 0:
|
if len(target.hand)+len(target.equipment) == 0:
|
||||||
self.pending_action = PendingAction.PLAY
|
self.pending_action = PendingAction.PLAY
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
else:
|
else:
|
||||||
self.choose(randrange(0, len(target.hand)+len(target.equipment)))
|
try:
|
||||||
|
card_index = randrange(0, len(target.hand)+len(target.equipment))
|
||||||
|
self.game.rpc_log.append(f'{self.name};choose;{card_index}')
|
||||||
|
self.choose(card_index)
|
||||||
|
except:
|
||||||
|
self.game.rpc_log.append(f'{self.name};choose;{0}')
|
||||||
|
self.choose(0)
|
||||||
|
|
||||||
def play_turn(self, can_play_vendetta = True, again = False):
|
|
||||||
|
def play_turn(self, can_play_vendetta = True, again = False, can_play_again_don_bell=True):
|
||||||
if (self.lives == 0 or self.is_dead) and not self.is_ghost:
|
if (self.lives == 0 or self.is_dead) and not self.is_ghost:
|
||||||
return self.end_turn(forced=True)
|
return self.end_turn(forced=True)
|
||||||
self.scrapped_cards = 0
|
self.scrapped_cards = 0
|
||||||
|
self.setaccio_count = 0
|
||||||
self.can_play_ranch = True
|
self.can_play_ranch = True
|
||||||
self.is_playing_ranch = False
|
self.is_playing_ranch = False
|
||||||
self.can_play_vendetta = can_play_vendetta
|
self.can_play_vendetta = can_play_vendetta
|
||||||
|
self.can_play_again_don_bell = can_play_again_don_bell
|
||||||
if not again:
|
if not again:
|
||||||
self.sio.emit('chat_message', room=self.game.name,
|
self.sio.emit('chat_message', room=self.game.name,
|
||||||
data=f'_turn|{self.name}')
|
data=f'_turn|{self.name}')
|
||||||
@ -319,6 +379,12 @@ class Player:
|
|||||||
if self.game.check_event(ceh.MezzogiornoDiFuoco):
|
if self.game.check_event(ceh.MezzogiornoDiFuoco):
|
||||||
self.attacker = None
|
self.attacker = None
|
||||||
self.lives -= 1
|
self.lives -= 1
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if self.character.check(self.game, grch.SimeonPicos):
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw(True))
|
||||||
if self.character.check(self.game, chars.BartCassidy) and self.lives > 0:
|
if self.character.check(self.game, chars.BartCassidy) and self.lives > 0:
|
||||||
self.hand.append(self.game.deck.draw(True))
|
self.hand.append(self.game.deck.draw(True))
|
||||||
self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}')
|
self.sio.emit('chat_message', room=self.game.name, data=f'_special_bart_cassidy|{self.name}')
|
||||||
@ -332,6 +398,7 @@ class Player:
|
|||||||
'name': p.name,
|
'name': p.name,
|
||||||
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||||
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
'alt_text': ''.join(['❤️']*p.lives)+''.join(['💀']*(p.max_lives-p.lives)),
|
||||||
|
'is_character': True,
|
||||||
'noDesc': True
|
'noDesc': True
|
||||||
} for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]
|
} for p in self.game.get_alive_players() if p != self and p.lives < p.max_lives]
|
||||||
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||||
@ -360,6 +427,7 @@ class Player:
|
|||||||
'name': p['name'],
|
'name': p['name'],
|
||||||
'icon': p['role'].icon if(self.game.initial_players == 3) else '⭐️' if p['is_sheriff'] else '🤠',
|
'icon': p['role'].icon if(self.game.initial_players == 3) else '⭐️' if p['is_sheriff'] else '🤠',
|
||||||
'alt_text': ''.join(['❤️']*p['lives'])+''.join(['💀']*(p['max_lives']-p['lives'])),
|
'alt_text': ''.join(['❤️']*p['lives'])+''.join(['💀']*(p['max_lives']-p['lives'])),
|
||||||
|
'is_character': True,
|
||||||
'desc': p['name']
|
'desc': p['name']
|
||||||
} for p in self.game.get_visible_players(self) if p['dist'] <= self.get_sight()]
|
} for p in self.game.get_visible_players(self) if p['dist'] <= self.get_sight()]
|
||||||
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||||
@ -370,6 +438,7 @@ class Player:
|
|||||||
self.available_cards = [{
|
self.available_cards = [{
|
||||||
'name': p.name,
|
'name': p.name,
|
||||||
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||||
|
'is_character': True,
|
||||||
'noDesc': True
|
'noDesc': True
|
||||||
} for p in self.game.get_alive_players() if len(p.equipment) > 0 and p != self]
|
} for p in self.game.get_alive_players() if len(p.equipment) > 0 and p != self]
|
||||||
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||||
@ -389,6 +458,12 @@ class Player:
|
|||||||
self.choose_text = 'choose_card_to_get'
|
self.choose_text = 'choose_card_to_get'
|
||||||
self.pending_action = PendingAction.CHOOSE
|
self.pending_action = PendingAction.CHOOSE
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
elif self.character.check(self.game, grch.DutchWill):
|
||||||
|
self.is_drawing = True
|
||||||
|
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) and type(pile) == str and pile != self.name and pile in self.game.players_map and len(self.game.get_player_named(pile).equipment) > 0:
|
elif self.character.check(self.game, chd.PatBrennan) and type(pile) == str and pile != self.name and pile in self.game.players_map and len(self.game.get_player_named(pile).equipment) > 0:
|
||||||
self.is_drawing = True
|
self.is_drawing = True
|
||||||
self.available_cards = self.game.get_player_named(pile).equipment
|
self.available_cards = self.game.get_player_named(pile).equipment
|
||||||
@ -398,45 +473,34 @@ class Player:
|
|||||||
self.notify_self()
|
self.notify_self()
|
||||||
else:
|
else:
|
||||||
self.pending_action = PendingAction.PLAY
|
self.pending_action = PendingAction.PLAY
|
||||||
if pile == 'scrap' and self.character.check(self.game, chars.PedroRamirez):
|
num = 2 if not self.character.check(self.game, chd.BillNoface) else self.max_lives-self.lives+1
|
||||||
self.hand.append(self.game.deck.draw_from_scrap_pile())
|
if self.character.check(self.game, chd.PixiePete): num += 1
|
||||||
if not self.game.check_event(ceh.Sete):
|
if (len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0): num += 1
|
||||||
self.hand.append(self.game.deck.draw())
|
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.check_event(ceh.CittaFantasma)): num += 1
|
||||||
self.sio.emit('chat_message', room=self.game.name,
|
elif self.game.check_event(ceh.Sete): num -= 1
|
||||||
data=f'_draw_from_scrap|{self.name}')
|
for i in range(num):
|
||||||
elif type(pile) == str and pile != self.name and pile in self.game.players_map and self.character.check(self.game, chars.JesseJones) and len(self.game.get_player_named(pile).hand) > 0:
|
if i == 0 and pile == 'scrap' and self.character.check(self.game, chars.PedroRamirez):
|
||||||
self.hand.append(self.game.get_player_named(pile).hand.pop(
|
self.hand.append(self.game.deck.draw_from_scrap_pile())
|
||||||
randrange(0, len(self.game.get_player_named(pile).hand))))
|
self.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_scrap|{self.name}')
|
||||||
self.game.get_player_named(pile).notify_self()
|
elif i == 0 and type(pile) == str and pile != self.name and pile in self.game.players_map and self.character.check(self.game, chars.JesseJones) and len(self.game.get_player_named(pile).hand) > 0:
|
||||||
self.sio.emit('chat_message', room=self.game.name,
|
self.hand.append(self.game.get_player_named(pile).hand.pop( randrange(0, len(self.game.get_player_named(pile).hand))))
|
||||||
data=f'_draw_from_player|{self.name}|{pile}')
|
self.game.get_player_named(pile).notify_self()
|
||||||
if not self.game.check_event(ceh.Sete):
|
self.sio.emit('chat_message', room=self.game.name, data=f'_draw_from_player|{self.name}|{pile}')
|
||||||
self.hand.append(self.game.deck.draw())
|
elif i == 1:
|
||||||
elif self.character.check(self.game, chd.BillNoface):
|
|
||||||
self.hand.append(self.game.deck.draw())
|
|
||||||
if not self.game.check_event(ceh.Sete):
|
|
||||||
for i in range(self.max_lives-self.lives):
|
|
||||||
self.hand.append(self.game.deck.draw())
|
|
||||||
else:
|
|
||||||
if self.character.check(self.game, chd.PixiePete):
|
|
||||||
self.hand.append(self.game.deck.draw())
|
|
||||||
for i in range(2):
|
|
||||||
card: cs.Card = self.game.deck.draw()
|
card: cs.Card = self.game.deck.draw()
|
||||||
self.hand.append(card)
|
if (self.character.check(self.game, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest)):
|
||||||
if i == 1 and (self.character.check(self.game, chars.BlackJack) or self.game.check_event(ce.LeggeDelWest)):
|
|
||||||
for p in self.game.get_alive_players():
|
for p in self.game.get_alive_players():
|
||||||
if p != self:
|
if p != self:
|
||||||
p.notify_card(self, card, 'blackjack_special' if self.character.check(self.game, chars.BlackJack) else 'foc.leggedelwest')
|
p.notify_card(self, card, 'blackjack_special' if self.character.check(self.game, chars.BlackJack) else 'foc.leggedelwest')
|
||||||
if self.game.check_event(ce.LeggeDelWest):
|
if self.game.check_event(ce.LeggeDelWest):
|
||||||
card.must_be_used = True
|
card.must_be_used = True
|
||||||
if card.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]) and self.character.check(self.game, chars.BlackJack):
|
if self.character.check(self.game, chars.BlackJack) and card.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]):
|
||||||
self.hand.append(self.game.deck.draw())
|
self.hand.append(self.game.deck.draw())
|
||||||
if self.game.check_event(ceh.Sete):
|
self.hand.append(card)
|
||||||
return self.notify_self()
|
else:
|
||||||
if self.game.check_event(ceh.IlTreno) or (self.is_ghost and self.game.check_event(ceh.CittaFantasma)):
|
|
||||||
self.hand.append(self.game.deck.draw())
|
self.hand.append(self.game.deck.draw())
|
||||||
self.manette()
|
self.manette()
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
|
||||||
def manette(self):
|
def manette(self):
|
||||||
if self.game.check_event(ceh.Manette):
|
if self.game.check_event(ceh.Manette):
|
||||||
@ -453,6 +517,8 @@ class Player:
|
|||||||
if self.pending_action != PendingAction.PICK:
|
if self.pending_action != PendingAction.PICK:
|
||||||
return
|
return
|
||||||
pickable_cards = 1 + self.character.pick_mod
|
pickable_cards = 1 + self.character.pick_mod
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||||
|
pickable_cards += 1
|
||||||
if self.is_my_turn:
|
if self.is_my_turn:
|
||||||
for i in range(len(self.equipment)):
|
for i in range(len(self.equipment)):
|
||||||
if i < len(self.equipment) and isinstance(self.equipment[i], cs.Dinamite):
|
if i < len(self.equipment) and isinstance(self.equipment[i], cs.Dinamite):
|
||||||
@ -464,6 +530,14 @@ class Player:
|
|||||||
data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
||||||
if picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0:
|
if picked.check_suit(self.game, [cs.Suit.SPADES]) and 2 <= picked.number <= 9 and pickable_cards == 0:
|
||||||
self.lives -= 3
|
self.lives -= 3
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
self.gold_nuggets += 3
|
||||||
|
if self.character.check(self.game, grch.SimeonPicos):
|
||||||
|
self.gold_nuggets += 3
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
self.attacker = None
|
self.attacker = None
|
||||||
self.game.deck.scrap(self.equipment.pop(i), True)
|
self.game.deck.scrap(self.equipment.pop(i), True)
|
||||||
self.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}')
|
self.sio.emit('chat_message', room=self.game.name, data=f'_explode|{self.name}')
|
||||||
@ -524,10 +598,24 @@ class Player:
|
|||||||
return playable_cards
|
return playable_cards
|
||||||
|
|
||||||
def play_card(self, hand_index: int, against=None, _with=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)}')
|
||||||
|
print(self.name, 'wants to play card ', hand_index, ' against:', against, ' with:', _with)
|
||||||
if not self.is_my_turn or self.pending_action != PendingAction.PLAY or self.game.is_handling_death:
|
if not self.is_my_turn or self.pending_action != PendingAction.PLAY or self.game.is_handling_death:
|
||||||
|
print('but cannot')
|
||||||
return
|
return
|
||||||
if not (0 <= hand_index < len(self.hand) + len(self.equipment)):
|
if not (0 <= hand_index < len(self.hand) + len(self.equipment) + len(self.gold_rush_equipment)):
|
||||||
|
print('but the card index is out of range')
|
||||||
return
|
return
|
||||||
|
elif len(self.hand) + len(self.equipment) <= hand_index < len(self.hand) + len(self.equipment) + len(self.gold_rush_equipment) and len(self.gold_rush_equipment):
|
||||||
|
print('which is a gold rush black card')
|
||||||
|
card: grc.ShopCard = self.gold_rush_equipment[hand_index - len(self.hand) - len(self.equipment)]
|
||||||
|
return card.play_card(self)
|
||||||
card: cs.Card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand))
|
card: cs.Card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand))
|
||||||
withCard: cs.Card = None
|
withCard: cs.Card = None
|
||||||
if _with != None:
|
if _with != None:
|
||||||
@ -535,19 +623,20 @@ class Player:
|
|||||||
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
|
print(self.name, 'is playing ', card, ' against:', against, ' with:', _with)
|
||||||
did_play_card = False
|
did_play_card = False
|
||||||
event_blocks_card = (self.game.check_event(ce.IlGiudice) and (card.is_equipment or (card.usable_next_turn and not card.can_be_used_now))) or (self.game.check_event(ce.Lazo) and card.usable_next_turn and card.can_be_used_now) or (self.game.check_event(ceh.Manette) and card.suit != self.committed_suit_manette and not (card.usable_next_turn and card.can_be_used_now))
|
event_blocks_card = (self.game.check_event(ce.IlGiudice) and (card.is_equipment or (card.usable_next_turn and not card.can_be_used_now))) or (self.game.check_event(ce.Lazo) and card.usable_next_turn and card.can_be_used_now) or (self.game.check_event(ceh.Manette) and card.suit != self.committed_suit_manette and not (card.usable_next_turn and card.can_be_used_now))
|
||||||
if not(against != None and isinstance(self.game.get_player_named(against).character, chd.ApacheKid) and card.check_suit(self.game, [cs.Suit.DIAMONDS])) and not event_blocks_card:
|
if not(against != None and (self.game.get_player_named(against).character.check(self.game, chd.ApacheKid) or len([c for c in self.game.get_player_named(against).gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0) and card.check_suit(self.game, [cs.Suit.DIAMONDS])) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) and not event_blocks_card:
|
||||||
if against == self.name and not isinstance(card, csd.Tequila):
|
if against == self.name and not isinstance(card, csd.Tequila) and not isinstance(card, cs.Panico) and not isinstance(card, cs.CatBalou):
|
||||||
did_play_card = False
|
did_play_card = False
|
||||||
else:
|
else:
|
||||||
did_play_card = card.play_card(self, against, withCard)
|
did_play_card = card.play_card(self, against, withCard)
|
||||||
if not card.is_equipment and not card.usable_next_turn or event_blocks_card:
|
if not card.is_equipment and not card.usable_next_turn and not (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK) or event_blocks_card:
|
||||||
if did_play_card:
|
if did_play_card:
|
||||||
self.game.deck.scrap(card, True)
|
self.game.deck.scrap(card, True)
|
||||||
else:
|
else:
|
||||||
self.hand.insert(hand_index, card)
|
self.hand.insert(hand_index, card)
|
||||||
if withCard:
|
if withCard:
|
||||||
self.hand.insert(_with, withCard)
|
self.hand.insert(_with, withCard)
|
||||||
elif card.usable_next_turn and card.can_be_used_now:
|
self.sio.emit('cant_play_card', room=self.sid)
|
||||||
|
elif (card.usable_next_turn and card.can_be_used_now) or (isinstance(card, grc.ShopCard) and card.kind == grc.ShopCardKind.BLACK):
|
||||||
if did_play_card:
|
if did_play_card:
|
||||||
self.game.deck.scrap(card, True)
|
self.game.deck.scrap(card, True)
|
||||||
else:
|
else:
|
||||||
@ -568,7 +657,9 @@ class Player:
|
|||||||
if self.target_p and self.target_p != '': # panico, cat balou
|
if self.target_p and self.target_p != '': # panico, cat balou
|
||||||
target = self.game.get_player_named(self.target_p)
|
target = self.game.get_player_named(self.target_p)
|
||||||
card = None
|
card = None
|
||||||
if card_index >= len(target.hand):
|
if (target.name == self.name):
|
||||||
|
card = self.equipment.pop(card_index)
|
||||||
|
elif card_index >= len(target.hand):
|
||||||
card = target.equipment.pop(card_index - len(target.hand))
|
card = target.equipment.pop(card_index - len(target.hand))
|
||||||
else:
|
else:
|
||||||
card = target.hand.pop(card_index)
|
card = target.hand.pop(card_index)
|
||||||
@ -588,6 +679,74 @@ class Player:
|
|||||||
while self.target_p == self.name or len(self.game.players[self.game.players_map[self.target_p]].hand) + len(self.game.players[self.game.players_map[self.target_p]].equipment) == 0:
|
while self.target_p == self.name or len(self.game.players[self.game.players_map[self.target_p]].hand) + len(self.game.players[self.game.players_map[self.target_p]].equipment) == 0:
|
||||||
self.target_p = self.game.players[(self.game.players_map[self.target_p]+1)%len(self.game.players)].name
|
self.target_p = self.game.players[(self.game.players_map[self.target_p]+1)%len(self.game.players)].name
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_ricercato':
|
||||||
|
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||||
|
player.sio.emit('chat_message', room=player.game.name, data=f'_play_card_against|{self.name}|Ricercato|{player.name}')
|
||||||
|
if len([c for c in player.gold_rush_equipment if isinstance(c, grc.Ricercato)]) > 0:
|
||||||
|
self.game.deck.shop_deck.append(grc.Ricercato())
|
||||||
|
else:
|
||||||
|
player.gold_rush_equipment.append(grc.Ricercato())
|
||||||
|
player.notify_self()
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_sid_scrap':
|
||||||
|
self.scrapped_cards += 1
|
||||||
|
self.game.deck.scrap(self.hand.pop(card_index), True)
|
||||||
|
if self.scrapped_cards == 2:
|
||||||
|
self.available_cards = []
|
||||||
|
self.pending_action = self.previous_pending_action
|
||||||
|
if self.game.players[self.game.turn] != self:
|
||||||
|
self.game.players[self.game.turn].pending_action = PendingAction.PLAY
|
||||||
|
self.game.players[self.game.turn].notify_self()
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_bicchierino':
|
||||||
|
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_play_card_for|{self.name}|{"Bicchierino"}|{player.name}')
|
||||||
|
player.lives = min(player.lives+1, player.max_lives)
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_birra_function':
|
||||||
|
if card_index == 0:
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_get_nugget|{self.name}')
|
||||||
|
else:
|
||||||
|
cs.Birra(1,1).play_card(self, skipChecks=True)
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_bottiglia':
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}')
|
||||||
|
if isinstance(self.available_cards[card_index], cs.Birra):
|
||||||
|
self.lives = min(self.lives+1, self.max_lives)
|
||||||
|
else:
|
||||||
|
self.hand.append(self.available_cards[card_index])
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'choose_complice':
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_play_card|{self.name}|{"Bottiglia"}')
|
||||||
|
self.hand.append(self.available_cards[card_index])
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
|
elif self.choose_text == 'gold_rush_discard':
|
||||||
|
if card_index == len(self.available_cards) - 1:
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
else:
|
||||||
|
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||||
|
self.available_cards = [c for c in player.gold_rush_equipment if c.number+1 <= self.gold_nuggets]
|
||||||
|
self.available_cards.append({'icon': '❌', 'noDesc': True})
|
||||||
|
self.choose_text = 'gold_rush_discard_2|' + player.name
|
||||||
|
self.notify_self()
|
||||||
|
elif 'gold_rush_discard_2' in self.choose_text:
|
||||||
|
if card_index == len(self.available_cards) - 1:
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
else:
|
||||||
|
self.gold_nuggets -= self.available_cards[card_index].number + 1
|
||||||
|
player = self.game.get_player_named(self.choose_text.split('|')[1])
|
||||||
|
player.gold_rush_equipment.remove(self.available_cards[card_index])
|
||||||
|
self.game.deck.shop_deck.append(self.available_cards[card_index])
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_gold_rush_pay_discard|{self.name}|{player.name}|{self.available_cards[card_index].name}')
|
||||||
|
player.notify_self()
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.notify_self()
|
||||||
elif self.game.check_event(ceh.NuovaIdentita) and self.choose_text == 'choose_nuova_identita':
|
elif self.game.check_event(ceh.NuovaIdentita) and self.choose_text == 'choose_nuova_identita':
|
||||||
if card_index == 1: # the other character
|
if card_index == 1: # the other character
|
||||||
self.character = self.not_chosen_character
|
self.character = self.not_chosen_character
|
||||||
@ -606,6 +765,12 @@ class Player:
|
|||||||
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
player = self.game.get_player_named(self.available_cards[card_index]['name'])
|
||||||
player.lives += 1
|
player.lives += 1
|
||||||
self.lives -= 1
|
self.lives -= 1
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if self.character.check(self.game, grch.SimeonPicos):
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
player.notify_self()
|
player.notify_self()
|
||||||
self.sio.emit('chat_message', room=self.game.name, data=f'_fratelli_sangue|{self.name}|{player.name}')
|
self.sio.emit('chat_message', room=self.game.name, data=f'_fratelli_sangue|{self.name}|{player.name}')
|
||||||
except: pass
|
except: pass
|
||||||
@ -677,19 +842,47 @@ class Player:
|
|||||||
self.notify_self()
|
self.notify_self()
|
||||||
# specifico per personaggio
|
# specifico per personaggio
|
||||||
elif self.is_drawing and self.character.check(self.game, chars.KitCarlson):
|
elif self.is_drawing and self.character.check(self.game, chars.KitCarlson):
|
||||||
self.hand.append(self.available_cards.pop(card_index))
|
card: cs.Card = self.available_cards.pop(card_index)
|
||||||
|
if len(self.available_cards) == 1: #ho pescato la seconda carta
|
||||||
|
if self.game.check_event(ce.LeggeDelWest):
|
||||||
|
card.must_be_used = True
|
||||||
|
self.hand.append(card)
|
||||||
pickable_stop = 1
|
pickable_stop = 1
|
||||||
if self.game.check_event(ceh.Sete): pickable_stop = 2
|
if self.game.check_event(ceh.Sete): pickable_stop += 1
|
||||||
if self.game.check_event(ceh.IlTreno): pickable_stop = 0
|
if self.game.check_event(ceh.IlTreno) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||||
|
pickable_stop -= 1
|
||||||
if len(self.available_cards) == pickable_stop:
|
if len(self.available_cards) == pickable_stop:
|
||||||
if len(self.available_cards) > 0:
|
if len(self.available_cards) > 0: #la carta non scelta la rimettiamo in cima al mazzo
|
||||||
self.game.deck.put_on_top(self.available_cards.pop())
|
self.game.deck.put_on_top(self.available_cards.pop())
|
||||||
|
if len(self.available_cards) > 0: #se sono rimaste carte le scartiamo
|
||||||
|
self.game.deck.scrap(self.available_cards.pop())
|
||||||
|
#se c'è sia treno che piccone pesco un'altra carta
|
||||||
|
if self.game.check_event(ceh.IlTreno) and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
self.is_drawing = False
|
self.is_drawing = False
|
||||||
self.pending_action = PendingAction.PLAY
|
self.pending_action = PendingAction.PLAY
|
||||||
self.manette()
|
self.manette()
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
# specifico per personaggio
|
# specifico per personaggio
|
||||||
|
elif self.is_drawing and self.character.check(self.game, grch.DutchWill):
|
||||||
|
if not self.game.check_event(ceh.Sete):
|
||||||
|
self.hand.append(self.available_cards.pop(card_index)) #prendo la carta scelta
|
||||||
|
else:
|
||||||
|
self.game.deck.scrap(self.available_cards.pop(0), True) #non pesco carte
|
||||||
|
self.game.deck.scrap(self.available_cards.pop(0), True) #scarto l'altra
|
||||||
|
#legge del west non si applica perchè la seconda carta viene scartata
|
||||||
|
if self.game.check_event(ceh.IlTreno):
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Piccone)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw())
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
self.is_drawing = False
|
||||||
|
self.pending_action = PendingAction.PLAY
|
||||||
|
self.manette()
|
||||||
|
self.notify_self()
|
||||||
|
# specifico per personaggio
|
||||||
elif self.is_drawing and self.character.check(self.game, chd.PatBrennan):
|
elif self.is_drawing and self.character.check(self.game, chd.PatBrennan):
|
||||||
|
#non pesca per niente dal mazzo
|
||||||
self.is_drawing = False
|
self.is_drawing = False
|
||||||
card = self.available_cards.pop(card_index)
|
card = self.available_cards.pop(card_index)
|
||||||
card.reset_card()
|
card.reset_card()
|
||||||
@ -704,6 +897,8 @@ class Player:
|
|||||||
|
|
||||||
def barrel_pick(self):
|
def barrel_pick(self):
|
||||||
pickable_cards = 1 + self.character.pick_mod
|
pickable_cards = 1 + self.character.pick_mod
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||||
|
pickable_cards += 1
|
||||||
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
||||||
pickable_cards = 2
|
pickable_cards = 2
|
||||||
while pickable_cards > 0:
|
while pickable_cards > 0:
|
||||||
@ -734,6 +929,8 @@ class Player:
|
|||||||
pickable_cards = 1 + self.character.pick_mod
|
pickable_cards = 1 + self.character.pick_mod
|
||||||
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
if len([c for c in self.equipment if isinstance(c, cs.Barile)]) > 0 and self.character.check(self.game, chars.Jourdonnais):
|
||||||
pickable_cards = 2
|
pickable_cards = 2
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.FerroDiCavallo)]) > 0:
|
||||||
|
pickable_cards += 1
|
||||||
while pickable_cards > 0:
|
while pickable_cards > 0:
|
||||||
pickable_cards -= 1
|
pickable_cards -= 1
|
||||||
picked: cs.Card = self.game.deck.pick_and_scrap()
|
picked: cs.Card = self.game.deck.pick_and_scrap()
|
||||||
@ -812,7 +1009,7 @@ class Player:
|
|||||||
|
|
||||||
def get_indians(self, attacker):
|
def get_indians(self, attacker):
|
||||||
self.attacker = attacker
|
self.attacker = attacker
|
||||||
if self.character.check(self.game, chd.ApacheKid): return False
|
if self.character.check(self.game, chd.ApacheKid) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0: return False
|
||||||
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0:
|
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0:
|
||||||
print('Cant defend')
|
print('Cant defend')
|
||||||
self.take_damage_response()
|
self.take_damage_response()
|
||||||
@ -850,6 +1047,7 @@ class Player:
|
|||||||
if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn:
|
if self.character.check(self.game, chd.MollyStark) and not self.is_my_turn:
|
||||||
self.hand.append(self.game.deck.draw(True))
|
self.hand.append(self.game.deck.draw(True))
|
||||||
self.lives += 1 if not self.character.check(self.game, chd.TequilaJoe) else 2
|
self.lives += 1 if not self.character.check(self.game, chd.TequilaJoe) else 2
|
||||||
|
self.lives = min(self.lives, self.max_lives)
|
||||||
self.game.deck.scrap(self.hand.pop(i), True)
|
self.game.deck.scrap(self.hand.pop(i), True)
|
||||||
self.sio.emit('chat_message', room=self.game.name,
|
self.sio.emit('chat_message', room=self.game.name,
|
||||||
data=f'_beer_save|{self.name}')
|
data=f'_beer_save|{self.name}')
|
||||||
@ -868,6 +1066,16 @@ class Player:
|
|||||||
self.sio.emit('chat_message', room=self.game.name,
|
self.sio.emit('chat_message', room=self.game.name,
|
||||||
data=f'_special_el_gringo|{self.name}|{self.attacker.name}')
|
data=f'_special_el_gringo|{self.name}|{self.attacker.name}')
|
||||||
self.attacker.notify_self()
|
self.attacker.notify_self()
|
||||||
|
if self.attacker and 'gold_rush' in self.game.expansions:
|
||||||
|
if (isinstance(self.attacker, Player)):
|
||||||
|
self.attacker.gold_nuggets += 1
|
||||||
|
self.attacker.notify_self()
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Talismano)]) > 0:
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if self.character.check(self.game, grch.SimeonPicos):
|
||||||
|
self.gold_nuggets += 1
|
||||||
|
if len([c for c in self.gold_rush_equipment if isinstance(c, grc.Stivali)]) > 0:
|
||||||
|
self.hand.append(self.game.deck.draw(True))
|
||||||
self.heal_if_needed()
|
self.heal_if_needed()
|
||||||
self.mancato_needed = 0
|
self.mancato_needed = 0
|
||||||
self.expected_response = []
|
self.expected_response = []
|
||||||
@ -894,11 +1102,12 @@ class Player:
|
|||||||
card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand))
|
card = self.hand.pop(hand_index) if hand_index < len(self.hand) else self.equipment.pop(hand_index-len(self.hand))
|
||||||
#hand_index < len(self.hand) with the '<=' due to the hand.pop
|
#hand_index < len(self.hand) with the '<=' due to the hand.pop
|
||||||
if self.character.check(self.game, chd.MollyStark) and hand_index <= len(self.hand) and not self.is_my_turn and self.event_type != 'duel':
|
if self.character.check(self.game, chd.MollyStark) and hand_index <= len(self.hand) and not self.is_my_turn and self.event_type != 'duel':
|
||||||
if self.attacker.character.check(self.game, chars.SlabTheKiller) and isinstance(self.hand[hand_index], cs.Mancato):
|
if hasattr(self.attacker,'character') and self.attacker.character.check(self.game, chars.SlabTheKiller) and isinstance(self.hand[hand_index], cs.Mancato):
|
||||||
self.molly_discarded_cards += 1
|
self.molly_discarded_cards += 1
|
||||||
else:
|
else:
|
||||||
self.hand.append(self.game.deck.draw(True))
|
self.hand.append(self.game.deck.draw(True))
|
||||||
card.use_card(self)
|
card.use_card(self)
|
||||||
|
print(f'{self.game.name}: {self.name} responded with {card.name}')
|
||||||
self.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card.name}')
|
self.sio.emit('chat_message', room=self.game.name, data=f'_respond|{self.name}|{card.name}')
|
||||||
self.game.deck.scrap(card, True)
|
self.game.deck.scrap(card, True)
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
@ -936,9 +1145,11 @@ class Player:
|
|||||||
if self.mancato_needed <= 0:
|
if self.mancato_needed <= 0:
|
||||||
self.attacker = None
|
self.attacker = None
|
||||||
|
|
||||||
def get_sight(self, countWeapon=True):
|
def get_sight(self, countWeapon=True): #come vedo io gli altri
|
||||||
if not self.character:
|
if not self.character:
|
||||||
return 0
|
return 0
|
||||||
|
if self.game.check_event(ce.Lazo):
|
||||||
|
return 1 + self.character.sight_mod
|
||||||
aim = 0
|
aim = 0
|
||||||
range = 0
|
range = 0
|
||||||
for card in self.equipment:
|
for card in self.equipment:
|
||||||
@ -946,19 +1157,18 @@ class Player:
|
|||||||
range += card.range
|
range += card.range
|
||||||
else:
|
else:
|
||||||
aim += card.sight_mod
|
aim += card.sight_mod
|
||||||
if self.game.check_event(ce.Lazo):
|
return max(1,range) + aim + (self.character.sight_mod if not self.game.check_event(ceh.Sbornia) else 0)
|
||||||
return 1 + self.character.sight_mod
|
|
||||||
return max(1, range) + aim + self.character.sight_mod
|
|
||||||
|
|
||||||
def get_visibility(self):
|
def get_visibility(self): #come mi vedono gli altri
|
||||||
if not self.character or not self.game or not self.game.players[self.game.turn].character:
|
if not self.character or not self.game or not self.game.players[self.game.turn].character:
|
||||||
return 0
|
return 0
|
||||||
covers = 0
|
covers = 0
|
||||||
|
ch_vis_mod = self.character.visibility_mod if not self.game.check_event(ceh.Sbornia) else 0
|
||||||
if self.game.check_event(ce.Lazo) or self.game.players[self.game.turn].character.check(self.game, chd.BelleStar):
|
if self.game.check_event(ce.Lazo) or self.game.players[self.game.turn].character.check(self.game, chd.BelleStar):
|
||||||
return self.character.visibility_mod
|
return ch_vis_mod
|
||||||
for card in self.equipment:
|
for card in self.equipment:
|
||||||
covers += card.vis_mod
|
covers += card.vis_mod
|
||||||
return self.character.visibility_mod + covers
|
return ch_vis_mod + covers
|
||||||
|
|
||||||
def scrap(self, card_index):
|
def scrap(self, card_index):
|
||||||
if self.is_my_turn or self.character.check(self.game, chars.SidKetchum):
|
if self.is_my_turn or self.character.check(self.game, chars.SidKetchum):
|
||||||
@ -974,51 +1184,105 @@ class Player:
|
|||||||
self.game.deck.scrap(card)
|
self.game.deck.scrap(card)
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
|
||||||
def holyday_special(self, data):
|
def special(self, data):
|
||||||
if self.character.check(self.game, chd.DocHolyday) and self.special_use_count < 1 and self.pending_action == PendingAction.PLAY:
|
self.character.special(self, data)
|
||||||
self.special_use_count += 1
|
|
||||||
cards = sorted(data['cards'], reverse=True)
|
|
||||||
for c in cards:
|
|
||||||
self.game.deck.scrap(self.hand.pop(c), True)
|
|
||||||
self.notify_self()
|
|
||||||
self.game.attack(self, data['against'])
|
|
||||||
|
|
||||||
def chuck_lose_hp_draw(self):
|
def gold_rush_discard(self):
|
||||||
if self.character.check(self.game, chd.ChuckWengam) and self.lives > 1 and self.is_my_turn:
|
self.available_cards = [{
|
||||||
self.lives -= 1
|
'name': p.name,
|
||||||
self.hand.append(self.game.deck.draw(True))
|
'icon': p.role.icon if(self.game.initial_players == 3) else '⭐️' if isinstance(p.role, r.Sheriff) else '🤠',
|
||||||
self.hand.append(self.game.deck.draw(True))
|
'is_character': True,
|
||||||
|
'alt_text': ''.join(['🎴️'] * len(p.gold_rush_equipment)),
|
||||||
|
'noDesc': True
|
||||||
|
} for p in self.game.get_alive_players() if p != self and len([e for e in p.gold_rush_equipment if e.number + 1 <= self.gold_nuggets]) > 0]
|
||||||
|
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):
|
||||||
|
print(f'{self.name} wants to buy gr-card index {index} in room {self.game.name}')
|
||||||
|
card: cs.Card = self.game.deck.shop_cards[index]
|
||||||
|
discount = 0
|
||||||
|
if self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1:
|
||||||
|
discount = 1
|
||||||
|
if self.pending_action == PendingAction.PLAY and self.gold_nuggets >= card.number - discount:
|
||||||
|
self.gold_nuggets -= card.number - discount
|
||||||
|
if self.character.check(self.game, grch.PrettyLuzena) and self.special_use_count < 1:
|
||||||
|
self.special_use_count += 1
|
||||||
|
if card.play_card(self):
|
||||||
|
self.game.deck.shop_deck.append(card)
|
||||||
|
self.game.deck.shop_cards[index] = None
|
||||||
|
self.game.deck.fill_gold_rush_shop()
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
|
||||||
|
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:
|
||||||
|
card = must_be_used_cards[0]
|
||||||
|
print(f'Legge del west card: {card.name}')
|
||||||
|
print(self.has_played_bang and not (any([isinstance(c, cs.Volcanic) for c in self.equipment]) and type(card) == type(cs.Bang)))
|
||||||
|
if card.suit == cs.Suit.DIAMONDS and card.need_target and len([p for p in self.game.get_alive_players() if (not p.character.check(self.game, chd.ApacheKid) and not any([isinstance(c, grc.Calumet) for c in p.gold_rush_equipment]))]) == 0:
|
||||||
|
if isinstance(card, cs.Bang):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return len(self.equipment) == 0 # se non ho carte equipaggiamento
|
||||||
|
elif (isinstance(card, cs.Bang) or (isinstance(card, cs.Mancato) and self.character.check(self.game, chars.CalamityJanet))) and self.has_played_bang and not any([isinstance(c, cs.Volcanic) for c in self.equipment]) or len([p for p in self.game.get_visible_players(self) if self.get_sight() >= p['dist']]) == 0:
|
||||||
|
return True
|
||||||
|
elif isinstance(card, cs.Mancato) or (card.need_with and len(self.hand) < 2):
|
||||||
|
return True
|
||||||
|
elif isinstance(card, cs.Panico) and len([p for p in self.game.get_visible_players(self) if self.get_sight(False) >= p['dist']]) == 0 and len(self.equipment) == 0:
|
||||||
|
return True
|
||||||
|
elif isinstance(card, csd.Pugno) and len([p for p in self.game.get_visible_players(self) if self.get_sight(False) >= p['dist']]) == 0:
|
||||||
|
return True
|
||||||
|
elif isinstance(card, cs.Prigione) and len([p for p in self.game.get_visible_players(self) if not p['is_sheriff']]) == 0:
|
||||||
|
return True
|
||||||
|
elif not card.is_weapon and len([c for c in self.equipment if c.name == card.name]) > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def end_turn(self, forced=False):
|
def end_turn(self, forced=False):
|
||||||
print(f"{self.name} wants to end his turn")
|
print(f"{self.name} wants to end his turn")
|
||||||
if not self.is_my_turn and not forced:
|
if not self.is_my_turn and not forced:
|
||||||
return
|
return
|
||||||
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
maxcards = self.lives if not self.character.check(self.game, chd.SeanMallory) else 10
|
||||||
|
if maxcards == self.lives and len([c for c in self.gold_rush_equipment if isinstance(c, grc.Cinturone)]) > 0:
|
||||||
|
maxcards = 8
|
||||||
if len(self.hand) > maxcards and not forced:
|
if len(self.hand) > maxcards and not forced:
|
||||||
print(
|
print(f"{self.name}: I have to many cards in my hand and I can't end the turn")
|
||||||
f"{self.name}: I have to many cards in my hand and I can't end the turn")
|
elif not self.check_can_end_turn():
|
||||||
|
print(f"{self.name}: I must play the legge del west card")
|
||||||
elif self.pending_action == PendingAction.PLAY or forced:
|
elif self.pending_action == PendingAction.PLAY or forced:
|
||||||
if not forced and self.game.check_event(ce.Vendetta) and self.can_play_vendetta:
|
|
||||||
picked: cs.Card = self.game.deck.pick_and_scrap()
|
|
||||||
self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
|
||||||
if picked.check_suit(self.game, [cs.Suit.HEARTS]):
|
|
||||||
self.play_turn(can_play_vendetta=False)
|
|
||||||
return
|
|
||||||
self.is_my_turn = False
|
|
||||||
self.has_played_bang = False
|
|
||||||
for i in range(len(self.equipment)):
|
for i in range(len(self.equipment)):
|
||||||
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now:
|
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now:
|
||||||
self.equipment[i].can_be_used_now = True
|
self.equipment[i].can_be_used_now = True
|
||||||
for i in range(len(self.hand)):
|
for i in range(len(self.hand)):
|
||||||
if self.hand[i].must_be_used:
|
if self.hand[i].must_be_used:
|
||||||
self.hand[i].must_be_used = False
|
self.hand[i].must_be_used = False
|
||||||
|
self.has_played_bang = False
|
||||||
|
##Vendetta##
|
||||||
|
if not forced and self.game.check_event(ce.Vendetta) and self.can_play_vendetta:
|
||||||
|
picked: cs.Card = self.game.deck.pick_and_scrap()
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
||||||
|
if picked.check_suit(self.game, [cs.Suit.HEARTS]):
|
||||||
|
self.play_turn(can_play_vendetta=False)
|
||||||
|
return
|
||||||
|
##Don Bell##
|
||||||
|
if not forced and self.character.check(self.game, grch.DonBell) and self.can_play_again_don_bell:
|
||||||
|
picked: cs.Card = self.game.deck.pick_and_scrap()
|
||||||
|
self.sio.emit('chat_message', room=self.game.name, data=f'_flipped|{self.name}|{picked.name}|{picked.num_suit()}')
|
||||||
|
if picked.check_suit(self.game, [cs.Suit.HEARTS, cs.Suit.DIAMONDS]):
|
||||||
|
self.play_turn(can_play_vendetta=False, can_play_again_don_bell=False)
|
||||||
|
return
|
||||||
|
##Ghost##
|
||||||
if self.is_dead and self.is_ghost and self.game.check_event(ceh.CittaFantasma):
|
if self.is_dead and self.is_ghost and self.game.check_event(ceh.CittaFantasma):
|
||||||
self.is_ghost = False
|
self.is_ghost = False
|
||||||
for i in range(len(self.hand)):
|
for i in range(len(self.hand)):
|
||||||
self.game.deck.scrap(self.hand.pop(), True)
|
self.game.deck.scrap(self.hand.pop(), True)
|
||||||
for i in range(len(self.equipment)):
|
for i in range(len(self.equipment)):
|
||||||
self.game.deck.scrap(self.equipment.pop(), True)
|
self.game.deck.scrap(self.equipment.pop(), True)
|
||||||
|
self.is_my_turn = False
|
||||||
self.committed_suit_manette = None
|
self.committed_suit_manette = None
|
||||||
self.pending_action = PendingAction.WAIT
|
self.pending_action = PendingAction.WAIT
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
@ -62,7 +62,9 @@ class Outlaw(Role):
|
|||||||
return True
|
return True
|
||||||
elif initial_players == 3 and attacker_role != None:
|
elif initial_players == 3 and attacker_role != None:
|
||||||
return isinstance(dead_role, Vice) and isinstance(attacker_role, Outlaw)
|
return isinstance(dead_role, Vice) and isinstance(attacker_role, Outlaw)
|
||||||
elif initial_players != 3 and not any([isinstance(p.role, Sheriff) for p in alive_players]):
|
elif (initial_players != 3 and (not any([isinstance(p.role, Sheriff) for p in alive_players]))
|
||||||
|
and (any([isinstance(p.role, Outlaw) for p in alive_players])
|
||||||
|
or any([isinstance(p.role, Renegade) for p in alive_players]) and len(alive_players) > 1)):
|
||||||
print("The Outlaw won!")
|
print("The Outlaw won!")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -82,7 +84,7 @@ class Renegade(Role):
|
|||||||
return True
|
return True
|
||||||
elif initial_players == 3 and attacker_role != None:
|
elif initial_players == 3 and attacker_role != None:
|
||||||
return isinstance(dead_role, Outlaw) and isinstance(attacker_role, Renegade)
|
return isinstance(dead_role, Outlaw) and isinstance(attacker_role, Renegade)
|
||||||
elif initial_players != 3 and len(alive_players) == 1 and alive_players[0].role == self:
|
elif initial_players != 3 and len(alive_players) == 1 and isinstance(alive_players[0].role, Renegade):
|
||||||
print("The Renegade won!")
|
print("The Renegade won!")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
certifi==2021.5.30
|
certifi==2021.10.8
|
||||||
dnspython==1.16.0
|
dnspython==2.2.1
|
||||||
eventlet==0.31.0
|
eventlet==0.33.0
|
||||||
greenlet==1.1.0
|
greenlet==1.1.2
|
||||||
python-engineio==3.14.2
|
python-engineio==3.14.2
|
||||||
python-socketio==4.6.1
|
python-socketio==4.6.1
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
|
pytest==7.0.1
|
||||||
|
requests==2.27.1
|
||||||
|
discord-webhook==0.15.0
|
584
backend/server.py
Normal file
584
backend/server.py
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
from typing import List
|
||||||
|
import eventlet
|
||||||
|
import socketio
|
||||||
|
|
||||||
|
from bang.game import Game
|
||||||
|
from bang.players import Player, PendingAction
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from discord_webhook import DiscordWebhook
|
||||||
|
|
||||||
|
import sys
|
||||||
|
sys.setrecursionlimit(10**6) # this should prevents bots from stopping
|
||||||
|
|
||||||
|
sio = socketio.Server(cors_allowed_origins="*")
|
||||||
|
|
||||||
|
static_files={
|
||||||
|
'/': {'content_type': 'text/html', 'filename': 'index.html'},
|
||||||
|
'/game': {'content_type': 'text/html', 'filename': 'index.html'},
|
||||||
|
'/help': {'content_type': 'text/html', 'filename': 'index.html'},
|
||||||
|
'/status': {'content_type': 'text/html', 'filename': 'index.html'},
|
||||||
|
# '/robots.txt': {'content_type': 'text/html', 'filename': 'robots.txt'},
|
||||||
|
'/favicon.ico': {'filename': 'favicon.ico'},
|
||||||
|
'/img/icons': './img/icons',
|
||||||
|
'/manifest.json': {'filename': 'manifest.json'},
|
||||||
|
'/css': './css',
|
||||||
|
'/media': './media',
|
||||||
|
'/js': './js',
|
||||||
|
}
|
||||||
|
if "UseRobots" in os.environ and os.environ['UseRobots'].upper() == "TRUE":
|
||||||
|
static_files['/robots.txt'] = {'content_type': 'text/html', 'filename': 'robots.txt'}
|
||||||
|
|
||||||
|
for file in [f for f in os.listdir('.') if '.js' in f or '.map' in f or '.html' in f]:
|
||||||
|
static_files[f'/{file}'] = f'./{file}'
|
||||||
|
|
||||||
|
app = socketio.WSGIApp(sio, static_files=static_files)
|
||||||
|
games: List[Game] = []
|
||||||
|
online_players = 0
|
||||||
|
blacklist: List[str] = []
|
||||||
|
|
||||||
|
def advertise_lobbies():
|
||||||
|
sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if not g.started and len(g.players) < 10 and not g.is_hidden])
|
||||||
|
sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in games if g.started])
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def connect(sid, environ):
|
||||||
|
global online_players
|
||||||
|
online_players += 1
|
||||||
|
print('connect ', sid)
|
||||||
|
sio.enter_room(sid, 'lobby')
|
||||||
|
sio.emit('players', room='lobby', data=online_players)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_online_players(sid):
|
||||||
|
global online_players
|
||||||
|
sio.emit('players', room='lobby', data=online_players)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def set_username(sid, username):
|
||||||
|
ses = sio.get_session(sid)
|
||||||
|
if not isinstance(ses, Player):
|
||||||
|
sio.save_session(sid, Player(username, sid, sio))
|
||||||
|
print(f'{sid} is now {username}')
|
||||||
|
advertise_lobbies()
|
||||||
|
elif ses.game == None or not ses.game.started:
|
||||||
|
print(f'{sid} changed username to {username}')
|
||||||
|
prev = ses.name
|
||||||
|
if len([p for p in ses.game.players if p.name == username]) > 0:
|
||||||
|
ses.name = f"{username}_{random.randint(0,100)}"
|
||||||
|
else:
|
||||||
|
ses.name = username
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data=f'_change_username|{prev}|{ses.name}')
|
||||||
|
sio.emit('me', data=ses.name, room=sid)
|
||||||
|
ses.game.notify_room()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_me(sid, room):
|
||||||
|
if isinstance(sio.get_session(sid), Player):
|
||||||
|
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||||
|
if sio.get_session(sid).game:
|
||||||
|
sio.get_session(sid).game.notify_room()
|
||||||
|
else:
|
||||||
|
sio.save_session(sid, Player('player', sid, sio))
|
||||||
|
de_games = [g for g in games if g.name == room['name']]
|
||||||
|
if len(de_games) == 1 and not de_games[0].started:
|
||||||
|
join_room(sid, room)
|
||||||
|
elif len(de_games) == 1 and de_games[0].started:
|
||||||
|
print('room exists')
|
||||||
|
if room['username'] != None and any([p.name == room['username'] for p in de_games[0].players if p.is_bot]):
|
||||||
|
print('getting inside the bot')
|
||||||
|
bot = [p for p in de_games[0].players if p.is_bot and p.name == room['username'] ][0]
|
||||||
|
bot.sid = sid
|
||||||
|
bot.is_bot = False
|
||||||
|
sio.enter_room(sid, de_games[0].name)
|
||||||
|
sio.save_session(sid, bot)
|
||||||
|
de_games[0].notify_room(sid)
|
||||||
|
eventlet.sleep(0.1)
|
||||||
|
de_games[0].notify_all()
|
||||||
|
sio.emit('role', room=sid, data=json.dumps(bot.role, default=lambda o: o.__dict__))
|
||||||
|
bot.notify_self()
|
||||||
|
if len(bot.available_characters) > 0:
|
||||||
|
bot.set_available_character(bot.available_characters)
|
||||||
|
else: #spectate
|
||||||
|
de_games[0].spectators.append(sio.get_session(sid))
|
||||||
|
sio.get_session(sid).game = de_games[0]
|
||||||
|
sio.enter_room(sid, de_games[0].name)
|
||||||
|
de_games[0].notify_room(sid)
|
||||||
|
de_games[0].notify_event_card(sid)
|
||||||
|
de_games[0].notify_scrap_pile(sid)
|
||||||
|
de_games[0].notify_all()
|
||||||
|
de_games[0].notify_gold_rush_shop()
|
||||||
|
de_games[0].notify_event_card()
|
||||||
|
else:
|
||||||
|
create_room(sid, room['name'])
|
||||||
|
if sio.get_session(sid).game == None:
|
||||||
|
sio.emit('me', data={'error':'Wrong password/Cannot connect'}, room=sid)
|
||||||
|
else:
|
||||||
|
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||||
|
if room['username'] == None or any([p.name == room['username'] for p in sio.get_session(sid).game.players]):
|
||||||
|
sio.emit('change_username', room=sid)
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sio.get_session(sid).game.name, data=f"_change_username|{sio.get_session(sid).name}|{room['username']}")
|
||||||
|
sio.get_session(sid).name = room['username']
|
||||||
|
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||||
|
if not sio.get_session(sid).game.started:
|
||||||
|
sio.get_session(sid).game.notify_room()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def disconnect(sid):
|
||||||
|
global online_players
|
||||||
|
online_players -= 1
|
||||||
|
if sio.get_session(sid):
|
||||||
|
sio.emit('players', room='lobby', data=online_players)
|
||||||
|
if sio.get_session(sid).game and sio.get_session(sid).disconnect():
|
||||||
|
sio.close_room(sio.get_session(sid).game.name)
|
||||||
|
games.pop(games.index(sio.get_session(sid).game))
|
||||||
|
print('disconnect ', sid)
|
||||||
|
advertise_lobbies()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def create_room(sid, room_name):
|
||||||
|
if sio.get_session(sid).game == None:
|
||||||
|
while len([g for g in games if g.name == room_name]):
|
||||||
|
room_name += f'_{random.randint(0,100)}'
|
||||||
|
sio.leave_room(sid, 'lobby')
|
||||||
|
sio.enter_room(sid, room_name)
|
||||||
|
g = Game(room_name, sio)
|
||||||
|
g.add_player(sio.get_session(sid))
|
||||||
|
if room_name in blacklist:
|
||||||
|
g.is_hidden = True
|
||||||
|
games.append(g)
|
||||||
|
print(f'{sid} created a room named {room_name}')
|
||||||
|
advertise_lobbies()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def private(sid):
|
||||||
|
g = sio.get_session(sid).game
|
||||||
|
g.set_private()
|
||||||
|
advertise_lobbies()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def toggle_expansion(sid, expansion_name):
|
||||||
|
g = sio.get_session(sid).game
|
||||||
|
g.toggle_expansion(expansion_name)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def toggle_comp(sid):
|
||||||
|
sio.get_session(sid).game.toggle_competitive()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def toggle_replace_with_bot(sid):
|
||||||
|
sio.get_session(sid).game.toggle_disconnect_bot()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def join_room(sid, room):
|
||||||
|
room_name = room['name']
|
||||||
|
i = [g.name for g in games].index(room_name)
|
||||||
|
if games[i].password != '' and games[i].password != room['password'].upper():
|
||||||
|
return
|
||||||
|
if not games[i].started:
|
||||||
|
print(f'{sid} joined a room named {room_name}')
|
||||||
|
sio.leave_room(sid, 'lobby')
|
||||||
|
sio.enter_room(sid, room_name)
|
||||||
|
while len([p for p in games[i].players if p.name == sio.get_session(sid).name]):
|
||||||
|
sio.get_session(sid).name += f'_{random.randint(0,100)}'
|
||||||
|
sio.emit('me', data=sio.get_session(sid).name, room=sid)
|
||||||
|
games[i].add_player(sio.get_session(sid))
|
||||||
|
advertise_lobbies()
|
||||||
|
else:
|
||||||
|
games[i].spectators.append(sio.get_session(sid))
|
||||||
|
sio.get_session(sid).game = games[i]
|
||||||
|
sio.get_session(sid).pending_action = PendingAction.WAIT
|
||||||
|
sio.enter_room(sid, games[0].name)
|
||||||
|
games[i].notify_room(sid)
|
||||||
|
eventlet.sleep(0.5)
|
||||||
|
games[i].notify_room(sid)
|
||||||
|
games[i].notify_all()
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sockets for the status page
|
||||||
|
"""
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_all_rooms(sid, deploy_key):
|
||||||
|
if 'DEPLOY_KEY' in os.environ and deploy_key == os.environ['DEPLOY_KEY']:
|
||||||
|
sio.emit('all_rooms', room=sid, data=[{
|
||||||
|
'name': g.name,
|
||||||
|
'hidden': g.is_hidden,
|
||||||
|
'players': [{'name':p.name, 'bot': p.is_bot, 'health': p.lives, 'sid': p.sid} for p in g.players],
|
||||||
|
'password': g.password,
|
||||||
|
'expansions': g.expansions,
|
||||||
|
'started': g.started,
|
||||||
|
'current_turn': g.turn,
|
||||||
|
'incremental_turn': g.incremental_turn,
|
||||||
|
'debug': g.debug,
|
||||||
|
'spectators': len(g.spectators)
|
||||||
|
} for g in games])
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def kick(sid, data):
|
||||||
|
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
||||||
|
sio.emit('kicked', room=data['sid'])
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def hide_toogle(sid, data):
|
||||||
|
if 'DEPLOY_KEY' in os.environ and data['key'] == os.environ['DEPLOY_KEY']:
|
||||||
|
game = [g for g in games if g.name==data['room']]
|
||||||
|
if len(games) > 0:
|
||||||
|
game[0].is_hidden = not game[0].is_hidden
|
||||||
|
if game[0].is_hidden:
|
||||||
|
if not data['room'] in blacklist:
|
||||||
|
blacklist.append(data['room'])
|
||||||
|
elif data['room'] in blacklist:
|
||||||
|
blacklist.remove(data['room'])
|
||||||
|
advertise_lobbies()
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sockets for the game
|
||||||
|
"""
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def start_game(sid):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.start_game()
|
||||||
|
advertise_lobbies()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def set_character(sid, name):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};set_character;{name}')
|
||||||
|
ses.set_character(name)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def refresh(sid):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.notify_self()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def draw(sid, pile):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};draw;{pile}')
|
||||||
|
ses.draw(pile)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def pick(sid):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};pick')
|
||||||
|
ses.pick()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def end_turn(sid):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};end_turn')
|
||||||
|
ses.end_turn()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def play_card(sid, data):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};play_card;{json.dumps(data)}')
|
||||||
|
ses.play_card(data['index'], data['against'], data['with'])
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def respond(sid, card_index):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};respond;{card_index}')
|
||||||
|
ses.respond(card_index)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def choose(sid, card_index):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};choose;{card_index}')
|
||||||
|
ses.choose(card_index)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def scrap(sid, card_index):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};scrap;{card_index}')
|
||||||
|
ses.scrap(card_index)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def special(sid, data):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};play_card;{json.dumps(data)}')
|
||||||
|
ses.special(data)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def gold_rush_discard(sid):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};gold_rush_discard;')
|
||||||
|
ses.gold_rush_discard()
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def buy_gold_rush_card(sid, data:int):
|
||||||
|
ses: Player = sio.get_session(sid)
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};buy_gold_rush_card;{data}')
|
||||||
|
ses.buy_gold_rush_card(data)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def chat_message(sid, msg, pl=None):
|
||||||
|
ses: Player = sio.get_session(sid) if pl is None else pl
|
||||||
|
ses.game.rpc_log.append(f'{ses.name};chat_message;{msg}')
|
||||||
|
if len(msg) > 0:
|
||||||
|
if msg[0] == '/':
|
||||||
|
commands = msg.split(';')
|
||||||
|
for msg in commands:
|
||||||
|
if '/addbot' in msg and not ses.game.started:
|
||||||
|
if len(msg.split()) > 1:
|
||||||
|
# for _ in range(int(msg.split()[1])):
|
||||||
|
# ses.game.add_player(Player(f'AI_{random.randint(0,1000)}', 'bot', sio, bot=True))
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'Only 1 bot at the time'})
|
||||||
|
else:
|
||||||
|
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
||||||
|
while any([p for p in ses.game.players if p.name == bot.name]):
|
||||||
|
bot = Player(f'AI_{random.randint(0,10)}', 'bot', sio, bot=True)
|
||||||
|
ses.game.add_player(bot)
|
||||||
|
bot.bot_spin()
|
||||||
|
return
|
||||||
|
if '/report' in msg and not ses.game.is_replay:
|
||||||
|
data = "\n".join(ses.game.rpc_log[:-1]).strip()
|
||||||
|
response = requests.post("https://www.toptal.com/developers/hastebin/documents", data)
|
||||||
|
key = json.loads(response.text).get('key')
|
||||||
|
if "DISCORD_WEBHOOK" in os.environ and len(os.environ['DISCORD_WEBHOOK']) > 0:
|
||||||
|
webhook = DiscordWebhook(url=os.environ['DISCORD_WEBHOOK'], content=f'New bug report, replay at https://www.toptal.com/developers/hastebin/{key}')
|
||||||
|
response = webhook.execute()
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'green','text':f'Report OK'})
|
||||||
|
else:
|
||||||
|
print("WARNING: DISCORD_WEBHOOK not found")
|
||||||
|
print(f'New bug report, replay at https://www.toptal.com/developers/hastebin/{key}')
|
||||||
|
return
|
||||||
|
if '/replay' in msg and not '/replayspeed' in msg:
|
||||||
|
_cmd = msg.split()
|
||||||
|
if len(_cmd) == 2:
|
||||||
|
replay_id = _cmd[1]
|
||||||
|
response = requests.get(f"https://www.toptal.com/developers/hastebin/raw/{replay_id}")
|
||||||
|
log = response.text.splitlines()
|
||||||
|
ses.game.spectators.append(ses)
|
||||||
|
ses.game.replay(log)
|
||||||
|
return
|
||||||
|
if '/replayspeed' in msg:
|
||||||
|
_cmd = msg.split()
|
||||||
|
if len(_cmd) == 2:
|
||||||
|
ses.game.replay_speed = float(_cmd[1])
|
||||||
|
return
|
||||||
|
if '/startwithseed' in msg and not ses.game.started:
|
||||||
|
if len(msg.split()) > 1:
|
||||||
|
ses.game.start_game(int(msg.split()[1]))
|
||||||
|
return
|
||||||
|
elif '/removebot' in msg and not ses.game.started:
|
||||||
|
if any([p.is_bot for p in ses.game.players]):
|
||||||
|
[p for p in ses.game.players if p.is_bot][-1].disconnect()
|
||||||
|
return
|
||||||
|
elif '/togglecomp' in msg and ses.game:
|
||||||
|
ses.game.toggle_competitive()
|
||||||
|
return
|
||||||
|
if '/debug' in msg:
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2 and 'DEPLOY_KEY' in os.environ and cmd[1] == os.environ['DEPLOY_KEY']: # solo chi ha la deploy key può attivare la modalità debug
|
||||||
|
ses.game.debug = not ses.game.debug
|
||||||
|
ses.game.notify_room()
|
||||||
|
elif ses == ses.game.players[0]: # solo l'owner può attivare la modalità debug
|
||||||
|
ses.game.debug = not ses.game.debug
|
||||||
|
ses.game.notify_room()
|
||||||
|
if ses.game.debug:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'red','text':f'debug mode is now active, only the owner of the room can disable it with /debug'})
|
||||||
|
return
|
||||||
|
if not ses.game.debug:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'debug mode is not active, only the owner of the room can enable it with /debug'})
|
||||||
|
elif '/set_chars' in msg and not ses.game.started:
|
||||||
|
cmd = msg.split()
|
||||||
|
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()
|
||||||
|
elif '/nextevent' in msg and ses.game.started:
|
||||||
|
ses.game.deck.flip_event()
|
||||||
|
elif '/notify' in msg and ses.game.started:
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) >= 3:
|
||||||
|
if cmd[1] in ses.game.players_map:
|
||||||
|
ses.game.get_player_named(cmd[1]).notify_card(ses, {
|
||||||
|
'name': ' '.join(cmd[2:]),
|
||||||
|
'icon': '🚨',
|
||||||
|
'suit': 4,
|
||||||
|
'number': ' '.join(cmd[2:])
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||||
|
elif '/show_cards' in msg and ses.game.started:
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2:
|
||||||
|
if cmd[1] in ses.game.players_map:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and is looking at {cmd[1]} hand'})
|
||||||
|
for c in ses.game.get_player_named(cmd[1]).hand:
|
||||||
|
ses.notify_card(ses, c)
|
||||||
|
eventlet.sleep(0.3)
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||||
|
elif '/ddc' in msg and ses.game.started: # debug destroy cards usage: [/ddc *] [/ddc username]
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode destroyed {cmd[1]} cards'})
|
||||||
|
if cmd[1] == "*":
|
||||||
|
for p in ses.game.players_map:
|
||||||
|
ses.game.get_player_named(p).hand = []
|
||||||
|
ses.game.get_player_named(p).equipment = []
|
||||||
|
ses.game.get_player_named(p).notify_self()
|
||||||
|
elif cmd[1] in ses.game.players_map:
|
||||||
|
ses.game.get_player_named(cmd[1]).hand = []
|
||||||
|
ses.game.get_player_named(cmd[1]).equipment = []
|
||||||
|
ses.game.get_player_named(cmd[1]).notify_self()
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||||
|
elif '/dsh' in msg and ses.game.started: #debug set health usage [/dsh * hp] [/dsh username hp]
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 3:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and is changing {cmd[1]} health'})
|
||||||
|
if cmd[1] == "*":
|
||||||
|
for p in ses.game.players_map:
|
||||||
|
ses.game.get_player_named(p).lives = int(cmd[2])
|
||||||
|
ses.game.get_player_named(p).notify_self()
|
||||||
|
elif cmd[1] in ses.game.players_map:
|
||||||
|
ses.game.get_player_named(cmd[1]).lives = int(cmd[2])
|
||||||
|
ses.game.get_player_named(cmd[1]).notify_self()
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||||
|
elif '/togglebot' in msg and ses.game:
|
||||||
|
ses.game.toggle_disconnect_bot()
|
||||||
|
elif '/cancelgame' in msg and ses.game.started:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} stopped the current game'})
|
||||||
|
ses.game.reset()
|
||||||
|
elif '/startgame' in msg and not ses.game.started:
|
||||||
|
ses.game.start_game()
|
||||||
|
elif '/setbotspeed' in msg:
|
||||||
|
ses.game.bot_speed = float(msg.split()[1])
|
||||||
|
elif '/addex' in msg and not ses.game.started:
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2:
|
||||||
|
cmd[1] = cmd[1].replace('foc', 'fistful_of_cards')
|
||||||
|
if cmd[1] not in ses.game.available_expansions:
|
||||||
|
ses.game.available_expansions.append(cmd[1])
|
||||||
|
ses.game.notify_room()
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} bad format'})
|
||||||
|
elif '/setcharacter' in msg:
|
||||||
|
import bang.characters as characters
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) >= 2:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and changed character'})
|
||||||
|
chs = characters.all_characters(ses.game.expansions)
|
||||||
|
ses.character = [c for c in chs if c.name == ' '.join(cmd[1:])][0]
|
||||||
|
ses.real_character = ses.character
|
||||||
|
ses.notify_self()
|
||||||
|
elif '/setevent' in msg and ses.game and ses.game.deck: #add event before the position /setevent (position) 0 (name) Peyote
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) >= 3:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and changed event'})
|
||||||
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
chs = []
|
||||||
|
chs.extend(ce.get_all_events())
|
||||||
|
chs.append(ce.get_endgame_card())
|
||||||
|
chs.extend(ceh.get_all_events())
|
||||||
|
chs.append(ceh.get_endgame_card())
|
||||||
|
ses.game.deck.event_cards.insert(int(cmd[1]), [c for c in chs if c!=None and c.name == ' '.join(cmd[2:])][0])
|
||||||
|
ses.game.notify_event_card()
|
||||||
|
elif '/removecard' in msg:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and removed a card'})
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2:
|
||||||
|
if int(cmd[1]) < len(ses.hand):
|
||||||
|
ses.hand.pop(int(cmd[1]))
|
||||||
|
else:
|
||||||
|
ses.equipment.pop(int(cmd[1])-len(ses.hand))
|
||||||
|
ses.notify_self()
|
||||||
|
elif '/getcard' in msg:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got a card'})
|
||||||
|
import bang.cards as cs
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) >= 2:
|
||||||
|
cards = cs.get_starting_deck(ses.game.expansions)
|
||||||
|
card_names = ' '.join(cmd[1:]).split(',')
|
||||||
|
for cn in card_names:
|
||||||
|
ses.hand.append([c for c in cards if c.name == cn][0])
|
||||||
|
ses.notify_self()
|
||||||
|
elif '/getnuggets' in msg:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'red','text':f'🚨 {ses.name} is in debug mode and got nuggets'})
|
||||||
|
import bang.cards as cs
|
||||||
|
cmd = msg.split()
|
||||||
|
if len(cmd) == 2:
|
||||||
|
ses.gold_nuggets += int(cmd[1])
|
||||||
|
ses.notify_self()
|
||||||
|
elif '/gameinfo' in msg:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {dict(filter(lambda x:x[0] != "rpc_log",ses.game.__dict__.items()))}'})
|
||||||
|
elif '/meinfo' in msg:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'info: {ses.__dict__}'})
|
||||||
|
elif '/mebot' in msg:
|
||||||
|
ses.is_bot = not ses.is_bot
|
||||||
|
if (ses.is_bot):
|
||||||
|
ses.was_player = True
|
||||||
|
ses.bot_spin()
|
||||||
|
elif '/arcadekick' in msg and ses.game.started:
|
||||||
|
if len([p for p in ses.game.players if p.pending_action != PendingAction.WAIT]) == 0:
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'','text':f'KICKING THE ARCADE CABINET'})
|
||||||
|
ses.game.next_turn()
|
||||||
|
else:
|
||||||
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
|
||||||
|
else:
|
||||||
|
color = sid.encode('utf-8').hex()[-3:]
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'#{color}','text':f'[{ses.name}]: {msg}'})
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sockets for the help screen
|
||||||
|
"""
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_cards(sid):
|
||||||
|
import bang.cards as c
|
||||||
|
cards = c.get_starting_deck(['dodge_city'])
|
||||||
|
cards_dict = {}
|
||||||
|
for ca in cards:
|
||||||
|
if ca.name not in cards_dict:
|
||||||
|
cards_dict[ca.name] = ca
|
||||||
|
cards = [cards_dict[i] for i in cards_dict]
|
||||||
|
sio.emit('cards_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_characters(sid):
|
||||||
|
import bang.characters as ch
|
||||||
|
cards = ch.all_characters(['dodge_city', 'gold_rush'])
|
||||||
|
sio.emit('characters_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_highnooncards(sid):
|
||||||
|
import bang.expansions.high_noon.card_events as ceh
|
||||||
|
chs = []
|
||||||
|
chs.extend(ceh.get_all_events())
|
||||||
|
chs.append(ceh.get_endgame_card())
|
||||||
|
sio.emit('highnooncards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_foccards(sid):
|
||||||
|
import bang.expansions.fistful_of_cards.card_events as ce
|
||||||
|
chs = []
|
||||||
|
chs.extend(ce.get_all_events())
|
||||||
|
chs.append(ce.get_endgame_card())
|
||||||
|
sio.emit('foccards_info', room=sid, data=json.dumps(chs, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
def get_goldrushcards(sid):
|
||||||
|
import bang.expansions.gold_rush.shop_cards as grc
|
||||||
|
cards = grc.get_cards()
|
||||||
|
cards_dict = {}
|
||||||
|
for ca in cards:
|
||||||
|
if ca.name not in cards_dict:
|
||||||
|
cards_dict[ca.name] = ca
|
||||||
|
cards = [cards_dict[i] for i in cards_dict]
|
||||||
|
sio.emit('goldrushcards_info', room=sid, data=json.dumps(cards, default=lambda o: o.__dict__))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
eventlet.wsgi.server(eventlet.listen(('', 5001)), app)
|
0
backend/tests/__init__.py
Normal file
0
backend/tests/__init__.py
Normal file
495
backend/tests/cards_test.py
Normal file
495
backend/tests/cards_test.py
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
from random import randint
|
||||||
|
from bang.characters import Character
|
||||||
|
from bang.cards import *
|
||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
from bang.deck import Deck
|
||||||
|
from bang.game import Game
|
||||||
|
from bang.players import Player, PendingAction
|
||||||
|
|
||||||
|
# test card Barile
|
||||||
|
def test_barile():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [Character('test_char', 2)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
barrel_guy = g.players[g.turn]
|
||||||
|
barrel_guy.draw('')
|
||||||
|
barrel_guy.hand = [Barile(0,0)]
|
||||||
|
barrel_guy.play_card(0)
|
||||||
|
assert isinstance(barrel_guy.equipment[0], Barile)
|
||||||
|
barrel_guy.end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Volcanic(0,0), Bang(0,0), Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
g.players[g.turn].play_card(0, barrel_guy.name)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.WAIT
|
||||||
|
assert barrel_guy.pending_action == PendingAction.PICK
|
||||||
|
g.deck.cards[0] = Bang(Suit.HEARTS, 5)
|
||||||
|
barrel_guy.pick()
|
||||||
|
assert barrel_guy.pending_action == PendingAction.WAIT
|
||||||
|
assert barrel_guy.lives == barrel_guy.max_lives
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
g.players[g.turn].play_card(0, barrel_guy.name)
|
||||||
|
g.deck.cards[0] = Bang(Suit.SPADES, 5)
|
||||||
|
barrel_guy.pick()
|
||||||
|
assert barrel_guy.pending_action == PendingAction.WAIT
|
||||||
|
assert barrel_guy.lives == barrel_guy.max_lives - 1
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
|
||||||
|
#test card Volcanic
|
||||||
|
def test_volcanic():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [Character('test_char', 3)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
volcanic_guy = g.players[g.turn]
|
||||||
|
volcanic_guy.draw('')
|
||||||
|
volcanic_guy.hand = [Volcanic(0,0), Bang(0,0), Bang(0,0)]
|
||||||
|
volcanic_guy.play_card(0)
|
||||||
|
assert isinstance(volcanic_guy.equipment[0], Volcanic)
|
||||||
|
assert volcanic_guy.get_sight() == 1
|
||||||
|
volcanic_guy.play_card(0, g.players[(g.turn+1)%3].name)
|
||||||
|
assert len(volcanic_guy.hand) == 1
|
||||||
|
volcanic_guy.play_card(0, g.players[(g.turn+1)%3].name)
|
||||||
|
assert len(volcanic_guy.hand) == 0
|
||||||
|
|
||||||
|
# test card Dinamite
|
||||||
|
def test_dinamite():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
dinamite_guy = g.players[g.turn]
|
||||||
|
dinamite_guy.draw('')
|
||||||
|
dinamite_guy.hand = [Dinamite(0,0)]
|
||||||
|
dinamite_guy.play_card(0)
|
||||||
|
assert isinstance(dinamite_guy.equipment[0], Dinamite)
|
||||||
|
dinamite_guy.end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.cards.insert(0, Dinamite(Suit.HEARTS, 5))
|
||||||
|
dinamite_guy.pick()
|
||||||
|
assert len(dinamite_guy.equipment) == 0
|
||||||
|
dinamite_guy.draw('')
|
||||||
|
dinamite_guy.end_turn()
|
||||||
|
assert len(g.players[g.turn].equipment) == 1
|
||||||
|
g.deck.cards.insert(0, Dinamite(Suit.SPADES, 5))
|
||||||
|
g.players[g.turn].pick()
|
||||||
|
assert len(g.players[g.turn].equipment) == 0
|
||||||
|
assert g.players[g.turn].lives == 1
|
||||||
|
|
||||||
|
# test mirino
|
||||||
|
def test_mirino():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)]
|
||||||
|
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)
|
||||||
|
mirino_guy = g.players[g.turn]
|
||||||
|
mirino_guy.draw('')
|
||||||
|
mirino_guy.hand = [Mirino(0,0)]
|
||||||
|
assert mirino_guy.get_sight(countWeapon=False) == 1
|
||||||
|
mirino_guy.play_card(0)
|
||||||
|
assert mirino_guy.get_sight(countWeapon=False) == 2
|
||||||
|
|
||||||
|
# test mustang
|
||||||
|
def test_mustang():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
mustang_guy = g.players[g.turn]
|
||||||
|
mustang_guy.draw('')
|
||||||
|
mustang_guy.hand = [Mustang(0,0)]
|
||||||
|
assert mustang_guy.get_visibility() == 0
|
||||||
|
mustang_guy.play_card(0)
|
||||||
|
assert mustang_guy.get_visibility() == 1
|
||||||
|
|
||||||
|
# test Prigione
|
||||||
|
def test_prigione():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)]
|
||||||
|
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)
|
||||||
|
sheriff = g.players[g.turn]
|
||||||
|
sheriff.draw('')
|
||||||
|
sheriff.hand = [Prigione(0,0)]
|
||||||
|
sheriff.play_card(0, g.players[(g.turn+1)%4].name)
|
||||||
|
assert len(sheriff.hand) == 0
|
||||||
|
sheriff.end_turn()
|
||||||
|
g.deck.cards.insert(0, Prigione(Suit.CLUBS, 5))
|
||||||
|
skip_check = g.turn
|
||||||
|
g.players[g.turn].pick()
|
||||||
|
assert g.turn != skip_check
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Prigione(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, sheriff.name)
|
||||||
|
assert len(g.players[g.turn].hand) == 1
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%4].name)
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.cards.insert(0, Prigione(Suit.HEARTS, 5))
|
||||||
|
skip_check = g.turn
|
||||||
|
g.players[g.turn].pick()
|
||||||
|
assert g.turn == skip_check
|
||||||
|
|
||||||
|
# test all weapons ranges
|
||||||
|
def test_all_weapons():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)]
|
||||||
|
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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Volcanic(0,0), Schofield(0,0), Remington(0,0), RevCarabine(0,0), Winchester(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].get_sight() == 1
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].get_sight() == 2
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].get_sight() == 3
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].get_sight() == 4
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].get_sight() == 5
|
||||||
|
|
||||||
|
# test bang
|
||||||
|
def test_bang():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0)]
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert len(g.players[g.turn].hand) == 1
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert len(g.players[g.turn].hand) == 1
|
||||||
|
|
||||||
|
# test birra
|
||||||
|
def test_birra_2p():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Birra(0,0)]
|
||||||
|
g.players[g.turn].lives = 1
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].lives == 1
|
||||||
|
|
||||||
|
# test birra
|
||||||
|
def test_birra_3p():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
initial_p = g.players[g.turn]
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Birra(0,0)]
|
||||||
|
g.players[g.turn].lives = 1
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].lives == 2
|
||||||
|
# test beer save
|
||||||
|
g.players[g.turn].hand = [Birra(0,0)]
|
||||||
|
g.players[g.turn].lives = 1
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, initial_p.name)
|
||||||
|
assert initial_p.lives == 1
|
||||||
|
# test non overflow
|
||||||
|
g.players[g.turn].lives = g.players[g.turn].max_lives
|
||||||
|
g.players[g.turn].hand = [Birra(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].lives == g.players[g.turn].max_lives
|
||||||
|
|
||||||
|
# test CatBalou
|
||||||
|
def test_catbalou():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [CatBalou(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
g.players[g.turn].choose(0)
|
||||||
|
assert len(g.players[g.turn].hand) == 0
|
||||||
|
assert len(g.deck.scrap_pile) == 2
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 3
|
||||||
|
|
||||||
|
|
||||||
|
# test Diligenza
|
||||||
|
def test_diligenza():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)]
|
||||||
|
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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Diligenza(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
|
||||||
|
# test Duello
|
||||||
|
def test_duello():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
# winning duello
|
||||||
|
g.players[g.turn].hand = [Duello(0,0), Duello(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert len(g.players[g.turn].hand) == 1
|
||||||
|
assert g.players[(g.turn+1)%2].lives == g.players[(g.turn+1)%2].max_lives - 1
|
||||||
|
# losing duello
|
||||||
|
g.players[(g.turn+1)%2].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.WAIT
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||||
|
g.players[(g.turn+1)%2].respond(0)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.WAIT
|
||||||
|
assert g.players[g.turn].lives == g.players[g.turn].max_lives - 1
|
||||||
|
|
||||||
|
# test Emporio
|
||||||
|
def test_emporio():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)]
|
||||||
|
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)
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Emporio(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.CHOOSE
|
||||||
|
g.players[g.turn].choose(0)
|
||||||
|
print(g.players[g.turn].name)
|
||||||
|
for i in range(1, len(g.players)-1):
|
||||||
|
assert g.players[(g.turn+i)%7].pending_action == PendingAction.CHOOSE
|
||||||
|
g.players[(g.turn+i)%7].choose(0)
|
||||||
|
for p in ps:
|
||||||
|
assert len(p.hand) == 1
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
|
||||||
|
# test Gatling
|
||||||
|
def test_gatling():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)]
|
||||||
|
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)
|
||||||
|
# test lose gatling
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Gatling(0,0), Gatling(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
assert p.lives == p.max_lives - 1
|
||||||
|
# test win gatling
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
p.hand = [Mancato(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.WAIT
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
p.respond(0)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
assert p.lives == p.max_lives - 1
|
||||||
|
|
||||||
|
# test Indiani
|
||||||
|
def test_indiani():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(7)]
|
||||||
|
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)
|
||||||
|
# test lose indiani
|
||||||
|
for p in ps:
|
||||||
|
p.hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Indiani(0,0), Indiani(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
assert p.lives == p.max_lives - 1
|
||||||
|
# test win indiani
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
p.hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.WAIT
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
p.respond(0)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
for p in ps:
|
||||||
|
if p != g.players[g.turn]:
|
||||||
|
assert p.lives == p.max_lives - 1
|
||||||
|
|
||||||
|
# test Mancato
|
||||||
|
def test_mancato():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
for p in ps:
|
||||||
|
p.hand = [Mancato(0,0)]
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||||
|
g.players[(g.turn+1)%2].respond(0)
|
||||||
|
assert g.players[(g.turn+1)%2].lives == g.players[(g.turn+1)%2].max_lives
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.WAIT
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
|
||||||
|
# test Panico
|
||||||
|
def test_panico():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) 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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Panico(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
g.players[g.turn].choose(0)
|
||||||
|
assert len(g.players[g.turn].hand) == 1
|
||||||
|
assert len(g.deck.scrap_pile) == 1
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 3
|
||||||
|
|
||||||
|
# test Saloon
|
||||||
|
def test_saloon():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(8)]
|
||||||
|
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)
|
||||||
|
for p in ps:
|
||||||
|
p.lives = randint(p.max_lives-1, p.max_lives)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Saloon(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for p in ps:
|
||||||
|
assert p.lives == p.max_lives
|
||||||
|
|
||||||
|
# test WellsFargo
|
||||||
|
def test_diligenza():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(4)]
|
||||||
|
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)
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [WellsFargo(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
assert len(g.players[g.turn].hand) == 3
|
367
backend/tests/character_test.py
Normal file
367
backend/tests/character_test.py
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
from random import randint
|
||||||
|
from bang.characters import *
|
||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
from bang.deck import Deck
|
||||||
|
from bang.game import Game
|
||||||
|
from bang.players import Player, PendingAction
|
||||||
|
from bang.cards import *
|
||||||
|
|
||||||
|
def test_bartcassidy():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), BartCassidy()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if isinstance(g.players[g.turn].character, BartCassidy):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[(g.turn+1)%2].hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 0
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 1
|
||||||
|
|
||||||
|
def test_blackjack():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(1)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [BlackJack()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.deck.cards.insert(1, Bang(Suit.HEARTS, 1))
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
assert len(g.players[g.turn].hand) == 3
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.cards.insert(1, Bang(Suit.CLUBS, 1))
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.cards.insert(1, Bang(Suit.DIAMONDS, 1))
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
assert len(g.players[g.turn].hand) == 3
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.cards.insert(1, Bang(Suit.SPADES, 1))
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
|
||||||
|
def test_calamityjanet():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), CalamityJanet()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if isinstance(g.players[g.turn].character, CalamityJanet):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[(g.turn+1)%2].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||||
|
g.players[(g.turn+1)%2].respond(0)
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Mancato(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].lives == 1
|
||||||
|
|
||||||
|
def test_ElGringo():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), ElGringo()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if isinstance(g.players[g.turn].character, ElGringo):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[(g.turn+1)%2].hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0)]
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 0
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 1
|
||||||
|
assert len(g.players[g.turn].hand) == 0
|
||||||
|
|
||||||
|
def test_JesseJones():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), JesseJones()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, JesseJones):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[(g.turn+1)%2].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].draw('p1' if g.turn == 0 else 'p0')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0)]
|
||||||
|
assert len(g.players[(g.turn+1)%2].hand) == 0
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
|
||||||
|
def test_Jourdonnais():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), Jourdonnais()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if isinstance(g.players[g.turn].character, Jourdonnais):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.PICK
|
||||||
|
|
||||||
|
def test_KitCarlson():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), KitCarlson()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, KitCarlson):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Mancato(0,0)]
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.CHOOSE
|
||||||
|
assert len(g.players[g.turn].available_cards) == 3
|
||||||
|
g.players[g.turn].choose(0)
|
||||||
|
assert len(g.players[g.turn].available_cards) == 2
|
||||||
|
g.players[g.turn].choose(1)
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PLAY
|
||||||
|
|
||||||
|
def test_LuckyDuke():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [LuckyDuke(), LuckyDuke()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
g.players[0].equipment = [Prigione(0,0)]
|
||||||
|
g.players[1].equipment = [Prigione(0,0)]
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.PICK
|
||||||
|
g.deck.cards.insert(0, Bang(Suit.SPADES,0))
|
||||||
|
g.deck.cards.insert(1, Bang(Suit.HEARTS,0))
|
||||||
|
g.players[g.turn].pick()
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.DRAW
|
||||||
|
|
||||||
|
def test_PaulRegret():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), PaulRegret()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
pls = g.get_visible_players(g.players[0])
|
||||||
|
assert len(pls) == 1
|
||||||
|
assert pls[0]['name'] == g.players[1].name
|
||||||
|
assert pls[0]['dist'] > g.players[0].get_sight()
|
||||||
|
|
||||||
|
def test_PedroRamirez():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), PedroRamirez()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, PedroRamirez):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.deck.scrap_pile.append(Bang(0,0))
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].draw('scrap')
|
||||||
|
assert len(g.players[g.turn].hand) == 2
|
||||||
|
assert g.players[g.turn].hand[0].number == 0
|
||||||
|
assert g.players[g.turn].hand[0].suit == 0
|
||||||
|
assert isinstance(g.players[g.turn].hand[0], Bang)
|
||||||
|
|
||||||
|
def test_RoseDoolan():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 2), RoseDoolan()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
g.players[0].equipment = [Mustang(0,0)]
|
||||||
|
g.players[0].notify_self()
|
||||||
|
pls = g.get_visible_players(g.players[1])
|
||||||
|
print(pls)
|
||||||
|
assert len(pls) == 1
|
||||||
|
assert pls[0]['name'] != g.players[1].name
|
||||||
|
assert pls[0]['dist'] <= g.players[1].get_sight()
|
||||||
|
|
||||||
|
def test_SidKetchum():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), SidKetchum()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, SidKetchum):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].lives = 1
|
||||||
|
g.players[g.turn].scrap(0)
|
||||||
|
assert g.players[g.turn].lives == 1
|
||||||
|
g.players[g.turn].scrap(0)
|
||||||
|
assert g.players[g.turn].lives == 2
|
||||||
|
|
||||||
|
def test_SlabTheKiller():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), SlabTheKiller()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, SlabTheKiller):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Mancato(0,0)]
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||||
|
g.players[(g.turn+1)%2].respond(0)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.RESPOND
|
||||||
|
g.players[(g.turn+1)%2].respond(-1)
|
||||||
|
assert g.players[(g.turn+1)%2].pending_action == PendingAction.WAIT
|
||||||
|
assert g.players[(g.turn+1)%2].lives == 3
|
||||||
|
|
||||||
|
def test_SuzyLafayette():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), SuzyLafayette()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
g.players[1].hand = []
|
||||||
|
assert len(g.players[1].hand) == 0
|
||||||
|
g.players[1].notify_self()
|
||||||
|
assert len(g.players[1].hand) == 1
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
|
||||||
|
def test_VultureSam():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(3)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), Character('test_char', 4), VultureSam()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if isinstance(g.players[g.turn].character, VultureSam):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0), Bang(0,0), Bang(0,0)]
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
while not isinstance(g.players[g.turn].character, VultureSam):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0), Bang(0,0), Bang(0,0)]
|
||||||
|
g.players[g.turn].lives = 0
|
||||||
|
g.players[g.turn].notify_self()
|
||||||
|
assert len(g.players[2].hand) == 8
|
||||||
|
return
|
||||||
|
|
||||||
|
def test_WillyTheKid():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
ps = [Player(f'p{i}', f'p{i}', sio) for i in range(2)]
|
||||||
|
for p in ps:
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
test_chars = [Character('test_char', 4), WillyTheKid()]
|
||||||
|
for p in ps:
|
||||||
|
p.available_characters = [test_chars.pop(0)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
if not isinstance(g.players[g.turn].character, WillyTheKid):
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = []
|
||||||
|
g.players[g.turn].end_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0), Bang(0,0), Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].lives == 3
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].lives == 2
|
||||||
|
g.players[g.turn].play_card(0, g.players[(g.turn+1)%2].name)
|
||||||
|
assert g.players[(g.turn+1)%2].lives == 1
|
37
backend/tests/deck_test.py
Normal file
37
backend/tests/deck_test.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
from bang.deck import Deck
|
||||||
|
from bang.game import Game
|
||||||
|
|
||||||
|
def test_card_flip():
|
||||||
|
g = Game('test', DummySocket())
|
||||||
|
g.deck = Deck(g)
|
||||||
|
l = len(g.deck.cards)
|
||||||
|
assert g.deck.pick_and_scrap() != None
|
||||||
|
assert len(g.deck.cards) == l - 1
|
||||||
|
assert len(g.deck.scrap_pile) == 1
|
||||||
|
|
||||||
|
def test_draw():
|
||||||
|
g = Game('test', DummySocket())
|
||||||
|
g.deck = Deck(g)
|
||||||
|
l = len(g.deck.cards)
|
||||||
|
assert g.deck.draw(True) != None
|
||||||
|
assert len(g.deck.cards) == l - 1
|
||||||
|
assert len(g.deck.scrap_pile) == 0
|
||||||
|
|
||||||
|
def test_reshuffle():
|
||||||
|
g = Game('test', DummySocket())
|
||||||
|
g.deck = Deck(g)
|
||||||
|
l = len(g.deck.cards)
|
||||||
|
for i in range(80):
|
||||||
|
assert g.deck.pick_and_scrap() != None
|
||||||
|
assert len(g.deck.cards) == 79
|
||||||
|
assert len(g.deck.scrap_pile) == 1
|
||||||
|
|
||||||
|
def test_draw_from_scrap():
|
||||||
|
g = Game('test', DummySocket())
|
||||||
|
g.deck = Deck(g)
|
||||||
|
l = len(g.deck.cards)
|
||||||
|
assert g.deck.pick_and_scrap() != None
|
||||||
|
assert g.deck.draw_from_scrap_pile() != None
|
||||||
|
assert len(g.deck.cards) == 79
|
||||||
|
assert len(g.deck.scrap_pile) == 0
|
11
backend/tests/dummy_socket.py
Normal file
11
backend/tests/dummy_socket.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
class DummySocket():
|
||||||
|
def __init__(self, sio=None):
|
||||||
|
self.true_sio = sio
|
||||||
|
|
||||||
|
def emit(self, event, data=None, to=None, room=None, skip_sid=None, namespace=None, callback=None, **kwargs):
|
||||||
|
# print(f'event: {event}, data: {data}, to: {to}, room: {room}')
|
||||||
|
if self.true_sio and event == 'chat_message':
|
||||||
|
self.true_sio.emit(event, data, to, room, skip_sid, namespace, callback, **kwargs)
|
||||||
|
return True
|
||||||
|
is_fake = True
|
91
backend/tests/game_test.py
Normal file
91
backend/tests/game_test.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
from bang.deck import Deck
|
||||||
|
from bang.game import Game
|
||||||
|
from bang.players import Player, PendingAction
|
||||||
|
from bang.roles import *
|
||||||
|
from bang.cards import *
|
||||||
|
|
||||||
|
# test that game can start
|
||||||
|
def test_game_start():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
p1 = Player('p1', 'p1', sio)
|
||||||
|
g.add_player(p1)
|
||||||
|
p2 = Player('p2', 'p2', sio)
|
||||||
|
g.add_player(p2)
|
||||||
|
p3 = Player('p3', 'p3', sio)
|
||||||
|
g.add_player(p3)
|
||||||
|
assert p1.role == None
|
||||||
|
assert p2.role == None
|
||||||
|
assert p3.role == None
|
||||||
|
assert not g.started
|
||||||
|
g.start_game()
|
||||||
|
assert g.started
|
||||||
|
assert p1.role != None
|
||||||
|
assert p2.role != None
|
||||||
|
assert p3.role != None
|
||||||
|
assert len(p1.available_characters) == g.characters_to_distribute
|
||||||
|
assert len(p2.available_characters) == g.characters_to_distribute
|
||||||
|
assert len(p3.available_characters) == g.characters_to_distribute
|
||||||
|
p1.set_character(p1.available_characters[0].name)
|
||||||
|
assert p1.character != None
|
||||||
|
p2.set_character(p2.available_characters[0].name)
|
||||||
|
assert p2.character != None
|
||||||
|
p3.set_character(p3.available_characters[0].name)
|
||||||
|
assert p3.character != None
|
||||||
|
assert g.players[g.turn].pending_action == PendingAction.DRAW
|
||||||
|
|
||||||
|
# test that dodge_city is added to games with more than 8 players
|
||||||
|
def test_dodge_city():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(9):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
assert 'dodge_city' in g.expansions
|
||||||
|
|
||||||
|
# test that a game with 2 players has only renegade as role
|
||||||
|
def test_renegade_only():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
p1 = Player('p1', 'p1', sio)
|
||||||
|
g.add_player(p1)
|
||||||
|
p2 = Player('p2', 'p2', sio)
|
||||||
|
g.add_player(p2)
|
||||||
|
g.start_game()
|
||||||
|
assert isinstance(g.players[0].role, Renegade)
|
||||||
|
assert isinstance(g.players[1].role, Renegade)
|
||||||
|
|
||||||
|
# test that a game with 3 player has Renegade, Vice and Outlaw as roles
|
||||||
|
def test_renegade_vice_outlaw():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(3):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
roles = {p.role.name for p in g.players}
|
||||||
|
assert len(roles) == 3
|
||||||
|
|
||||||
|
# test that a game with 4 players has all roles except the deputy
|
||||||
|
def test_4_players_roles():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(4):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
roles = {p.role.name for p in g.players}
|
||||||
|
assert len(roles) == 3
|
||||||
|
|
||||||
|
# test that a game with 5 players has all roles
|
||||||
|
def test_5_players_roles():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(5):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
roles = {p.role.name for p in g.players}
|
||||||
|
assert len(roles) == 4
|
||||||
|
|
213
backend/tests/roles_test.py
Normal file
213
backend/tests/roles_test.py
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
from bang.characters import Character
|
||||||
|
from tests.dummy_socket import DummySocket
|
||||||
|
from bang.deck import Deck
|
||||||
|
from bang.game import Game
|
||||||
|
from bang.players import Player, PendingAction
|
||||||
|
from bang.roles import *
|
||||||
|
from bang.cards import *
|
||||||
|
|
||||||
|
# test that a game with 3 player the deputy kills renegade and wins
|
||||||
|
def test_3p_deputy_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(3):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 3
|
||||||
|
assert isinstance(g.players[g.turn].role, Vice)
|
||||||
|
for i in range(3):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, against=g.players[roles['Rinnegato']].name)
|
||||||
|
assert (hasattr(g.players[g.turn], 'win_status') and g.players[g.turn].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Rinnegato']], 'win_status') and g.players[roles['Rinnegato']].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Fuorilegge']], 'win_status') and g.players[roles['Fuorilegge']].win_status)
|
||||||
|
|
||||||
|
# test that a game with 3 player the renegade kills the outlaw and wins
|
||||||
|
def test_3p_renegade_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(3):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 3
|
||||||
|
assert isinstance(g.players[g.turn].role, Vice)
|
||||||
|
for i in range(3):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.turn = roles['Rinnegato']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, against=g.players[roles['Fuorilegge']].name)
|
||||||
|
assert (hasattr(g.players[g.turn], 'win_status') and g.players[g.turn].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Vice']], 'win_status') and g.players[roles['Vice']].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Fuorilegge']], 'win_status') and g.players[roles['Fuorilegge']].win_status)
|
||||||
|
|
||||||
|
# test that a game with 3 player the outlaw kills the deputy and wins
|
||||||
|
def test_3p_outlaw_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(3):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 3
|
||||||
|
assert isinstance(g.players[g.turn].role, Vice)
|
||||||
|
for i in range(3):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.turn = roles['Fuorilegge']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, against=g.players[roles['Vice']].name)
|
||||||
|
assert (hasattr(g.players[g.turn], 'win_status') and g.players[g.turn].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Vice']], 'win_status') and g.players[roles['Vice']].win_status)
|
||||||
|
assert not (hasattr(g.players[roles['Rinnegato']], 'win_status') and g.players[roles['Rinnegato']].win_status)
|
||||||
|
|
||||||
|
# test that a game with 4 player the outlaw kills the sheriff and win
|
||||||
|
def test_4p_outlaw_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(4):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 3
|
||||||
|
assert isinstance(g.players[g.turn].role, Sheriff)
|
||||||
|
for i in range(4):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.turn = roles['Fuorilegge']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Bang(0,0)]
|
||||||
|
g.players[g.turn].play_card(0, against=g.players[roles['Sceriffo']].name)
|
||||||
|
for i in range(4):
|
||||||
|
if isinstance(g.players[i].role, Outlaw):
|
||||||
|
assert (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
else:
|
||||||
|
assert not (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
|
||||||
|
# test that a game with 5 player the renegade kills all the other players and wins
|
||||||
|
def test_5p_renegade_gatling_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(5):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 4
|
||||||
|
assert isinstance(g.players[g.turn].role, Sheriff)
|
||||||
|
g.players[g.turn].is_my_turn = False
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.turn = roles['Rinnegato']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Gatling(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
if isinstance(g.players[i].role, Renegade):
|
||||||
|
print (g.players[i].role.name, 'win_status:', hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
assert (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
else:
|
||||||
|
print(g.players[i].role.name, 'win_status:', (hasattr(g.players[i], 'win_status') and g.players[i].win_status))
|
||||||
|
assert not (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
|
||||||
|
# test that a game with 5 player the renegade kills all the other players and wins
|
||||||
|
def test_5p_renegade_indiani_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(5):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 4
|
||||||
|
assert isinstance(g.players[g.turn].role, Sheriff)
|
||||||
|
g.players[g.turn].is_my_turn = False
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.turn = roles['Rinnegato']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Indiani(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
if isinstance(g.players[i].role, Renegade):
|
||||||
|
print (g.players[i].role.name, 'win_status:', hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
assert (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
else:
|
||||||
|
print(g.players[i].role.name, 'win_status:', (hasattr(g.players[i], 'win_status') and g.players[i].win_status))
|
||||||
|
assert not (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
|
||||||
|
# test that a game with 5 player the renegade kills the sheriff but it isn't the last alive player and the outlaws wins
|
||||||
|
def test_5p_outlaw_death_win():
|
||||||
|
sio = DummySocket()
|
||||||
|
g = Game('test', sio)
|
||||||
|
for i in range(5):
|
||||||
|
p = Player(f'p{i}', f'p{i}', sio)
|
||||||
|
g.add_player(p)
|
||||||
|
g.start_game()
|
||||||
|
for p in g.players:
|
||||||
|
p.available_characters = [Character('test_char', 4)]
|
||||||
|
p.set_character(p.available_characters[0].name)
|
||||||
|
roles = {g.players[i].role.name:i for i in range(len(g.players))}
|
||||||
|
print(roles)
|
||||||
|
assert len(roles) == 4
|
||||||
|
assert isinstance(g.players[g.turn].role, Sheriff)
|
||||||
|
g.players[g.turn].is_my_turn = False
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
g.players[i].lives = 1
|
||||||
|
g.players[i].hand = []
|
||||||
|
g.players[roles['Vice']].lives = 2
|
||||||
|
g.turn = roles['Rinnegato']
|
||||||
|
g.play_turn()
|
||||||
|
g.players[g.turn].draw('')
|
||||||
|
g.players[g.turn].hand = [Gatling(0,0)]
|
||||||
|
g.players[g.turn].play_card(0)
|
||||||
|
for i in range(len(g.players)):
|
||||||
|
if isinstance(g.players[i].role, Outlaw):
|
||||||
|
print (g.players[i].role.name, 'win_status:', hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
assert (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
||||||
|
assert (hasattr(g.players[i], 'is_dead') and g.players[i].is_dead)
|
||||||
|
else:
|
||||||
|
print(g.players[i].role.name, 'win_status:', (hasattr(g.players[i], 'win_status') and g.players[i].win_status))
|
||||||
|
assert not (hasattr(g.players[i], 'win_status') and g.players[i].win_status)
|
@ -8,27 +8,28 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.15.1",
|
"caniuse-lite": "^1.0.30001313",
|
||||||
|
"core-js": "^3.16.2",
|
||||||
"pretty-checkbox-vue": "^1.1.9",
|
"pretty-checkbox-vue": "^1.1.9",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"socket.io-client": "^4.1.2",
|
"socket.io-client": "^4.4.1",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-clipboard2": "^0.3.1",
|
"vue-clipboard2": "^0.3.3",
|
||||||
"vue-i18n": "^8.24.5",
|
"vue-i18n": "^8.27.0",
|
||||||
"vue-router": "^3.5.2",
|
"vue-router": "^3.5.3",
|
||||||
"vue-socket.io": "^3.0.10"
|
"vue-socket.io": "^3.0.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.13",
|
"@vue/cli-plugin-babel": "~4.5.15",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.13",
|
"@vue/cli-plugin-eslint": "~4.5.15",
|
||||||
"@vue/cli-plugin-pwa": "~4.5.13",
|
"@vue/cli-plugin-pwa": "~4.5.15",
|
||||||
"@vue/cli-plugin-router": "~4.5.0",
|
"@vue/cli-plugin-router": "~5.0.1",
|
||||||
"@vue/cli-service": "~4.5.13",
|
"@vue/cli-service": "~4.5.15",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
"eslint-plugin-vue": "^7.11.1",
|
"eslint-plugin-vue": "^7.20.0",
|
||||||
"vue-template-compiler": "^2.6.14",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"workbox-webpack-plugin": "^6.1.5"
|
"workbox-webpack-plugin": "^6.5.1"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
BIN
frontend/src/assets/sounds/cash.mp3
Normal file
BIN
frontend/src/assets/sounds/cash.mp3
Normal file
Binary file not shown.
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ card: true, equipment: card.is_equipment, character:card.is_character, back:card.is_back, 'usable-next-turn':card.usable_next_turn, 'must-be-used':card.must_be_used}">
|
<div :class="{ card: true, equipment: card.is_equipment, character:card.is_character, back:card.is_back, 'usable-next-turn':card.usable_next_turn, 'must-be-used':card.must_be_used, 'gold-rush': card.expansion === 'gold_rush', 'brown':card.kind === 0, 'black':card.kind === 1,}">
|
||||||
<h4>{{cardName}}</h4>
|
<h4>{{cardName}}</h4>
|
||||||
<div class="emoji">{{card.icon}}</div>
|
<div class="emoji">{{emoji}}</div>
|
||||||
<div class="alt_text">{{card.alt_text}}</div>
|
<div class="alt_text">{{card.alt_text}}</div>
|
||||||
<div class="suit">{{number}}{{suit}}</div>
|
<div class="suit">{{number}}<span :style="`${(card.suit !== undefined && card.suit%2 === 0)? 'color:red':''}`">{{suit}}</span></div>
|
||||||
<div class="expansion" v-if="card.expansion_icon">{{card.expansion_icon}}</div>
|
<div class="expansion" v-if="card.expansion_icon">{{card.expansion_icon}}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -21,16 +21,26 @@ export default {
|
|||||||
if (!this.donotlocalize && this.$t(`cards.${this.card.name}.name`) !== `cards.${this.card.name}.name`) {
|
if (!this.donotlocalize && this.$t(`cards.${this.card.name}.name`) !== `cards.${this.card.name}.name`) {
|
||||||
return this.$t(`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
|
return this.card.name
|
||||||
},
|
},
|
||||||
|
emoji(){
|
||||||
|
return this.card.icon != "you" ? this.card.icon : this.$t('you')
|
||||||
|
},
|
||||||
suit() {
|
suit() {
|
||||||
if (this.card && !isNaN(this.card.suit)) {
|
if (this.card && !isNaN(this.card.suit)) {
|
||||||
let x = ['♦️','♣️','♥️','♠️']
|
let x = ['♦️','♣️','♥️','♠️']
|
||||||
return x[this.card.suit];
|
return x[this.card.suit];
|
||||||
|
} else if (this.card.suit) {
|
||||||
|
return this.card.suit;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
number() {
|
number() {
|
||||||
|
if (isNaN(this.card.suit))
|
||||||
|
return this.card.number
|
||||||
if (this.card.number === 1) return 'A'
|
if (this.card.number === 1) return 'A'
|
||||||
else if (this.card.number === 11) return 'J'
|
else if (this.card.number === 11) return 'J'
|
||||||
else if (this.card.number === 12) return 'Q'
|
else if (this.card.number === 12) return 'Q'
|
||||||
@ -107,6 +117,17 @@ export default {
|
|||||||
);
|
);
|
||||||
border: 2pt solid rgb(50 122 172);
|
border: 2pt solid rgb(50 122 172);
|
||||||
}
|
}
|
||||||
|
.card.brown.gold-rush {
|
||||||
|
box-shadow: 0 0 0pt 4pt var(--bg-color), 0 0 5pt 4pt #aaa;
|
||||||
|
border: 2pt dotted #9C7340;
|
||||||
|
}
|
||||||
|
.card.black.gold-rush {
|
||||||
|
box-shadow: 0 0 0pt 4pt var(--bg-color), 0 0 5pt 4pt #aaa;
|
||||||
|
border: 2pt dotted #000;
|
||||||
|
}
|
||||||
|
.card.back.gold-rush {
|
||||||
|
background: repeating-linear-gradient(347deg, #ffb32f, #987e51 );
|
||||||
|
}
|
||||||
.card h4 {
|
.card h4 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -5,12 +5,15 @@
|
|||||||
<transition-group name="message" tag="div" id="chatbox">
|
<transition-group name="message" tag="div" id="chatbox">
|
||||||
<!-- <div id="chatbox"> -->
|
<!-- <div id="chatbox"> -->
|
||||||
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color}`">{{msg.text}}</p>
|
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color}`">{{msg.text}}</p>
|
||||||
<p style="margin:1pt 15pt;" class="chat-message" v-for="(msg, i) in commandSuggestion" v-bind:key="`${i}-c`" :style="`color:orange`">{{msg}}</p>
|
|
||||||
<p class="end" key="end" style="color:#0000">.</p>
|
<p class="end" key="end" style="color:#0000">.</p>
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</transition-group>
|
</transition-group>
|
||||||
|
<div v-if="commandSuggestion.length > 0">
|
||||||
|
<p style="margin:1pt 15pt;cursor:pointer;" class="chat-message" v-for="(msg, i) in commandSuggestion" v-bind:key="`${i}-c`" :style="`color:orange`"
|
||||||
|
@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">
|
<form @submit="sendChatMessage" id="msg-form">
|
||||||
<input v-model="text" style="flex-grow:2;"/>
|
<input id="my-msg" autocomplete="off" v-model="text" style="flex-grow:2;"/>
|
||||||
<input id="submit-message" type="submit" class="btn" :value="$t('submit')"/>
|
<input id="submit-message" type="submit" class="btn" :value="$t('submit')"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -23,6 +26,7 @@ import dynamite_sfx from '@/assets/sounds/dynamite.mp3'
|
|||||||
import prison_sfx from '@/assets/sounds/prison.mp3'
|
import prison_sfx from '@/assets/sounds/prison.mp3'
|
||||||
import turn_sfx from '@/assets/sounds/turn.mp3'
|
import turn_sfx from '@/assets/sounds/turn.mp3'
|
||||||
import death_sfx from '@/assets/sounds/death.mp3'
|
import death_sfx from '@/assets/sounds/death.mp3'
|
||||||
|
import cash_sfx from '@/assets/sounds/cash.mp3'
|
||||||
export default {
|
export default {
|
||||||
name: 'Chat',
|
name: 'Chat',
|
||||||
props: {
|
props: {
|
||||||
@ -32,7 +36,7 @@ export default {
|
|||||||
messages: [],
|
messages: [],
|
||||||
text: '',
|
text: '',
|
||||||
spectators: 0,
|
spectators: 0,
|
||||||
commands: ['/debug'],
|
commands: [{cmd:'/debug', help:'Toggles the debug mode'}],
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
commandSuggestion() {
|
commandSuggestion() {
|
||||||
@ -40,7 +44,7 @@ export default {
|
|||||||
if (this.text.length < 1) {
|
if (this.text.length < 1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return this.commands.filter(x => x.slice(0, this.text.length) == this.text);
|
return this.commands.filter(x => x.cmd.slice(0, this.text.length) == this.text);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sockets: {
|
sockets: {
|
||||||
@ -54,7 +58,7 @@ export default {
|
|||||||
}
|
}
|
||||||
let params = msg.split('|')
|
let params = msg.split('|')
|
||||||
let type = params.shift().substring(1)
|
let type = params.shift().substring(1)
|
||||||
if (["flipped", "respond", "play_card", "play_card_against", "play_card_for", "spilled_beer", "diligenza", "wellsfargo", "saloon", "special_calamity"].indexOf(type) !== -1){
|
if (["flipped", "respond", "play_card", "play_card_against", "play_card_for", "spilled_beer", "diligenza", "wellsfargo", "saloon", "special_calamity", 'won'].indexOf(type) !== -1){
|
||||||
params[1] = this.$t(`cards.${params[1]}.name`)
|
params[1] = this.$t(`cards.${params[1]}.name`)
|
||||||
} else if (type === "choose_character"){
|
} else if (type === "choose_character"){
|
||||||
params.push(this.$t(`cards.${params[1]}.desc`))
|
params.push(this.$t(`cards.${params[1]}.desc`))
|
||||||
@ -83,6 +87,8 @@ export default {
|
|||||||
(new Audio(dynamite_sfx)).play();
|
(new Audio(dynamite_sfx)).play();
|
||||||
} else if (type == 'prison_turn') {
|
} else if (type == 'prison_turn') {
|
||||||
(new Audio(prison_sfx)).play();
|
(new Audio(prison_sfx)).play();
|
||||||
|
} else if (type == 'purchase_card') {
|
||||||
|
(new Audio(cash_sfx)).play();
|
||||||
} else {
|
} else {
|
||||||
(new Audio(notification_sfx)).play();
|
(new Audio(notification_sfx)).play();
|
||||||
}
|
}
|
||||||
@ -115,6 +121,10 @@ export default {
|
|||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
|
fillCmd(cmd) {
|
||||||
|
this.text = cmd;
|
||||||
|
document.getElementById('my-msg').focus();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -136,6 +146,9 @@ input {
|
|||||||
height: 0pt;
|
height: 0pt;
|
||||||
margin-top: -1.5pt;
|
margin-top: -1.5pt;
|
||||||
}
|
}
|
||||||
|
.std-text {
|
||||||
|
color: var(--font-color);
|
||||||
|
}
|
||||||
.chat {
|
.chat {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -49,10 +49,12 @@ export default {
|
|||||||
//console.log(card)
|
//console.log(card)
|
||||||
if (card.noDesc || card.name == null || card.name == "PewPew!")
|
if (card.noDesc || card.name == null || card.name == "PewPew!")
|
||||||
this.desc = ""
|
this.desc = ""
|
||||||
else if (card.desc)
|
|
||||||
this.desc = (this.$i18n.locale=='it'?card.desc:card.desc_eng)
|
|
||||||
else if (card.is_character)
|
else if (card.is_character)
|
||||||
this.desc = card.name
|
this.desc = card.name
|
||||||
|
else if (card.goal)
|
||||||
|
this.desc = this.$t(`cards.${card.name}.name`)
|
||||||
|
else if (card.desc)
|
||||||
|
this.desc = (this.$i18n.locale=='it'?card.desc:card.desc_eng)
|
||||||
else
|
else
|
||||||
this.desc = this.$t(`cards.${card.name}.desc`)
|
this.desc = this.$t(`cards.${card.name}.desc`)
|
||||||
}
|
}
|
||||||
@ -62,6 +64,9 @@ export default {
|
|||||||
if (this.realCancelText == '') {
|
if (this.realCancelText == '') {
|
||||||
this.realCancelText = this.$t('cancel')
|
this.realCancelText = this.$t('cancel')
|
||||||
}
|
}
|
||||||
|
if (this.cards.length == 1) {
|
||||||
|
this.showDesc(this.cards[0])
|
||||||
|
}
|
||||||
if (this.playAudio) {
|
if (this.playAudio) {
|
||||||
(new Audio(show_sfx)).play();
|
(new Audio(show_sfx)).play();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div >
|
||||||
<div class="deck">
|
<div class="deck">
|
||||||
<card v-if="endTurnAction && isPlaying" :donotlocalize="true" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/>
|
<card v-if="endTurnAction && isPlaying" :donotlocalize="true" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/>
|
||||||
|
<div class="deck" style="position:relative" 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(-15deg) translate(0, -50px) 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(+0deg) translate(0, -50px) 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(+15deg) translate(0, -50px) 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)}"/>
|
||||||
|
<div style="position:relative">
|
||||||
|
<div class="card gold-rush back" style="position:relative; bottom:-3pt;right:-3pt;"/>
|
||||||
|
<div class="card gold-rush back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
|
||||||
|
<card :card="goldRushCardBack" :donotlocalize="true" class="gold-rush back last-event" @click.native="goldRushShopOpen = !goldRushShopOpen"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="eventCard" style="position:relative">
|
<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:relative; bottom:-3pt;right:-3pt;"/>
|
||||||
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
|
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
|
||||||
@ -20,8 +30,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition name="list">
|
<transition name="list">
|
||||||
<p v-if="eventCard" class="center-stuff"><i>{{eventDesc}}</i></p>
|
<p v-if="eventCard" class="center-stuff"><b>{{eventDesc}}</b></p>
|
||||||
</transition>
|
</transition>
|
||||||
|
<transition name="list">
|
||||||
|
<div v-if="goldRushDesc">
|
||||||
|
<p class="center-stuff">🤑️ <i>{{$t(`cards.${goldRushDesc.name}.desc`)}}</i> 🤑️</p>
|
||||||
|
<p class="center-stuff">🤑️ <b>{{goldRushDesc.number - gold_rush_discount}} 💵️</b> 🤑️</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>
|
||||||
<transition name="list">
|
<transition name="list">
|
||||||
<p v-if="desc" class="center-stuff"><i>{{desc}}</i></p>
|
<p v-if="desc" class="center-stuff"><i>{{desc}}</i></p>
|
||||||
</transition>
|
</transition>
|
||||||
@ -44,18 +63,31 @@ export default {
|
|||||||
name: 'PewPew!',
|
name: 'PewPew!',
|
||||||
icon: '💥',
|
icon: '💥',
|
||||||
},
|
},
|
||||||
|
goldRushCardBack: {
|
||||||
|
name: 'GoldRush!',
|
||||||
|
icon: '🤑️',
|
||||||
|
},
|
||||||
lastScrap: null,
|
lastScrap: null,
|
||||||
eventCard: null,
|
eventCard: null,
|
||||||
previousScrap: null,
|
previousScrap: null,
|
||||||
pending_action: false,
|
pending_action: false,
|
||||||
isPlaying: true,
|
isPlaying: true,
|
||||||
desc: '',
|
desc: '',
|
||||||
|
goldRushShopOpen: true,
|
||||||
|
goldRushCards: [],
|
||||||
|
gold_nuggets: 0,
|
||||||
|
goldRushDesc: null,
|
||||||
|
can_gold_rush_discard: false,
|
||||||
|
gold_rush_discount: 0,
|
||||||
}),
|
}),
|
||||||
sockets: {
|
sockets: {
|
||||||
self(self){
|
self(self){
|
||||||
self = JSON.parse(self)
|
self = JSON.parse(self)
|
||||||
this.isPlaying = self.lives > 0 || self.is_ghost
|
this.isPlaying = self.lives > 0 || self.is_ghost
|
||||||
this.pending_action = self.pending_action
|
this.pending_action = self.pending_action
|
||||||
|
this.gold_nuggets = self.gold_nuggets
|
||||||
|
this.can_gold_rush_discard = self.can_gold_rush_discard
|
||||||
|
this.gold_rush_discount = self.gold_rush_discount
|
||||||
},
|
},
|
||||||
scrap(card) {
|
scrap(card) {
|
||||||
this.lastScrap = card
|
this.lastScrap = card
|
||||||
@ -68,6 +100,10 @@ export default {
|
|||||||
expansion: 'fistful-of-cards',
|
expansion: 'fistful-of-cards',
|
||||||
} : card
|
} : card
|
||||||
},
|
},
|
||||||
|
gold_rush_shop(cards) {
|
||||||
|
console.log('GOLD RUSH:'+ cards)
|
||||||
|
this.goldRushCards = JSON.parse(cards)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
endTurnCard() {
|
endTurnCard() {
|
||||||
@ -102,6 +138,9 @@ export default {
|
|||||||
this.$socket.emit('draw', pile)
|
this.$socket.emit('draw', pile)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
buy_gold_rush_card(index) {
|
||||||
|
this.$socket.emit('buy_gold_rush_card', index)
|
||||||
|
},
|
||||||
event() {
|
event() {
|
||||||
if (this.pending_action !== false) {
|
if (this.pending_action !== false) {
|
||||||
this.$socket.emit('draw', 'event')
|
this.$socket.emit('draw', 'event')
|
||||||
@ -112,6 +151,14 @@ export default {
|
|||||||
this.desc = (this.$i18n.locale=='it'?this.lastScrap.desc:this.lastScrap.desc_eng)
|
this.desc = (this.$i18n.locale=='it'?this.lastScrap.desc:this.lastScrap.desc_eng)
|
||||||
else
|
else
|
||||||
this.desc = this.$t(`cards.${this.lastScrap.name}.desc`)
|
this.desc = this.$t(`cards.${this.lastScrap.name}.desc`)
|
||||||
|
},
|
||||||
|
setGoldRushDesc(card) {
|
||||||
|
this.goldRushDesc = card
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (window.innerWidth < 1000) {
|
||||||
|
this.goldRushShopOpen = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -129,6 +176,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
flex-wrap: wrap-reverse;
|
||||||
}
|
}
|
||||||
.last-scrap {
|
.last-scrap {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -140,6 +188,10 @@ export default {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
transform: translateY(-10px);
|
transform: translateY(-10px);
|
||||||
}
|
}
|
||||||
|
.gold-rush:not(.back) {
|
||||||
|
animation-duration: 0.8s;
|
||||||
|
animation-name: slidein;
|
||||||
|
}
|
||||||
@keyframes slidein {
|
@keyframes slidein {
|
||||||
from {
|
from {
|
||||||
transform: translate(-100px, 10px) scale(1.3) rotate(-10deg);
|
transform: translate(-100px, 10px) scale(1.3) rotate(-10deg);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 id="help">{{$t('help.title')}}</h1>
|
<h1 id="help">{{$t('help.title')}}</h1>
|
||||||
|
<a href="#thecards"><p>{{$t('help.gotocards')}}</p></a>
|
||||||
|
<a href="#highnooncards"><p>{{$t('help.gotohighnoon')}}</p></a>
|
||||||
|
<a href="#foccards"><p>{{$t('help.gotofoc')}}</p></a>
|
||||||
|
<a href="#goldrushcards"><p>{{$t('help.gotogoldrush')}}</p></a>
|
||||||
<h2>{{$t('help.character')}}</h2>
|
<h2>{{$t('help.character')}}</h2>
|
||||||
<p>{{$t('help.characters_special')}}</p>
|
<p>{{$t('help.characters_special')}}</p>
|
||||||
<a href="#basecharacters"><p>{{$t('help.gotoallcharacters')}}</p></a>
|
<a href="#basecharacters"><p>{{$t('help.gotoallcharacters')}}</p></a>
|
||||||
@ -55,29 +59,31 @@
|
|||||||
<li><p>{{$t('help.endgameshriffdeath')}}</p></li>
|
<li><p>{{$t('help.endgameshriffdeath')}}</p></li>
|
||||||
<li><p>{{$t('help.endgamesheriffwin')}}</p></li>
|
<li><p>{{$t('help.endgamesheriffwin')}}</p></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2>{{$t('help.thecards')}}</h2>
|
<h2 id="thecards">{{$t('help.thecards')}}</h2>
|
||||||
<div>
|
<div class="flexy-cards-wrapper">
|
||||||
<div v-for="(c, i) in cards" v-bind:key="c.name ? (c.name+c.number) : i" style="display:flex">
|
<div v-for="(c, i) in cards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||||
<Card :card="c" @pointerenter.native="''" @pointerleave.native="''"/>
|
<Card :card="c" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||||
<div style="margin-left:6pt;">
|
<div style="margin-left:6pt;">
|
||||||
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||||
<p v-if="c.is_equipment"><b>{{$t('help.equipment')}}</b></p>
|
<p v-if="c.is_equipment"><b>{{$t('help.equipment')}}</b></p>
|
||||||
<p v-if="c.is_weapon"><b>{{$t('help.weapon')}}</b></p>
|
<p v-if="c.is_weapon"><b>{{$t('help.weapon')}}</b></p>
|
||||||
|
<p v-if="c.expansion"><b>{{c.expansion}}</b></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2 id="basecharacters">{{$t('help.allcharacters')}}</h2>
|
<h2 id="basecharacters">{{$t('help.allcharacters')}}</h2>
|
||||||
<div>
|
<div class="flexy-cards-wrapper">
|
||||||
<div v-for="(c, i) in characters" v-bind:key="c.name ? (c.name+c.number) : i" style="display:flex">
|
<div v-for="(c, i) in characters" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||||
<Card :card="c" @pointerenter.native="''" @pointerleave.native="''"/>
|
<Card :card="c" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||||
<div style="margin-left:6pt;">
|
<div style="margin-left:6pt;">
|
||||||
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||||
|
<p v-if="c.expansion"><b>{{c.expansion}}</b></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2 id="highnooncards">{{$t('help.highnooncards')}}</h2>
|
<h2 id="highnooncards">{{$t('help.highnooncards')}}</h2>
|
||||||
<div>
|
<div class="flexy-cards-wrapper">
|
||||||
<div v-for="(c, i) in highnooncards" v-bind:key="c.name ? (c.name+c.number) : i" style="display:flex">
|
<div v-for="(c, i) in highnooncards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||||
<Card :card="c" :class="'high-noon last-event'" @pointerenter.native="''" @pointerleave.native="''"/>
|
<Card :card="c" :class="'high-noon last-event'" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||||
<div style="margin-left:6pt;">
|
<div style="margin-left:6pt;">
|
||||||
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||||
@ -85,14 +91,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2 id="foccards">{{$t('help.foccards')}}</h2>
|
<h2 id="foccards">{{$t('help.foccards')}}</h2>
|
||||||
<div>
|
<div class="flexy-cards-wrapper">
|
||||||
<div v-for="(c, i) in foccards" v-bind:key="c.name ? (c.name+c.number) : i" style="display:flex">
|
<div v-for="(c, i) in foccards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||||
<Card :card="c" :class="'fistful-of-cards last-event'" @pointerenter.native="''" @pointerleave.native="''"/>
|
<Card :card="c" :class="'fistful-of-cards last-event'" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||||
<div style="margin-left:6pt;">
|
<div style="margin-left:6pt;">
|
||||||
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h2 id="goldrushcards">{{$t('help.goldrushcards')}}</h2>
|
||||||
|
<div class="flexy-cards-wrapper">
|
||||||
|
<div v-for="(c, i) in goldrushcards" v-bind:key="c.name ? (c.name+c.number) : i" class="flexy-cards">
|
||||||
|
<Card :card="c" class="gold-rush" @pointerenter.native="''" @pointerleave.native="''"/>
|
||||||
|
<div style="margin-left:6pt;">
|
||||||
|
<p>{{$t(`cards.${c.name}.desc`)}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -111,6 +126,7 @@ export default {
|
|||||||
characters: [],
|
characters: [],
|
||||||
highnooncards: [],
|
highnooncards: [],
|
||||||
foccards: [],
|
foccards: [],
|
||||||
|
goldrushcards: [],
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
endTurnCard() {
|
endTurnCard() {
|
||||||
@ -140,17 +156,41 @@ export default {
|
|||||||
...x,
|
...x,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
goldrushcards_info(cardsJson) {
|
||||||
|
this.goldrushcards = JSON.parse(cardsJson).map(x=>({
|
||||||
|
...x,
|
||||||
|
}))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$socket.emit('get_cards')
|
this.$socket.emit('get_cards')
|
||||||
this.$socket.emit('get_characters')
|
this.$socket.emit('get_characters')
|
||||||
this.$socket.emit('get_highnooncards')
|
this.$socket.emit('get_highnooncards')
|
||||||
this.$socket.emit('get_foccards')
|
this.$socket.emit('get_foccards')
|
||||||
|
this.$socket.emit('get_goldrushcards')
|
||||||
document.getElementById('help').scrollIntoView();
|
document.getElementById('help').scrollIntoView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.flexy-cards-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
.flexy-cards {
|
||||||
|
flex: 30%;
|
||||||
|
display:flex;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width:500px) {
|
||||||
|
.flexy-cards {
|
||||||
|
flex: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width:800px) {
|
||||||
|
.flexy-cards {
|
||||||
|
flex: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes pick {
|
@keyframes pick {
|
||||||
0% {
|
0% {
|
||||||
transform: translate(0,0);
|
transform: translate(0,0);
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
<div v-if="!started">
|
<div v-if="!started">
|
||||||
<h3>{{$t("expansions")}}</h3>
|
<h3>{{$t("expansions")}}</h3>
|
||||||
<div v-for="ex in expansionsStatus" v-bind:key="ex.id">
|
<div v-for="ex in expansionsStatus" v-bind:key="ex.id">
|
||||||
<PrettyCheck @click.native="toggleExpansions(ex.id)" :disabled="!isRoomOwner" :checked="ex.enabled" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{ex.name}}</PrettyCheck>
|
<PrettyCheck @click.native="toggleExpansions(ex.id)" :disabled="!isRoomOwner" :checked="ex.enabled" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{ex.name}}
|
||||||
|
<p v-if="ex.is_beta" style="padding: 0px 10px;color: red;border-radius: 12pt;position: absolute;right: -50pt;top: -12pt;">BETA</p>
|
||||||
|
</PrettyCheck>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
<h3>{{$t('mods')}}</h3>
|
<h3>{{$t('mods')}}</h3>
|
||||||
@ -39,6 +41,9 @@
|
|||||||
<transition-group name="list" tag="div" class="players-table">
|
<transition-group name="list" tag="div" class="players-table">
|
||||||
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
||||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
||||||
|
<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">
|
<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">❤️</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`" :alt="i">💀</span>
|
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
||||||
@ -54,9 +59,12 @@
|
|||||||
<div class="tiny-equipment">
|
<div class="tiny-equipment">
|
||||||
<Card v-for="(card, i) in p.equipment" v-bind:key="card.name+card.number"
|
<Card v-for="(card, i) in p.equipment" v-bind:key="card.name+card.number"
|
||||||
:card="card" @click.native="selectedInfo = p.equipment"
|
:card="card" @click.native="selectedInfo = p.equipment"
|
||||||
:style="`margin-top: ${i<1?10:-(Math.min((p.equipment.length+1)*12,80))}pt`"/>
|
:style="`margin-top: ${i<1?10:-(Math.min((p.equipment.length+p.gold_rush_equipment.length+1)*12,80))}pt`"/>
|
||||||
|
<Card v-for="(card, i) in p.gold_rush_equipment" v-bind:key="card.name+card.number"
|
||||||
|
:card="card" @click.native="selectedInfo = p.gold_rush_equipment"
|
||||||
|
:style="`margin-top: ${i+p.equipment.length<1?10:-(Math.min((p.equipment.length+p.gold_rush_equipment.length+1)*12,80))}pt`"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="p.is_bot" style="position:absolute;bottom:57%;" class="center-stuff">
|
<div v-if="p.is_bot" style="position:absolute;bottom:57%;width:20pt;" class="center-stuff">
|
||||||
<span>🤖</span>
|
<span>🤖</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -70,6 +78,8 @@
|
|||||||
<Chooser v-if="selectedInfo" :text="$t('details')" :cards="selectedInfo" :cancelText="$t('ok')" :cancel="()=>{selectedInfo = null}" :select="()=>{selectedInfo = null}"/>
|
<Chooser v-if="selectedInfo" :text="$t('details')" :cards="selectedInfo" :cancelText="$t('ok')" :cancel="()=>{selectedInfo = null}" :select="()=>{selectedInfo = null}"/>
|
||||||
<transition name="bounce">
|
<transition name="bounce">
|
||||||
<Chooser v-show="hasToChoose" :text="`${$t('choose_card')}${target_p?$t('choose_card_from') + target_p:''}`" :cards="chooseCards" :select="chooseCard"/>
|
<Chooser v-show="hasToChoose" :text="`${$t('choose_card')}${target_p?$t('choose_card_from') + target_p:''}`" :cards="chooseCards" :select="chooseCard"/>
|
||||||
|
</transition>
|
||||||
|
<transition name="bounce">
|
||||||
<full-screen-input v-if="!started && hasToSetUsername" :defaultValue="storedUsername" :text="$t('choose_username')" :val="username" :cancel="setUsername" :cancelText="$t('ok')"/>
|
<full-screen-input v-if="!started && hasToSetUsername" :defaultValue="storedUsername" :text="$t('choose_username')" :val="username" :cancel="setUsername" :cancelText="$t('ok')"/>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -114,6 +124,7 @@ export default {
|
|||||||
password: '',
|
password: '',
|
||||||
togglable_expansions: [],
|
togglable_expansions: [],
|
||||||
expansions: [],
|
expansions: [],
|
||||||
|
beta_expansions: ['gold_rush'],
|
||||||
hasToSetUsername: false,
|
hasToSetUsername: false,
|
||||||
is_competitive: false,
|
is_competitive: false,
|
||||||
disconnect_bot: false,
|
disconnect_bot: false,
|
||||||
@ -199,6 +210,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
id: x,
|
id: x,
|
||||||
name: x.replace(/(^|_)([a-z])/g, function($0,$1,$2) {return ' ' + $2.toUpperCase()}),
|
name: x.replace(/(^|_)([a-z])/g, function($0,$1,$2) {return ' ' + $2.toUpperCase()}),
|
||||||
|
is_beta: this.beta_expansions.indexOf(x) !== -1,
|
||||||
enabled: this.expansions.indexOf(x) !== -1
|
enabled: this.expansions.indexOf(x) !== -1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -225,7 +237,7 @@ export default {
|
|||||||
playersTable() {
|
playersTable() {
|
||||||
if (Vue.config.devtools)
|
if (Vue.config.devtools)
|
||||||
console.log('update players')
|
console.log('update players')
|
||||||
return this.players.map((x,i) => {
|
return this.players.map((x, i) => {
|
||||||
let offsetAngle = 360.0 / this.players.length
|
let offsetAngle = 360.0 / this.players.length
|
||||||
let rotateAngle = (i) * offsetAngle
|
let rotateAngle = (i) * offsetAngle
|
||||||
let size = 130
|
let size = 130
|
||||||
@ -292,12 +304,13 @@ export default {
|
|||||||
if (Vue.config.devtools)
|
if (Vue.config.devtools)
|
||||||
console.log(pl)
|
console.log(pl)
|
||||||
let arr = []
|
let arr = []
|
||||||
for (let i=0; i<pl.ncards; i++)
|
if (this.username != player_name)
|
||||||
arr.push({
|
for (let i=0; i<pl.ncards; i++)
|
||||||
name: 'PewPew!',
|
arr.push({
|
||||||
icon: '💥',
|
name: 'PewPew!',
|
||||||
is_back: true,
|
icon: '💥',
|
||||||
})
|
is_back: true,
|
||||||
|
})
|
||||||
pl.equipment.forEach(x=>arr.push(x))
|
pl.equipment.forEach(x=>arr.push(x))
|
||||||
this.chooseCards = arr
|
this.chooseCards = arr
|
||||||
this.hasToChoose = true
|
this.hasToChoose = true
|
||||||
|
@ -147,7 +147,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.randomTip = `tip_${1+Math.floor(Math.random() * 7)}`
|
this.randomTip = `tip_${1+Math.floor(Math.random() * 8)}`
|
||||||
if (localStorage.getItem('username'))
|
if (localStorage.getItem('username'))
|
||||||
this.username = localStorage.getItem('username')
|
this.username = localStorage.getItem('username')
|
||||||
else {
|
else {
|
||||||
|
@ -11,10 +11,16 @@
|
|||||||
<span v-for="(n, i) in lives" v-bind:key="i" :alt="i">❤️</span>
|
<span v-for="(n, i) in lives" v-bind:key="i" :alt="i">❤️</span>
|
||||||
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
<span v-for="(n, i) in (max_lives-lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
|
<div v-if="gold_nuggets > 0" style="display: flex;align-items: center;margin-left: 12pt;margin-right: -10pt;justify-content: space-evenly;width: 25pt;">
|
||||||
|
<transition name="list">
|
||||||
|
<span :key="gold_nuggets">{{gold_nuggets}}</span>
|
||||||
|
</transition>
|
||||||
|
<span>💵️</span>
|
||||||
|
</div>
|
||||||
<transition-group v-if="lives > 0 || is_ghost" name="list" tag="div" style="margin: 0 0 0 10pt; display:flex;">
|
<transition-group v-if="lives > 0 || is_ghost" name="list" tag="div" style="margin: 0 0 0 10pt; display:flex;">
|
||||||
<Card v-for="card in equipment" v-bind:key="card.name+card.number" :card="card"
|
<Card v-for="card in equipmentComputed" v-bind:key="card.name+card.number" :card="card"
|
||||||
@pointerenter.native="setDesc(card)" @pointerleave.native="desc=''"
|
@pointerenter.native="setDesc(card)" @pointerleave.native="desc=''"
|
||||||
@click.native="play_card(card, true)" :class="{'cant-play':((eventCard && eventCard.name == 'Lazo') || !card.can_be_used_now)}"/>
|
@click.native="play_card(card, true)" :class="{'cant-play':((eventCard && eventCard.name == 'Lazo') || (!card.can_be_used_now && !card.is_equipment))}"/>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
<transition name="list">
|
<transition name="list">
|
||||||
@ -22,14 +28,17 @@
|
|||||||
</transition>
|
</transition>
|
||||||
<div style="margin-bottom:6pt;margin-bottom: 6pt;display: flex;flex-direction: column;">
|
<div style="margin-bottom:6pt;margin-bottom: 6pt;display: flex;flex-direction: column;">
|
||||||
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Sid Ketchum' && lives < max_lives && hand.length > 1" @click="sidWantsScrapForHealth=true">{{$t('special_ability')}}</button>
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Sid Ketchum' && lives < max_lives && hand.length > 1" @click="sidWantsScrapForHealth=true">{{$t('special_ability')}}</button>
|
||||||
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Chuck Wengam' && lives > 1" @click="chuckSpecial">{{$t('special_ability')}}</button>
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Chuck Wengam' && lives > 1" @click="()=>{$socket.emit('special', {})}">{{$t('special_ability')}}</button>
|
||||||
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'José Delgado' && special_use_count < 2 && hand.filter(x => x.is_equipment).length > 0" @click="joseScrap=true">{{$t('special_ability')}}</button>
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'José Delgado' && special_use_count < 2 && hand.filter(x => x.is_equipment).length > 0" @click="joseScrap=true">{{$t('special_ability')}}</button>
|
||||||
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Doc Holyday' && special_use_count < 1 && hand.length > 1" @click="holydayScrap=true">{{$t('special_ability')}}</button>
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Doc Holyday' && special_use_count < 1 && hand.length > 1" @click="holydayScrap=true">{{$t('special_ability')}}</button>
|
||||||
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Jacky Murieta' && gold_nuggets >=2" @click="()=>{$socket.emit('special', {})}">{{$t('special_ability')}}</button>
|
||||||
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Josh McCloud' && gold_nuggets >=2" @click="()=>{$socket.emit('special', {})}">{{$t('special_ability')}}</button>
|
||||||
|
<button :class="{'btn': true, 'cant-play':(pending_action != 2)}" :disabled="pending_action != 2" v-if="!(eventCard && eventCard.name == 'Sbornia') && is_my_turn && character.name === 'Raddie Snake' && special_use_count < 2 && gold_nuggets >=1" @click="()=>{$socket.emit('special', {})}">{{$t('special_ability')}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="lives > 0 || is_ghost" style="position:relative">
|
<div v-if="lives > 0 || is_ghost" style="position:relative">
|
||||||
<span id="hand_text">{{$t('hand')}}</span>
|
<span id="hand_text">{{$t('hand')}}</span>
|
||||||
<transition-group name="list" tag="div" :class="{hand:true, 'play-cards':pending_action===2}">
|
<transition-group name="list" tag="div" :class="{hand:true, 'play-cards':pending_action===2}">
|
||||||
<Card v-for="card in handComputed" v-bind:key="card.name+card.number" :card="card"
|
<Card v-for="card in handComputed" v-bind:key="card.name+card.number+card.suit" :card="card"
|
||||||
@click.native="play_card(card, false)"
|
@click.native="play_card(card, false)"
|
||||||
@pointerenter.native="setHint(card)" @pointerleave.native="hint=''"
|
@pointerenter.native="setHint(card)" @pointerleave.native="hint=''"
|
||||||
:class="{'cant-play':card.cantBePlayed}"/>
|
:class="{'cant-play':card.cantBePlayed}"/>
|
||||||
@ -39,18 +48,19 @@
|
|||||||
<p v-if="hint"><i>{{hint}}</i></p>
|
<p v-if="hint"><i>{{hint}}</i></p>
|
||||||
</transition>
|
</transition>
|
||||||
<Chooser v-if="is_my_turn && pending_action == 4 && (lives > 0 || is_ghost) && !(emporioCards && emporioCards.cards && emporioCards.cards.length > 0)" :text="$t('wait')" :cards="[]"/>
|
<Chooser v-if="is_my_turn && pending_action == 4 && (lives > 0 || is_ghost) && !(emporioCards && emporioCards.cards && emporioCards.cards.length > 0)" :text="$t('wait')" :cards="[]"/>
|
||||||
<Chooser v-if="card_against" :text="$t('card_against')" :hint-text="visiblePlayers.length === 0 ? $t('no_players_in_range'):''" :cards="visiblePlayers" :select="selectAgainst" :cancel="cancelCardAgainst"/>
|
<Chooser v-if="card_against" :text="$t('card_against')" :hint-text="visiblePlayers.length === 0 ? $t('no_players_in_range'):''" :cards="visiblePlayers" :select="selectAgainst" :cancel="card_against.number !== 42 ? cancelCardAgainst : null"/>
|
||||||
<Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond" :playAudio="true"/>
|
<Chooser v-if="pending_action == 3" :text="respondText" :cards="respondCards" :select="respond" :playAudio="true"/>
|
||||||
<Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose" :playAudio="true"/>
|
<Chooser v-if="shouldChooseCard" :text="$t(choose_text)" :cards="available_cards" :select="choose" :playAudio="true"/>
|
||||||
<Chooser v-if="lives <= 0 && max_lives > 0 && !is_ghost && !spectator" :text="$t('you_died')" :cancelText="$t('spectate')" :cancel="()=>{max_lives = 0; spectator = true}"/>
|
<Chooser v-if="lives <= 0 && max_lives > 0 && !is_ghost && !spectator" :text="$t('you_died')" :cancelText="$t('spectate')" :cancel="()=>{max_lives = 0; spectator = true}"/>
|
||||||
<Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" />
|
<Chooser v-if="win_status !== undefined" :text="win_status?$t('you_win'):$t('you_lose')" />
|
||||||
<Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')" />
|
<Chooser v-if="show_role" :text="$t('you_are')" :cards="[my_role]" :hintText="($i18n.locale=='it'?my_role.goal:my_role.goal_eng)" :select="() => {show_role=false}" :cancel="() => {show_role=false}" :cancelText="$t('ok')" />
|
||||||
<Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ${$t('did_pick_as')}:`" :cards="[notifycard.card]" :hintText="$t(notifycard.message)" class="turn-notify-4s"/>
|
<Chooser v-if="notifycard" :key="notifycard.card" :text="`${notifycard.player} ${$t('did_pick_as')}:`" :cards="[notifycard.card]" :hintText="$t(notifycard.message)" class="turn-notify-4s"/>
|
||||||
|
<Chooser v-if="cantplaycard" :key="cantplaycard" :text="`${$t('cantplaycard')}`" class="turn-notify-4s"/>
|
||||||
<Chooser v-if="!show_role && is_my_turn && pending_action < 2" :text="$t('play_your_turn')" :key="is_my_turn" class="turn-notify" />
|
<Chooser v-if="!show_role && is_my_turn && pending_action < 2" :text="$t('play_your_turn')" :key="is_my_turn" class="turn-notify" />
|
||||||
<Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/>
|
<Chooser v-if="!show_role && availableCharacters.length > 0" :text="$t('choose_character')" :cards="availableCharacters" :select="setCharacter"/>
|
||||||
<Chooser v-if="hasToPickResponse" :playAudio="true" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" />
|
<Chooser v-if="hasToPickResponse" :playAudio="true" :text="`${$t('pick_a_card')} ${attacker?($t('to_defend_from')+' '+attacker):''}`" :key="hasToPickResponse" class="turn-notify" />
|
||||||
<Chooser v-if="!card_against && card_with" :text="`${$t('choose_scarp_card_to')} ${card_with.name.toUpperCase()}`" :cards="handComputed.filter(x => x !== card_with)" :select="selectWith" :cancel="()=>{card_with = null}"/>
|
<Chooser v-if="!card_against && card_with" :text="`${$t('choose_scarp_card_to')} ${card_with.name.toUpperCase()}`" :cards="handComputed.filter(x => x !== card_with)" :select="selectWith" :cancel="()=>{card_with = null}"/>
|
||||||
<Chooser v-if="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${lives}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/>
|
<Chooser v-if="showScrapScreen" :text="`${$t('discard')} ${hand.length}/${maxHandLength()}`" :cards="hand" :select="scrap" :cancel="cancelEndingTurn"/>
|
||||||
<Chooser v-if="sidWantsScrapForHealth && scrapHand.length < 2" :text="`${$t('discard')} ${2 - scrapHand.length} ${$t('to_regain_1_hp')}`"
|
<Chooser v-if="sidWantsScrapForHealth && scrapHand.length < 2" :text="`${$t('discard')} ${2 - scrapHand.length} ${$t('to_regain_1_hp')}`"
|
||||||
:cards="notScrappedHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;scrapHand=[]}"/>
|
:cards="notScrappedHand" :select="sidScrap" :cancel="() => {sidWantsScrapForHealth = false;scrapHand=[]}"/>
|
||||||
<Chooser v-if="joseScrap" :text="`${$t('discard')}`"
|
<Chooser v-if="joseScrap" :text="`${$t('discard')}`"
|
||||||
@ -83,6 +93,7 @@ export default {
|
|||||||
character: null,
|
character: null,
|
||||||
availableCharacters: [],
|
availableCharacters: [],
|
||||||
equipment: [],
|
equipment: [],
|
||||||
|
gold_rush_equipment: [],
|
||||||
hand: [],
|
hand: [],
|
||||||
lives: 0,
|
lives: 0,
|
||||||
max_lives: 0,
|
max_lives: 0,
|
||||||
@ -118,6 +129,8 @@ export default {
|
|||||||
spectator: false,
|
spectator: false,
|
||||||
noStar: false,
|
noStar: false,
|
||||||
committed_suit_manette: null,
|
committed_suit_manette: null,
|
||||||
|
gold_nuggets: 0,
|
||||||
|
cantplaycard: false,
|
||||||
}),
|
}),
|
||||||
sockets: {
|
sockets: {
|
||||||
role(role) {
|
role(role) {
|
||||||
@ -141,11 +154,12 @@ export default {
|
|||||||
}
|
}
|
||||||
this.hand = self.hand
|
this.hand = self.hand
|
||||||
this.equipment = self.equipment
|
this.equipment = self.equipment
|
||||||
|
this.gold_rush_equipment = self.gold_rush_equipment
|
||||||
this.lives = self.lives
|
this.lives = self.lives
|
||||||
this.max_lives = self.max_lives
|
this.max_lives = self.max_lives
|
||||||
this.has_played_bang = self.has_played_bang
|
this.has_played_bang = self.has_played_bang
|
||||||
this.special_use_count = self.special_use_count
|
this.special_use_count = self.special_use_count
|
||||||
this.choose_text = self.choose_text
|
this.choose_text = self.choose_text.split('|')[0]
|
||||||
this.is_my_turn = self.is_my_turn
|
this.is_my_turn = self.is_my_turn
|
||||||
this.committed_suit_manette = self.committed_suit_manette
|
this.committed_suit_manette = self.committed_suit_manette
|
||||||
if (this.is_my_turn) document.title = this.$t('your_turn')+' | PewPew!'
|
if (this.is_my_turn) document.title = this.$t('your_turn')+' | PewPew!'
|
||||||
@ -169,6 +183,11 @@ export default {
|
|||||||
this.shouldChooseCard = false
|
this.shouldChooseCard = false
|
||||||
}
|
}
|
||||||
this.noStar = self.noStar
|
this.noStar = self.noStar
|
||||||
|
this.gold_nuggets = self.gold_nuggets
|
||||||
|
let mustplay = this.handComputed.filter(x => x.number == 42);
|
||||||
|
if (mustplay.length > 0) {
|
||||||
|
this.play_card(mustplay[0], false)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
self_vis(vis) {
|
self_vis(vis) {
|
||||||
// console.log('received visibility update')
|
// console.log('received visibility update')
|
||||||
@ -181,6 +200,12 @@ export default {
|
|||||||
this.notifycard = null
|
this.notifycard = null
|
||||||
}.bind(this), 4000)
|
}.bind(this), 4000)
|
||||||
},
|
},
|
||||||
|
cant_play_card() {
|
||||||
|
this.cantplaycard = true
|
||||||
|
setTimeout(function(){
|
||||||
|
this.cantplaycard = false
|
||||||
|
}.bind(this), 1000)
|
||||||
|
},
|
||||||
event_card(card) {
|
event_card(card) {
|
||||||
this.eventCard = card
|
this.eventCard = card
|
||||||
},
|
},
|
||||||
@ -227,7 +252,6 @@ export default {
|
|||||||
name: this.name,
|
name: this.name,
|
||||||
number: 0,
|
number: 0,
|
||||||
icon: this.$t('you'),
|
icon: this.$t('you'),
|
||||||
desc: this.$t('you'),
|
|
||||||
is_character: true,
|
is_character: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -243,7 +267,7 @@ export default {
|
|||||||
return x[this.pending_action]
|
return x[this.pending_action]
|
||||||
},
|
},
|
||||||
canEndTurn() {
|
canEndTurn() {
|
||||||
return (this.pending_action == 2 && this.hand.length <= (this.character.name === "Sean Mallory" && !(this.eventCard && this.eventCard.name == "Sbornia")?10:this.lives))
|
return (this.pending_action == 2 && this.hand.length <= this.maxHandLength())
|
||||||
},
|
},
|
||||||
respondCards() {
|
respondCards() {
|
||||||
let cc = [{
|
let cc = [{
|
||||||
@ -252,7 +276,9 @@ export default {
|
|||||||
is_equipment: true,
|
is_equipment: true,
|
||||||
noDesc: true,
|
noDesc: true,
|
||||||
}]
|
}]
|
||||||
this.hand.filter(x => (x.can_be_used_now && this.expected_response.indexOf(x.name) !== -1) || this.character.name === "Elena Fuente").forEach(x=>{
|
let expectedBangResponse = this.expected_response.indexOf('Bang!') !== -1
|
||||||
|
let sborniaEvent = this.eventCard && this.eventCard.name == "Sbornia"
|
||||||
|
this.hand.filter(x => (x.can_be_used_now && this.expected_response.indexOf(x.name) !== -1) || (!expectedBangResponse && this.character.name === "Elena Fuente" && !sborniaEvent)).forEach(x=>{
|
||||||
cc.push(x)
|
cc.push(x)
|
||||||
})
|
})
|
||||||
this.equipment.filter(x => x.usable_next_turn && x.can_be_used_now && this.expected_response.indexOf(x.name) !== -1).forEach(x=>{
|
this.equipment.filter(x => x.usable_next_turn && x.can_be_used_now && this.expected_response.indexOf(x.name) !== -1).forEach(x=>{
|
||||||
@ -260,6 +286,12 @@ export default {
|
|||||||
})
|
})
|
||||||
return cc
|
return cc
|
||||||
},
|
},
|
||||||
|
equipmentComputed() {
|
||||||
|
let eq = []
|
||||||
|
this.equipment.forEach(x => eq.push(x));
|
||||||
|
this.gold_rush_equipment.forEach(x => eq.push(x));
|
||||||
|
return eq
|
||||||
|
},
|
||||||
handComputed() {
|
handComputed() {
|
||||||
return this.hand.map(x => {
|
return this.hand.map(x => {
|
||||||
let cantBePlayed = false
|
let cantBePlayed = false
|
||||||
@ -278,6 +310,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
maxHandLength() {
|
||||||
|
return (this.character.name === "Sean Mallory" && !(this.eventCard && this.eventCard.name == "Sbornia")?10:(this.gold_rush_equipment.filter(x => x.name == 'Cinturone').length>0?8:this.lives))
|
||||||
|
},
|
||||||
setCharacter(char) {
|
setCharacter(char) {
|
||||||
this.availableCharacters = []
|
this.availableCharacters = []
|
||||||
this.$socket.emit('set_character', char.name)
|
this.$socket.emit('set_character', char.name)
|
||||||
@ -308,16 +343,13 @@ export default {
|
|||||||
this.scrapHand.push(this.hand.indexOf(c))
|
this.scrapHand.push(this.hand.indexOf(c))
|
||||||
},
|
},
|
||||||
holydayScrapBang(other) {
|
holydayScrapBang(other) {
|
||||||
this.$socket.emit('holyday_special', {
|
this.$socket.emit('special', {
|
||||||
cards : [this.scrapHand[0], this.scrapHand[1]],
|
cards : [this.scrapHand[0], this.scrapHand[1]],
|
||||||
against: other.name
|
against: other.name
|
||||||
})
|
})
|
||||||
this.scrapHand = []
|
this.scrapHand = []
|
||||||
this.holydayScrap = false
|
this.holydayScrap = false
|
||||||
},
|
},
|
||||||
chuckSpecial(){
|
|
||||||
this.$socket.emit('chuck_lose_hp_draw')
|
|
||||||
},
|
|
||||||
end_turn(){
|
end_turn(){
|
||||||
// console.log('ending turn')
|
// console.log('ending turn')
|
||||||
this.cancelEndingTurn()
|
this.cancelEndingTurn()
|
||||||
@ -327,10 +359,11 @@ export default {
|
|||||||
this.$socket.emit('scrap', this.hand.indexOf(c))
|
this.$socket.emit('scrap', this.hand.indexOf(c))
|
||||||
},
|
},
|
||||||
play_card(card, from_equipment) {
|
play_card(card, from_equipment) {
|
||||||
if (from_equipment && (!card.usable_next_turn || !card.can_be_used_now || (this.eventCard && this.eventCard.name == "Lazo"))) return;
|
console.log('play' + card.name)
|
||||||
|
if (from_equipment && (!card.can_be_used_now || (this.eventCard && this.eventCard.name == "Lazo"))) return;
|
||||||
else if (card.usable_next_turn && !card.can_be_used_now) return this.really_play_card(card, null);
|
else if (card.usable_next_turn && !card.can_be_used_now) return this.really_play_card(card, null);
|
||||||
let calamity_special = (card.name === 'Mancato!' && this.character.name === 'Calamity Janet')
|
let calamity_special = (card.name === 'Mancato!' && this.character.name === 'Calamity Janet')
|
||||||
let cant_play_bang = (this.has_played_bang && this.equipment.filter(x => x.name == 'Volcanic').length == 0)
|
let cant_play_bang = (this.has_played_bang && card.number !==42 && this.equipment.filter(x => x.name == 'Volcanic').length == 0)
|
||||||
if (this.pending_action == 2) {
|
if (this.pending_action == 2) {
|
||||||
this.can_target_sheriff = (card.name !== 'Prigione')
|
this.can_target_sheriff = (card.name !== 'Prigione')
|
||||||
if (card.need_with && !this.card_with) {
|
if (card.need_with && !this.card_with) {
|
||||||
@ -383,7 +416,7 @@ export default {
|
|||||||
really_play_card(card, against) {
|
really_play_card(card, against) {
|
||||||
let res = this.handComputed.indexOf(card)
|
let res = this.handComputed.indexOf(card)
|
||||||
if (res === -1) {
|
if (res === -1) {
|
||||||
res = this.equipment.indexOf(card)
|
res = this.equipmentComputed.indexOf(card)
|
||||||
if (res !== -1) res += this.hand.length
|
if (res !== -1) res += this.hand.length
|
||||||
}
|
}
|
||||||
let card_data = {
|
let card_data = {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"tip_5": "Found a bug? Message us on discord!",
|
"tip_5": "Found a bug? Message us on discord!",
|
||||||
"tip_6": "Now with a discord server!",
|
"tip_6": "Now with a discord server!",
|
||||||
"tip_7": "If you want to help us translate the game in your language, ping us on discord!",
|
"tip_7": "If you want to help us translate the game in your language, ping us on discord!",
|
||||||
|
"tip_8": "If you disconnect during in an ongoing game you will be replaced by a bot (while you are gone)!",
|
||||||
"online_players": "Online players: ",
|
"online_players": "Online players: ",
|
||||||
"choose_username": "Pick an username:",
|
"choose_username": "Pick an username:",
|
||||||
"available_lobbies": "Available Lobbies:",
|
"available_lobbies": "Available Lobbies:",
|
||||||
@ -46,6 +47,7 @@
|
|||||||
"choose_manette": "Choose a suit, you will be able to play only cards with that suit on this turn.",
|
"choose_manette": "Choose a suit, you will be able to play only cards with that suit on this turn.",
|
||||||
"hand": "HAND",
|
"hand": "HAND",
|
||||||
"card_against": "Who will you play your card against?",
|
"card_against": "Who will you play your card against?",
|
||||||
|
"choose_sid_scrap": "Discard 2 cards to not die",
|
||||||
"choose_card_to_get": "Choose a card",
|
"choose_card_to_get": "Choose a card",
|
||||||
"choose_guess": "Guess the color of the suit",
|
"choose_guess": "Guess the color of the suit",
|
||||||
"choose_ranch": "Choose the cards to replace",
|
"choose_ranch": "Choose the cards to replace",
|
||||||
@ -55,12 +57,19 @@
|
|||||||
"choose_rimbalzo_player": "Choose the target of the bang",
|
"choose_rimbalzo_player": "Choose the target of the bang",
|
||||||
"choose_rimbalzo_card": "Choose the card to discard the bang to",
|
"choose_rimbalzo_card": "Choose the card to discard the bang to",
|
||||||
"choose_nuova_identita": "Choose if you want to keep your current character, or if you want to change it and start from 2 HP",
|
"choose_nuova_identita": "Choose if you want to keep your current character, or if you want to change it and start from 2 HP",
|
||||||
|
"choose_bicchierino": "A player of your choice regains 1 hp",
|
||||||
|
"choose_bottiglia": "Choose how you will play Bottle!",
|
||||||
|
"choose_complice": "Choose how you will play Pardner!",
|
||||||
|
"choose_ricercato": "Choose who you will play Wanted against.",
|
||||||
|
"choose_birra_function": "Choose between getting 1 gold nugget by discarding beer or if you want to play the beer.",
|
||||||
"emporio_others": "{0} is choosing which card to get from the General Store",
|
"emporio_others": "{0} is choosing which card to get from the General Store",
|
||||||
"you_died": "YOU DIED",
|
"you_died": "YOU DIED",
|
||||||
"spectate": "SPECTATE",
|
"spectate": "SPECTATE",
|
||||||
"you_win": "YOU WON",
|
"you_win": "YOU WON",
|
||||||
"you_lose": "YOU LOST",
|
"you_lose": "YOU LOST",
|
||||||
"special_ability": "SPECIAL ABILITY",
|
"special_ability": "SPECIAL ABILITY",
|
||||||
|
"gold_rush_discard": "Discard another player's gold rush equipment (paying the price + 1)",
|
||||||
|
"gold_rush_discard_2": "Discard another player's gold rush equipment (paying the price + 1)",
|
||||||
"discard": "DISCARD",
|
"discard": "DISCARD",
|
||||||
"to_regain_1_hp": "TO REGAIN 1 HP",
|
"to_regain_1_hp": "TO REGAIN 1 HP",
|
||||||
"play_your_turn": "PLAY YOUR TURN",
|
"play_your_turn": "PLAY YOUR TURN",
|
||||||
@ -73,13 +82,14 @@
|
|||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"copy": "Copy invite",
|
"copy": "Copy invite",
|
||||||
"no_players_in_range": "You can't see the other players, equip a weapon or a scope!",
|
"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!",
|
||||||
"chat": {
|
"chat": {
|
||||||
"spectators": " | A spectator is watching the game | {n} spectators are watching the game",
|
"spectators": " | A spectator is watching the game | {n} spectators are watching the game",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"joined": "{0} joined the lobby",
|
"joined": "{0} joined the lobby",
|
||||||
"died": "{0} died",
|
"died": "{0} died",
|
||||||
"died_role": "{0} was a {1}!",
|
"died_role": "{0} was a {1}!",
|
||||||
"won": "{0} won!",
|
"won": "{0} won! Their role was {1}",
|
||||||
"choose_character": "{0} has {1} as character, his special ability is: {2}!",
|
"choose_character": "{0} has {1} as character, his special ability is: {2}!",
|
||||||
"starting": "The game is starting!",
|
"starting": "The game is starting!",
|
||||||
"sheriff": "{0} is the sheriff!",
|
"sheriff": "{0} is the sheriff!",
|
||||||
@ -90,7 +100,9 @@
|
|||||||
"flipped": "{0} flipped a {1} {2}.",
|
"flipped": "{0} flipped 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.",
|
"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": "{0} played {1}.",
|
||||||
|
"purchase_card": "{0} purchased {1}.",
|
||||||
"play_card_against": "{0} played {1} against {2}.",
|
"play_card_against": "{0} played {1} against {2}.",
|
||||||
"play_card_for": "{0} played {1} for {2}.",
|
"play_card_for": "{0} played {1} for {2}.",
|
||||||
"spilled_beer": "{0} spilled a {1}.",
|
"spilled_beer": "{0} spilled a {1}.",
|
||||||
@ -113,7 +125,10 @@
|
|||||||
"prison_free": "{0} got out of prison",
|
"prison_free": "{0} got out of prison",
|
||||||
"prison_turn": "{0} stayed in prison this turn",
|
"prison_turn": "{0} stayed in prison this turn",
|
||||||
"flip_event": "🎴 EVENT: {0} 🎴",
|
"flip_event": "🎴 EVENT: {0} 🎴",
|
||||||
"choose_manette": "{0} committed to play only cards of suit {1} in this turn."
|
"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",
|
||||||
|
"use_special": "{0} used the special ability of their character ({1})",
|
||||||
|
"gold_rush_pay_discard": "{0} discarded {2} from {1}."
|
||||||
},
|
},
|
||||||
"foc": {
|
"foc": {
|
||||||
"leggedelwest": "He must play this card on this turn if possible."
|
"leggedelwest": "He must play this card on this turn if possible."
|
||||||
@ -553,13 +568,108 @@
|
|||||||
"name": "Handcuffs",
|
"name": "Handcuffs",
|
||||||
"desc": "After drawing in phase 1, the player declares a suit. He will be able to use only cards of that suit for that turn"
|
"desc": "After drawing in phase 1, the player declares a suit. He will be able to use only cards of that suit for that turn"
|
||||||
},
|
},
|
||||||
"Mezzogiorno di Fuoco":{
|
"Mezzogiorno di Fuoco": {
|
||||||
"name": "High Noon",
|
"name": "High Noon",
|
||||||
"desc": "Every player loses 1 HP when their turn starts"
|
"desc": "Every player loses 1 HP when their turn starts"
|
||||||
},
|
},
|
||||||
"Per Un Pugno Di Carte":{
|
"Per Un Pugno Di Carte": {
|
||||||
"name": "A Fistful of Cards",
|
"name": "A Fistful of Cards",
|
||||||
"desc": "On the beginning of his turn, the player is target of as many Bang as how many cards he has in his hand"
|
"desc": "On the beginning of his turn, the player is target of as many Bang as how many cards he has in his hand"
|
||||||
|
},
|
||||||
|
"Pepita": {
|
||||||
|
"name": "Gold Nugget"
|
||||||
|
},
|
||||||
|
"Bicchierino": {
|
||||||
|
"name": "Shot Glass",
|
||||||
|
"desc": "You can choose a player that regains 1 HP (even you)"
|
||||||
|
},
|
||||||
|
"Bottiglia": {
|
||||||
|
"name": "Bottle",
|
||||||
|
"desc": "Can be played as Panic!, Beer or BANG!"
|
||||||
|
},
|
||||||
|
"Complice": {
|
||||||
|
"name": "Pardner",
|
||||||
|
"desc": "Can be played as General Store, Duel or Cat Balou."
|
||||||
|
},
|
||||||
|
"Corsa All Oro_gr": {
|
||||||
|
"name": "Gold Rush",
|
||||||
|
"desc": "You end your turn, regain all your HP and start a new turn."
|
||||||
|
},
|
||||||
|
"Rum": {
|
||||||
|
"name": "Rhum",
|
||||||
|
"desc": "Flip 4 cards, for each different suit you gain 1 HP."
|
||||||
|
},
|
||||||
|
"Union Pacific": {
|
||||||
|
"name": "Union Pacific",
|
||||||
|
"desc": "Draw 4 cards from the deck."
|
||||||
|
},
|
||||||
|
"Calumet": {
|
||||||
|
"name": "Calumet",
|
||||||
|
"desc": "Cards of diamonds played by other players have no effect on you."
|
||||||
|
},
|
||||||
|
"Cinturone": {
|
||||||
|
"name": "Gun Belt",
|
||||||
|
"desc": "You can keep up to 8 cards when you end your turn."
|
||||||
|
},
|
||||||
|
"Ferro di Cavallo": {
|
||||||
|
"name": "Horseshoe",
|
||||||
|
"desc": "Every time you have to flip a card you flip twice!"
|
||||||
|
},
|
||||||
|
"Piccone": {
|
||||||
|
"name": "Pickaxe",
|
||||||
|
"desc": "You get 1 more card when you draw at the beginning of the turn."
|
||||||
|
},
|
||||||
|
"Ricercato": {
|
||||||
|
"name": "Wanted",
|
||||||
|
"desc": "Play it on another player, who kills the target gets 2 extra cards and 1 gold nugget."
|
||||||
|
},
|
||||||
|
"Setaccio": {
|
||||||
|
"name": "Gold Pan",
|
||||||
|
"desc": "Pay 1 gold nugget to draw 1 card from the deck, up to twice per turn. (Click on the equipped card to use)"
|
||||||
|
},
|
||||||
|
"Stivali": {
|
||||||
|
"name": "Boots",
|
||||||
|
"desc": "Each time you lose 1 hp, you draw 1 card from the deck."
|
||||||
|
},
|
||||||
|
"Talismano": {
|
||||||
|
"name": "Lucky Charm",
|
||||||
|
"desc": "Each time you lose 1 hp, you get 1 gold nugget."
|
||||||
|
},
|
||||||
|
"Zaino": {
|
||||||
|
"name": "Rucksack",
|
||||||
|
"desc": "Pay 2 gold nugget to heal 1 HP. (Click on the equipped card to use)"
|
||||||
|
},
|
||||||
|
"Don Bell": {
|
||||||
|
"name": "Don Bell",
|
||||||
|
"desc": "When he ends his turn, he flips a card, if it flips Hearts ❤️ or Diamonds ♦️ he plays again."
|
||||||
|
},
|
||||||
|
"Dutch Will": {
|
||||||
|
"name": "Dutch Will",
|
||||||
|
"desc": "He draws 2 cards, discards 1 and takes 1 gold nugget."
|
||||||
|
},
|
||||||
|
"Jacky Murieta": {
|
||||||
|
"name": "Jacky Murieta",
|
||||||
|
"desc": "During his turn he can pay 2 gold nuggets to shoot another BANG!"
|
||||||
|
},
|
||||||
|
"Josh McCloud": {
|
||||||
|
"name": "Josh McCloud",
|
||||||
|
"desc": "He can pay 2 gold nuggets to get the first card from the Gold Rush deck."
|
||||||
|
},
|
||||||
|
"Madam Yto": {
|
||||||
|
"name": "Madam Yto",
|
||||||
|
"desc": "She draws 1 card from the deck each time a beer is played."
|
||||||
|
},
|
||||||
|
"Pretty Luzena": {
|
||||||
|
"name": "Pretty Luzena",
|
||||||
|
"desc": "Once per turn she can have a discount of 1 on the gold rush cards shop."
|
||||||
|
},
|
||||||
|
"Raddie Snake": {
|
||||||
|
"name": "Raddie Snake",
|
||||||
|
"desc": "He can throw away 1 gold nugget to draw 1 card from the deck during his turn (2 times max per turn)."
|
||||||
|
},
|
||||||
|
"Simeon Picos": {
|
||||||
|
"name": "Simeon Picos",
|
||||||
|
"desc": "He gets 1 gold nugget every time he loses 1 hp."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
@ -604,8 +714,13 @@
|
|||||||
"sheriff": "Sheriff",
|
"sheriff": "Sheriff",
|
||||||
"allcharacters": "All characters",
|
"allcharacters": "All characters",
|
||||||
"gotoallcharacters": "Jump to all characters",
|
"gotoallcharacters": "Jump to all characters",
|
||||||
|
"gotocards": "Jump to cards",
|
||||||
|
"gotohighnoon": "Jump to High Noon",
|
||||||
|
"gotofoc": "Jump to Fistful Of Cards",
|
||||||
|
"gotogoldrush": "Jump to Gold Rush",
|
||||||
"highnooncards": "High Noon - Event Cards",
|
"highnooncards": "High Noon - Event Cards",
|
||||||
"foccards": "Fistful of Cards - Event Cards"
|
"foccards": "Fistful of Cards - Event Cards",
|
||||||
|
"goldrushcards": "Gold Rush Cards"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"sepia": "Sepia",
|
"sepia": "Sepia",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"tip_5": "Se trovi un bug comunicacelo su discord!",
|
"tip_5": "Se trovi un bug comunicacelo su discord!",
|
||||||
"tip_6": "Adesso con un server discord!",
|
"tip_6": "Adesso con un server discord!",
|
||||||
"tip_7": "Se vuoi aiutarci a tradurre il gioco nella tua lingua scrivicelo su discord!",
|
"tip_7": "Se vuoi aiutarci a tradurre il gioco nella tua lingua scrivicelo su discord!",
|
||||||
|
"tip_8": "Se ti disconnetti durante una partita verrai sostituito da un bot (durante la tua assenza)!",
|
||||||
"online_players": "Giocatori online: ",
|
"online_players": "Giocatori online: ",
|
||||||
"choose_username": "Scegli un username:",
|
"choose_username": "Scegli un username:",
|
||||||
"available_lobbies": "Stanze disponibili:",
|
"available_lobbies": "Stanze disponibili:",
|
||||||
@ -47,6 +48,7 @@
|
|||||||
"hand": "MANO",
|
"hand": "MANO",
|
||||||
"card_against": "Contro chi vuoi giocare la carta",
|
"card_against": "Contro chi vuoi giocare la carta",
|
||||||
"choose_card_to_get": "Scegli che carta pescare",
|
"choose_card_to_get": "Scegli che carta pescare",
|
||||||
|
"choose_sid_scrap": "Scarta 2 carte per non morire",
|
||||||
"choose_guess": "Indovina il colore del seme",
|
"choose_guess": "Indovina il colore del seme",
|
||||||
"choose_ranch": "Scegli le carte da sostituire",
|
"choose_ranch": "Scegli le carte da sostituire",
|
||||||
"choose_dalton": "Scegli che equipaggiamento scartare",
|
"choose_dalton": "Scegli che equipaggiamento scartare",
|
||||||
@ -55,12 +57,19 @@
|
|||||||
"choose_rimbalzo_player": "Scegli contro chi scartare il bang",
|
"choose_rimbalzo_player": "Scegli contro chi scartare il bang",
|
||||||
"choose_rimbalzo_card": "Scegli contro che carta scartare il bang",
|
"choose_rimbalzo_card": "Scegli contro che carta scartare il bang",
|
||||||
"choose_nuova_identita": "Scegli se rimanere con il tuo personaggio corrente o se cambiarlo e ripartire con 2 vite.",
|
"choose_nuova_identita": "Scegli se rimanere con il tuo personaggio corrente o se cambiarlo e ripartire con 2 vite.",
|
||||||
|
"choose_bicchierino": "Scegli a chi far recuperare 1 punto vita",
|
||||||
|
"choose_bottiglia": "Scegli come giocare Bottiglia!",
|
||||||
|
"choose_complice": "Scegli come giocare Complice!",
|
||||||
|
"choose_ricercato": "Scegli il giocatore su cui vuoi giocare Ricercato",
|
||||||
|
"choose_birra_function": "Scegli tra ottenere 1 pepita scartando la birra oppure giocare la birra.",
|
||||||
"emporio_others": "{0} sta scegliendo che carta prendere dall'emporio",
|
"emporio_others": "{0} sta scegliendo che carta prendere dall'emporio",
|
||||||
"you_died": "SEI MORTO",
|
"you_died": "SEI MORTO",
|
||||||
"spectate": "SPETTATORE",
|
"spectate": "SPETTATORE",
|
||||||
"you_win": "HAI VINTO",
|
"you_win": "HAI VINTO",
|
||||||
"you_lose": "HAI PERSO",
|
"you_lose": "HAI PERSO",
|
||||||
"special_ability": "ABILITÀ SPECIALE",
|
"special_ability": "ABILITÀ SPECIALE",
|
||||||
|
"gold_rush_discard": "Fai scartare equipaggiamento gold rush (pagando pepite + 1)",
|
||||||
|
"gold_rush_discard_2": "Fai scartare equipaggiamento gold rush (pagando pepite + 1)",
|
||||||
"discard": "SCARTA",
|
"discard": "SCARTA",
|
||||||
"to_regain_1_hp": "PER RECUPERARE 1 VITA",
|
"to_regain_1_hp": "PER RECUPERARE 1 VITA",
|
||||||
"play_your_turn": "GIOCA IL TUO TURNO",
|
"play_your_turn": "GIOCA IL TUO TURNO",
|
||||||
@ -73,13 +82,14 @@
|
|||||||
"submit": "Invia",
|
"submit": "Invia",
|
||||||
"copy": "Copia invito",
|
"copy": "Copia invito",
|
||||||
"no_players_in_range": "Non vedi nessun giocatore, equipaggia un arma o un mirino!",
|
"no_players_in_range": "Non vedi nessun giocatore, equipaggia un arma o un mirino!",
|
||||||
|
"cantplaycard": "Non puoi giocare questa carta in questo modo!",
|
||||||
"chat": {
|
"chat": {
|
||||||
"spectators": " | Uno spettatore sta guardando la partita | {n} spettatori stanno guardando la partita",
|
"spectators": " | Uno spettatore sta guardando la partita | {n} spettatori stanno guardando la partita",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"joined": "{0} è entrato nella stanza",
|
"joined": "{0} è entrato nella stanza",
|
||||||
"died": "{0} è morto",
|
"died": "{0} è morto",
|
||||||
"died_role": "{0} era {1}!",
|
"died_role": "{0} era {1}!",
|
||||||
"won": "{0} ha vinto!",
|
"won": "{0} ha vinto! Il suo ruolo era {1}",
|
||||||
"choose_character": "{0} ha come personaggio {1}, la sua abilità speciale è: {2}!",
|
"choose_character": "{0} ha come personaggio {1}, la sua abilità speciale è: {2}!",
|
||||||
"starting": "La partita sta iniziando!",
|
"starting": "La partita sta iniziando!",
|
||||||
"sheriff": "{0} è lo sceriffo!",
|
"sheriff": "{0} è lo sceriffo!",
|
||||||
@ -90,7 +100,9 @@
|
|||||||
"flipped": "{0} ha estratto {1} {2}.",
|
"flipped": "{0} ha estratto {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.",
|
"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": "{0} ha giocato {1}.",
|
||||||
|
"purchase_card": "{0} ha comprato {1}.",
|
||||||
"play_card_against": "{0} ha giocato {1} contro {2}.",
|
"play_card_against": "{0} ha giocato {1} contro {2}.",
|
||||||
"play_card_for": "{0} ha giocato {1} per {2}.",
|
"play_card_for": "{0} ha giocato {1} per {2}.",
|
||||||
"spilled_beer": "{0} ha rovesciato una {1}.",
|
"spilled_beer": "{0} ha rovesciato una {1}.",
|
||||||
@ -113,7 +125,10 @@
|
|||||||
"prison_free": "{0} è uscito di prigione",
|
"prison_free": "{0} è uscito di prigione",
|
||||||
"prison_turn": "{0} rimane in prigione questo turno",
|
"prison_turn": "{0} rimane in prigione questo turno",
|
||||||
"flip_event": "🎴 EVENTO: {0} 🎴",
|
"flip_event": "🎴 EVENTO: {0} 🎴",
|
||||||
"choose_manette": "{0} si è impegnato ad usare solo carte di seme {1} in questo turno."
|
"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",
|
||||||
|
"use_special": "{0} ha usato l'abilità speciale del suo personaggio ({1})",
|
||||||
|
"gold_rush_pay_discard": "{0} ha fatto scartare {2} a {1}."
|
||||||
},
|
},
|
||||||
"foc": {
|
"foc": {
|
||||||
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"
|
"leggedelwest": "Ed è obbligato a usarla nel suo turno, se possibile"
|
||||||
@ -553,13 +568,108 @@
|
|||||||
"name": "Manette",
|
"name": "Manette",
|
||||||
"desc": "Dopo aver pescato in fase 1, il giocatore di turno dichiara un seme: potrà usare solamente carte di quel seme nel suo turno"
|
"desc": "Dopo aver pescato in fase 1, il giocatore di turno dichiara un seme: potrà usare solamente carte di quel seme nel suo turno"
|
||||||
},
|
},
|
||||||
"Mezzogiorno di Fuoco":{
|
"Mezzogiorno di Fuoco": {
|
||||||
"name": "Mezzogiorno di Fuoco",
|
"name": "Mezzogiorno di Fuoco",
|
||||||
"desc": "Ogni giocatore perde 1 punto vita all'inizio del turno"
|
"desc": "Ogni giocatore perde 1 punto vita all'inizio del turno"
|
||||||
},
|
},
|
||||||
"Per Un Pugno Di Carte":{
|
"Per Un Pugno Di Carte": {
|
||||||
"name": "Per Un Pugno Di Carte",
|
"name": "Per Un Pugno Di Carte",
|
||||||
"desc": "All'inizio del proprio turno, il giocatore subisce tanti bang quante carte ha in mano"
|
"desc": "All'inizio del proprio turno, il giocatore subisce tanti bang quante carte ha in mano"
|
||||||
|
},
|
||||||
|
"Pepita": {
|
||||||
|
"name": "Pepita D'oro"
|
||||||
|
},
|
||||||
|
"Bicchierino": {
|
||||||
|
"name": "Bicchierino",
|
||||||
|
"desc": "Un giocatore a scelta recupera una vita (anche se stessi)"
|
||||||
|
},
|
||||||
|
"Bottiglia": {
|
||||||
|
"name": "Bottiglia",
|
||||||
|
"desc": "Usa questa carta come Panico!, Birra o Bang!"
|
||||||
|
},
|
||||||
|
"Complice": {
|
||||||
|
"name": "Complice",
|
||||||
|
"desc": "Usa questa carta come Emporio, Duello o Cat Balou"
|
||||||
|
},
|
||||||
|
"Corsa All Oro_gr": {
|
||||||
|
"name": "Corsa All'Oro",
|
||||||
|
"desc": "Il tuo turno termina subito. Recuperi tutti i tuoi punti vita, poi gioca un altro turno"
|
||||||
|
},
|
||||||
|
"Rum": {
|
||||||
|
"name": "Rum",
|
||||||
|
"desc": "Estrai 4 carte dal mazzo, guadagni 1 vita per ogni seme diverso"
|
||||||
|
},
|
||||||
|
"Union Pacific": {
|
||||||
|
"name": "Union Pacific",
|
||||||
|
"desc": "Pesca 4 carte dal mazzo"
|
||||||
|
},
|
||||||
|
"Calumet": {
|
||||||
|
"name": "Calumet",
|
||||||
|
"desc": "Le carte di quadri giocate dagli altri giocatori non hanno effetto su di te"
|
||||||
|
},
|
||||||
|
"Cinturone": {
|
||||||
|
"name": "Cinturone",
|
||||||
|
"desc": "Alla fine del tuo turno puoi tenere in mano fino a 8 carte"
|
||||||
|
},
|
||||||
|
"Ferro di Cavallo": {
|
||||||
|
"name": "Ferro di Cavallo",
|
||||||
|
"desc": "Ogni volta che estrai scegli la migliore tra due carte"
|
||||||
|
},
|
||||||
|
"Piccone": {
|
||||||
|
"name": "Piccone",
|
||||||
|
"desc": "Pesca una carta in più nella tua fase 1"
|
||||||
|
},
|
||||||
|
"Ricercato": {
|
||||||
|
"name": "Ricercato",
|
||||||
|
"desc": "Scegli un giocatore qualsiasi, chi elimina quel giocatore pesca 2 carte in più e pesca una pepita"
|
||||||
|
},
|
||||||
|
"Setaccio": {
|
||||||
|
"name": "Setaccio",
|
||||||
|
"desc": "Fino a due volte in un turno puoi pagare una pepita per pescare una carta dal mazzo (Clicca sulla carta per usare l'abilità)"
|
||||||
|
},
|
||||||
|
"Stivali": {
|
||||||
|
"name": "Stivali",
|
||||||
|
"desc": "Ogni volta che perdi un punto vita peschi una carta dal mazzo"
|
||||||
|
},
|
||||||
|
"Talismano": {
|
||||||
|
"name": "Talismano",
|
||||||
|
"desc": "Ogni volta che perdi un punto vita prendi una pepita"
|
||||||
|
},
|
||||||
|
"Zaino": {
|
||||||
|
"name": "Zaino",
|
||||||
|
"desc": "Paga 2 pepite per recuperare un punto vita"
|
||||||
|
},
|
||||||
|
"Don Bell": {
|
||||||
|
"name": "Don Bell",
|
||||||
|
"desc": "Alla fine del suo turno estrae, se esce Cuori ❤️ o Quadri ♦️ gioca un altro turno"
|
||||||
|
},
|
||||||
|
"Dutch Will": {
|
||||||
|
"name": "Dutch Will",
|
||||||
|
"desc": "Pesca 2 carte, ne scarta 1 e pesca una pepita."
|
||||||
|
},
|
||||||
|
"Jacky Murieta": {
|
||||||
|
"name": "Jacky Murieta",
|
||||||
|
"desc": "Durante il suo turno può pagare 2 pepite per sparare un bang, può farlo più volte per turno"
|
||||||
|
},
|
||||||
|
"Josh McCloud": {
|
||||||
|
"name": "Josh McCloud",
|
||||||
|
"desc": "Può pescare la prima carta del mazzetto pagando solo due pepite"
|
||||||
|
},
|
||||||
|
"Madam Yto": {
|
||||||
|
"name": "Madam Yto",
|
||||||
|
"desc": "Ogni volta che viene giocata una Birra pesca 1 carta dal mazzo"
|
||||||
|
},
|
||||||
|
"Pretty Luzena": {
|
||||||
|
"name": "Pretty Luzena",
|
||||||
|
"desc": "Una volta per turno può pagare una carta del mazzetto a costo diminuito di 1"
|
||||||
|
},
|
||||||
|
"Raddie Snake": {
|
||||||
|
"name": "Raddie Snake",
|
||||||
|
"desc": "Durante il suo turno, può pagare una pepita per pescare 1 carta dal mazzo (fino a due volte per turno)"
|
||||||
|
},
|
||||||
|
"Simeon Picos": {
|
||||||
|
"name": "Simeon Picos",
|
||||||
|
"desc": "Ogni volta che viene ferito, prende una pepita"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
@ -603,9 +713,14 @@
|
|||||||
"renegade": "Rinnegato",
|
"renegade": "Rinnegato",
|
||||||
"vice": "Vice",
|
"vice": "Vice",
|
||||||
"gotoallcharacters": "Visualizza tutti i personaggi",
|
"gotoallcharacters": "Visualizza tutti i personaggi",
|
||||||
|
"gotocards": "Visualizza le carte",
|
||||||
|
"gotohighnoon": "Visualizza le carte High Noon",
|
||||||
|
"gotofoc": "Visualizza le carte Fistful Of Cards",
|
||||||
|
"gotogoldrush": "Visualizza le carte Gold Rush",
|
||||||
"allcharacters": "Tutti i personaggi",
|
"allcharacters": "Tutti i personaggi",
|
||||||
"highnooncards": "Carte Evento High Noon",
|
"highnooncards": "Carte Evento High Noon",
|
||||||
"foccards": "Carte Evento Fistful of Cards"
|
"foccards": "Carte Evento Fistful of Cards",
|
||||||
|
"goldrushcards": "Carte Gold Rush"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"sepia": "Seppia",
|
"sepia": "Seppia",
|
||||||
|
6594
frontend/yarn.lock
6594
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user