Merge branch 'main' into dev
This commit is contained in:
commit
a6edde725a
27
.devcontainer/devcontainer.json
Normal file
27
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
|
||||||
|
{
|
||||||
|
"name": "BangCodespace",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/javascript-node:0-16",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/python:1": {
|
||||||
|
"version": "3.7.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
"forwardPorts": [5001, 8080],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "cd frontend;npm i;cd ../backend;pip install -r requirements.txt"
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
9
.github/workflows/test-backend.yml
vendored
9
.github/workflows/test-backend.yml
vendored
@ -26,12 +26,3 @@ jobs:
|
|||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
python -m pytest -p no:warnings
|
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 }}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# build Vue frontend
|
# build Vue frontend
|
||||||
FROM node:lts-alpine as builder
|
FROM node:16-alpine as builder
|
||||||
COPY ./frontend .
|
COPY ./frontend .
|
||||||
RUN npm install
|
RUN npm install
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
@ -226,7 +226,7 @@ class Bang(Card):
|
|||||||
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else 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), card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -350,7 +350,7 @@ class Gatling(Card):
|
|||||||
|
|
||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.game.attack_others(player)
|
player.game.attack_others(player, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ class Mancato(Card):
|
|||||||
data=f'_special_calamity|{player.name}|{self.name}|{against}')
|
data=f'_special_calamity|{player.name}|{self.name}|{against}')
|
||||||
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
|
player.has_played_bang = True if not player.game.check_event(ceh.Sparatoria) else player.bang_used > 1
|
||||||
player.game.attack(player, against)
|
player.game.attack(player, against, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class Pugno(Card):
|
|||||||
def play_card(self, player, against, _with=None):
|
def play_card(self, player, against, _with=None):
|
||||||
if against != None:
|
if against != None:
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.game.attack(player, against)
|
player.game.attack(player, against, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ class SpringField(Card):
|
|||||||
if against != None and _with != None:
|
if against != None and _with != None:
|
||||||
player.game.deck.scrap(_with)
|
player.game.deck.scrap(_with)
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.game.attack(player, against)
|
player.game.attack(player, against, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -367,7 +367,7 @@ class Pepperbox(Bang):
|
|||||||
if self.can_be_used_now:
|
if self.can_be_used_now:
|
||||||
if against != None:
|
if against != None:
|
||||||
Card.play_card(self, player, against=against)
|
Card.play_card(self, player, against=against)
|
||||||
player.game.attack(player, against)
|
player.game.attack(player, against, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@ -392,7 +392,7 @@ class FucileDaCaccia(Card):
|
|||||||
if self.can_be_used_now:
|
if self.can_be_used_now:
|
||||||
if against != None:
|
if against != None:
|
||||||
super().play_card(player, against=against)
|
super().play_card(player, against=against)
|
||||||
player.game.attack(player, against)
|
player.game.attack(player, against, card_name=self.name)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -303,7 +303,7 @@ class Game:
|
|||||||
self.players[i].notify_self()
|
self.players[i].notify_self()
|
||||||
self.notify_event_card()
|
self.notify_event_card()
|
||||||
|
|
||||||
def attack_others(self, attacker: pl.Player):
|
def attack_others(self, attacker: pl.Player, card_name:str=None):
|
||||||
self.attack_in_progress = True
|
self.attack_in_progress = True
|
||||||
attacker.pending_action = pl.PendingAction.WAIT
|
attacker.pending_action = pl.PendingAction.WAIT
|
||||||
attacker.notify_self()
|
attacker.notify_self()
|
||||||
@ -311,7 +311,7 @@ class Game:
|
|||||||
self.ready_count = 0
|
self.ready_count = 0
|
||||||
for p in self.get_alive_players():
|
for p in self.get_alive_players():
|
||||||
if p != attacker:
|
if p != attacker:
|
||||||
if p.get_banged(attacker=attacker):
|
if p.get_banged(attacker=attacker, card_name=card_name):
|
||||||
self.waiting_for += 1
|
self.waiting_for += 1
|
||||||
p.notify_self()
|
p.notify_self()
|
||||||
if self.waiting_for == 0:
|
if self.waiting_for == 0:
|
||||||
@ -339,8 +339,8 @@ class Game:
|
|||||||
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()
|
||||||
|
|
||||||
def attack(self, attacker: pl.Player, target_username:str, double:bool=False):
|
def attack(self, attacker: pl.Player, target_username:str, double:bool=False, card_name:str=None):
|
||||||
if self.get_player_named(target_username).get_banged(attacker=attacker, double=double):
|
if self.get_player_named(target_username).get_banged(attacker=attacker, double=double, card_name=card_name):
|
||||||
self.ready_count = 0
|
self.ready_count = 0
|
||||||
self.waiting_for = 1
|
self.waiting_for = 1
|
||||||
attacker.pending_action = pl.PendingAction.WAIT
|
attacker.pending_action = pl.PendingAction.WAIT
|
||||||
|
@ -57,6 +57,7 @@ class Player:
|
|||||||
self.on_failed_response_cb = None
|
self.on_failed_response_cb = None
|
||||||
self.event_type: str = None
|
self.event_type: str = None
|
||||||
self.expected_response = []
|
self.expected_response = []
|
||||||
|
self.attacking_card = None
|
||||||
self.attacker: Player = None
|
self.attacker: Player = None
|
||||||
self.target_p: str = None
|
self.target_p: str = None
|
||||||
self.is_drawing = False
|
self.is_drawing = False
|
||||||
@ -960,8 +961,9 @@ class Player:
|
|||||||
self.on_failed_response_cb = self.take_no_damage_response
|
self.on_failed_response_cb = self.take_no_damage_response
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
|
|
||||||
def get_banged(self, attacker, double=False, no_dmg=False, card_index=None):
|
def get_banged(self, attacker, double=False, no_dmg=False, card_index=None, card_name=None):
|
||||||
self.attacker = attacker
|
self.attacker = attacker
|
||||||
|
self.attacking_card = card_name
|
||||||
print(f'attacker -> {attacker}')
|
print(f'attacker -> {attacker}')
|
||||||
self.mancato_needed = 1 if not double else 2
|
self.mancato_needed = 1 if not double else 2
|
||||||
if card_index != None:
|
if card_index != None:
|
||||||
@ -1016,6 +1018,7 @@ class Player:
|
|||||||
|
|
||||||
def get_indians(self, attacker):
|
def get_indians(self, attacker):
|
||||||
self.attacker = attacker
|
self.attacker = attacker
|
||||||
|
self.attacking_card = "Indiani!"
|
||||||
if self.character.check(self.game, chd.ApacheKid) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0: return False
|
if 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')
|
||||||
@ -1033,6 +1036,7 @@ class Player:
|
|||||||
|
|
||||||
def get_dueled(self, attacker):
|
def get_dueled(self, attacker):
|
||||||
self.attacker = attacker
|
self.attacker = attacker
|
||||||
|
self.attacking_card = "Duello"
|
||||||
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0):
|
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and 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()
|
||||||
@ -1086,6 +1090,7 @@ class Player:
|
|||||||
self.heal_if_needed()
|
self.heal_if_needed()
|
||||||
self.mancato_needed = 0
|
self.mancato_needed = 0
|
||||||
self.expected_response = []
|
self.expected_response = []
|
||||||
|
self.attacking_card = None
|
||||||
self.event_type = ''
|
self.event_type = ''
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
self.attacker = None
|
self.attacker = None
|
||||||
@ -1096,6 +1101,7 @@ class Player:
|
|||||||
self.dmg_card_index = -1
|
self.dmg_card_index = -1
|
||||||
self.mancato_needed = 0
|
self.mancato_needed = 0
|
||||||
self.expected_response = []
|
self.expected_response = []
|
||||||
|
self.attacking_card = None
|
||||||
self.event_type = ''
|
self.event_type = ''
|
||||||
self.notify_self()
|
self.notify_self()
|
||||||
self.attacker = None
|
self.attacker = None
|
||||||
|
@ -567,8 +567,11 @@ def chat_message(sid, msg, pl=None):
|
|||||||
else:
|
else:
|
||||||
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
|
sio.emit('chat_message', room=sid, data={'color': f'','text':f'{msg} COMMAND NOT FOUND'})
|
||||||
else:
|
else:
|
||||||
color = sid.encode('utf-8').hex()[-3:]
|
# get a color from sid
|
||||||
sio.emit('chat_message', room=ses.game.name, data={'color': f'#{color}','text':f'[{ses.name}]: {msg}'})
|
color = sid.encode('utf-8').hex()[0:6]
|
||||||
|
#bg color will be slightly darker and transparent
|
||||||
|
bg_color = f'{int(color[0:2],16)-10:02x}{int(color[2:4],16)-10:02x}{int(color[4:6],16)-10:02x}20'
|
||||||
|
sio.emit('chat_message', room=ses.game.name, data={'color': f'#{color}', 'bgcolor': f'#{bg_color}','text':f'[{ses.name}]: {msg}'})
|
||||||
if not ses.game.is_replay:
|
if not ses.game.is_replay:
|
||||||
Metrics.send_metric('chat_message', points=[1], tags=[f'game:{ses.game.name.replace(" ","_")}'])
|
Metrics.send_metric('chat_message', points=[1], tags=[f'game:{ses.game.name.replace(" ","_")}'])
|
||||||
|
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="chat">
|
<div class="chat" :style="`${collapsed?'min-width:0':''}`">
|
||||||
<h4 v-if="spectators > 0">{{$tc("chat.spectators", spectators)}}</h4>
|
<div class="chat-header">
|
||||||
<h3>{{$t("chat.chat")}}</h3>
|
<div style="display:flex;align-items: center;max-height: 20pt;">
|
||||||
<transition-group name="message" tag="div" id="chatbox">
|
<h3>{{$t("chat.chat")}}</h3>
|
||||||
<!-- <div id="chatbox"> -->
|
<button class="btn" @click="collapsed = !collapsed" style="max-height:20pt;">{{collapsed?">>":"X"}}</button>
|
||||||
<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>
|
</div>
|
||||||
<p class="end" key="end" style="color:#0000">.</p>
|
<h4 v-if="spectators > 0">{{$tc("chat.spectators", spectators)}}</h4>
|
||||||
<!-- </div> -->
|
|
||||||
</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>
|
</div>
|
||||||
<form @submit="sendChatMessage" id="msg-form">
|
<div class="cont">
|
||||||
<input id="my-msg" autocomplete="off" v-model="text" style="flex-grow:2;"/>
|
<transition-group name="message" tag="div" id="chatbox" :style="`${collapsed?'display:none':''}`">
|
||||||
<input id="submit-message" type="submit" class="btn" :value="$t('submit')"/>
|
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color};background:${msg.bgcolor}${msg.bgcolor?';border-left: medium solid '+msg.color+';padding-left:2pt;':''}`">{{msg.text}}</p>
|
||||||
</form>
|
<p class="end" key="end" style="color:#0000">.</p>
|
||||||
|
</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">
|
||||||
|
<input id="my-msg" autocomplete="off" v-model="text" style="flex-grow:2;"/>
|
||||||
|
<input id="submit-message" type="submit" class="btn" :value="$t('submit')"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<transition-group name="message" tag="div" id="toast-chatbox">
|
||||||
|
<p style="margin:1pt;" class="chat-message" v-for="msg in toasts" v-bind:key="`${msg.text}-c`" :style="`width:fit-content;color:${msg.color};background:${msg.bgcolor}${msg.bgcolor?';border-left: medium solid '+msg.color+';padding-left:2pt;padding-right:4pt;':''}`">{{msg.text}}</p>
|
||||||
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -34,9 +42,11 @@ export default {
|
|||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
messages: [],
|
messages: [],
|
||||||
|
toasts: [],
|
||||||
text: '',
|
text: '',
|
||||||
spectators: 0,
|
spectators: 0,
|
||||||
commands: [{cmd:'/debug', help:'Toggles the debug mode'}],
|
commands: [{cmd:'/debug', help:'Toggles the debug mode'}],
|
||||||
|
collapsed: false,
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
commandSuggestion() {
|
commandSuggestion() {
|
||||||
@ -52,8 +62,10 @@ export default {
|
|||||||
// console.log(msg)
|
// console.log(msg)
|
||||||
if ((typeof msg === "string" && msg.indexOf('_') === 0) || (msg.color != null && msg.text.indexOf('_') === 0)) {
|
if ((typeof msg === "string" && msg.indexOf('_') === 0) || (msg.color != null && msg.text.indexOf('_') === 0)) {
|
||||||
let t_color = null
|
let t_color = null
|
||||||
|
let bg_color = null
|
||||||
if (msg.color != null) {
|
if (msg.color != null) {
|
||||||
t_color = msg.color
|
t_color = msg.color
|
||||||
|
bg_color = msg.bgcolor
|
||||||
msg = msg.text
|
msg = msg.text
|
||||||
}
|
}
|
||||||
let params = msg.split('|')
|
let params = msg.split('|')
|
||||||
@ -75,7 +87,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (t_color != null) {
|
if (t_color != null) {
|
||||||
this.messages.push({color:t_color, text:this.$t(`chat.${type}`, params)});
|
this.messages.push({color:t_color, bgcolor: bg_color, text:this.$t(`chat.${type}`, params)});
|
||||||
} else {
|
} else {
|
||||||
this.messages.push({text:this.$t(`chat.${type}`, params)});
|
this.messages.push({text:this.$t(`chat.${type}`, params)});
|
||||||
}
|
}
|
||||||
@ -95,6 +107,10 @@ export default {
|
|||||||
} else { // a chat message
|
} else { // a chat message
|
||||||
(new Audio(message_sfx)).play();
|
(new Audio(message_sfx)).play();
|
||||||
this.messages.push(msg);
|
this.messages.push(msg);
|
||||||
|
if (this.collapsed || window.innerWidth < 1000) {
|
||||||
|
this.toasts.push(msg);
|
||||||
|
setTimeout(() => this.toasts.shift(), 5000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let container = this.$el.querySelector("#chatbox");
|
let container = this.$el.querySelector("#chatbox");
|
||||||
container.scrollTop = container.scrollHeight;
|
container.scrollTop = container.scrollHeight;
|
||||||
@ -149,7 +165,7 @@ input {
|
|||||||
.std-text {
|
.std-text {
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
}
|
}
|
||||||
.chat {
|
.chat, .cont {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@ -167,7 +183,10 @@ input {
|
|||||||
transform: translateX(30px);
|
transform: translateX(30px);
|
||||||
}
|
}
|
||||||
@media only screen and (min-width:1000px) {
|
@media only screen and (min-width:1000px) {
|
||||||
.chat {
|
.chat-header {
|
||||||
|
margin-left: 10pt;
|
||||||
|
}
|
||||||
|
.chat, .cont {
|
||||||
height: 90vh;
|
height: 90vh;
|
||||||
margin-left: 10pt;
|
margin-left: 10pt;
|
||||||
}
|
}
|
||||||
@ -176,6 +195,12 @@ input {
|
|||||||
margin-right: -5pt;
|
margin-right: -5pt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#toast-chatbox {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30pt;
|
||||||
|
left: 0;
|
||||||
|
background: --var(--bg-color);
|
||||||
|
}
|
||||||
@media only screen and (max-width:1000px) {
|
@media only screen and (max-width:1000px) {
|
||||||
#msg-form {
|
#msg-form {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -38,37 +38,40 @@
|
|||||||
<p v-if="players.length < 3" class="center-stuff" style="min-height: 19px;">{{$t('minimum_players')}}</p>
|
<p v-if="players.length < 3" class="center-stuff" style="min-height: 19px;">{{$t('minimum_players')}}</p>
|
||||||
<p v-else style="min-height: 19px;"> </p>
|
<p v-else style="min-height: 19px;"> </p>
|
||||||
</div>
|
</div>
|
||||||
<transition-group name="list" tag="div" class="players-table">
|
<div style="position:relative;">
|
||||||
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
<div v-if="showTurnFlow" id="turn-indicator" :class="{reversed:turnReversed}"/>
|
||||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
<transition-group name="list" tag="div" class="players-table">
|
||||||
<transition-group v-if="p.gold_nuggets && p.gold_nuggets > 0" name="list" tag="div" style="position: absolute;top: -10pt; font-size:9pt;">
|
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
||||||
<span v-for="(n, i) in p.gold_nuggets" v-bind:key="i" :alt="i">💵️</span>
|
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
||||||
</transition-group>
|
<transition-group v-if="p.gold_nuggets && p.gold_nuggets > 0" name="list" tag="div" style="position: absolute;top: -10pt; font-size:9pt;">
|
||||||
<transition-group v-if="p.max_lives && !p.is_ghost" name="list" tag="div" class="tiny-health">
|
<span v-for="(n, i) in p.gold_nuggets" v-bind:key="i" :alt="i">💵️</span>
|
||||||
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i">❤️</span>
|
</transition-group>
|
||||||
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
<transition-group v-if="p.max_lives && !p.is_ghost" name="list" tag="div" class="tiny-health">
|
||||||
</transition-group>
|
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i">❤️</span>
|
||||||
<div v-else-if="p.is_ghost" class="tiny-health">
|
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
||||||
<span>👻</span>
|
</transition-group>
|
||||||
|
<div v-else-if="p.is_ghost" class="tiny-health">
|
||||||
|
<span>👻</span>
|
||||||
|
</div>
|
||||||
|
<Card :card="p.card" @click.native="drawFromPlayer(p.name)" :donotlocalize="true" :class="{is_my_turn:p.is_my_turn}"/>
|
||||||
|
<Card v-if="p.character" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
|
||||||
|
<Card v-if="p.character && p.character.name !== p.real_character.name" style="transform:scale(0.5) translate(-90px, -50px);" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
|
||||||
|
<tiny-hand :ncards="p.ncards" @click.native="drawFromPlayer(p.name)" :ismyturn="p.pending_action === 2"/>
|
||||||
|
<span style="position:absolute;top:10pt;" class="center-stuff">{{getActionEmoji(p)}}</span>
|
||||||
|
<div class="tiny-equipment">
|
||||||
|
<Card v-for="(card, i) in p.equipment" v-bind:key="card.name+card.number"
|
||||||
|
:card="card" @click.native="selectedInfo = p.equipment"
|
||||||
|
:style="`margin-top: ${i<1?10:-(Math.min((p.equipment.length+p.gold_rush_equipment.length+1)*12,80))}pt`"/>
|
||||||
|
<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 v-if="p.is_bot" style="position:absolute;bottom:57%;width:20pt;" class="center-stuff">
|
||||||
|
<span>🤖</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Card :card="p.card" @click.native="drawFromPlayer(p.name)" :donotlocalize="true" :class="{is_my_turn:p.is_my_turn}"/>
|
</transition-group>
|
||||||
<Card v-if="p.character" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
|
</div>
|
||||||
<Card v-if="p.character && p.character.name !== p.real_character.name" style="transform:scale(0.5) translate(-90px, -50px);" :card="p.character" class="character tiny-character" @click.native="selectedInfo = [p.character]"/>
|
|
||||||
<tiny-hand :ncards="p.ncards" @click.native="drawFromPlayer(p.name)" :ismyturn="p.pending_action === 2"/>
|
|
||||||
<span style="position:absolute;top:10pt;" class="center-stuff">{{getActionEmoji(p)}}</span>
|
|
||||||
<div class="tiny-equipment">
|
|
||||||
<Card v-for="(card, i) in p.equipment" v-bind:key="card.name+card.number"
|
|
||||||
:card="card" @click.native="selectedInfo = p.equipment"
|
|
||||||
:style="`margin-top: ${i<1?10:-(Math.min((p.equipment.length+p.gold_rush_equipment.length+1)*12,80))}pt`"/>
|
|
||||||
<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 v-if="p.is_bot" style="position:absolute;bottom:57%;width:20pt;" class="center-stuff">
|
|
||||||
<span>🤖</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition-group>
|
|
||||||
<div v-if="started">
|
<div v-if="started">
|
||||||
<deck :endTurnAction="()=>{wantsToEndTurn = true}"/>
|
<deck :endTurnAction="()=>{wantsToEndTurn = true}"/>
|
||||||
<player :isEndingTurn="wantsToEndTurn" :cancelEndingTurn="()=>{wantsToEndTurn = false}" :chooseCardFromPlayer="choose" :cancelChooseCardFromPlayer="()=>{hasToChoose=false}"/>
|
<player :isEndingTurn="wantsToEndTurn" :cancelEndingTurn="()=>{wantsToEndTurn = false}" :chooseCardFromPlayer="choose" :cancelChooseCardFromPlayer="()=>{hasToChoose=false}"/>
|
||||||
@ -129,6 +132,9 @@ export default {
|
|||||||
is_competitive: false,
|
is_competitive: false,
|
||||||
disconnect_bot: false,
|
disconnect_bot: false,
|
||||||
debug_mode: false,
|
debug_mode: false,
|
||||||
|
showTurnFlow: false,
|
||||||
|
turnReversed: false,
|
||||||
|
turn: -1,
|
||||||
}),
|
}),
|
||||||
sockets: {
|
sockets: {
|
||||||
room(data) {
|
room(data) {
|
||||||
@ -347,7 +353,18 @@ export default {
|
|||||||
privateRoom(old, _new) {
|
privateRoom(old, _new) {
|
||||||
if (this.isRoomOwner && old !== _new)
|
if (this.isRoomOwner && old !== _new)
|
||||||
this.$socket.emit('private')
|
this.$socket.emit('private')
|
||||||
}
|
},
|
||||||
|
players(_, _new) {
|
||||||
|
let x = _new.findIndex(x => x.is_my_turn);
|
||||||
|
if (x !== -1 && x !== this.turn) {
|
||||||
|
this.turnReversed = (x+1 === this.turn)
|
||||||
|
this.showTurnFlow = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showTurnFlow = false
|
||||||
|
}, 1000);
|
||||||
|
this.turn = x;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (Vue.config.devtools)
|
if (Vue.config.devtools)
|
||||||
@ -409,6 +426,39 @@ export default {
|
|||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
margin-bottom: 12pt;
|
margin-bottom: 12pt;
|
||||||
}
|
}
|
||||||
|
#turn-indicator{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: linear-gradient(135deg, #cbcbcb33 25%, transparent 25%), linear-gradient(45deg, #cbcbcb33 25%, transparent 25%);
|
||||||
|
background-size: 80px 200px;
|
||||||
|
background-position: 0 100px;
|
||||||
|
background-position-x: 0;
|
||||||
|
opacity: 0;
|
||||||
|
background-repeat: repeat;
|
||||||
|
animation-name: next-turn-animation;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-iteration-count: 3;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
#turn-indicator.reversed {
|
||||||
|
background-image: linear-gradient(225deg, #cbcbcb33 25%, transparent 25%), linear-gradient(315deg, #cbcbcb33 25%, transparent 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes next-turn-animation {
|
||||||
|
0% {
|
||||||
|
background-position-x: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position-x: 80px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
background-position-x: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
.lobby {
|
.lobby {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -115,6 +115,7 @@ export default {
|
|||||||
can_target_sheriff: true,
|
can_target_sheriff: true,
|
||||||
show_role: false,
|
show_role: false,
|
||||||
attacker: undefined,
|
attacker: undefined,
|
||||||
|
attacking_card: undefined,
|
||||||
notifycard: null,
|
notifycard: null,
|
||||||
desc: '',
|
desc: '',
|
||||||
scrapHand: [],
|
scrapHand: [],
|
||||||
@ -175,6 +176,7 @@ export default {
|
|||||||
this.sight = self.sight
|
this.sight = self.sight
|
||||||
this.sight_extra = self.sight_extra
|
this.sight_extra = self.sight_extra
|
||||||
this.attacker = self.attacker
|
this.attacker = self.attacker
|
||||||
|
this.attacking_card = self.attacking_card
|
||||||
this.mancato_needed = self.mancato_needed
|
this.mancato_needed = self.mancato_needed
|
||||||
this.is_ghost = self.is_ghost
|
this.is_ghost = self.is_ghost
|
||||||
if (this.pending_action == 5 && self.target_p) {
|
if (this.pending_action == 5 && self.target_p) {
|
||||||
@ -215,7 +217,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed:{
|
computed:{
|
||||||
respondText() {
|
respondText() {
|
||||||
return `${this.$t('choose_response')}${this.attacker?(this.$t('choose_response_to')+this.attacker):''}${(this.mancato_needed>1)?(` (${this.$t('choose_response_needed')} ` + this.mancato_needed + ')'):''}`
|
let attCard = this.attacking_card ? ' ('+this.$t('cards.'+this.attacking_card+'.name')+')' : '';
|
||||||
|
return `${this.$t('choose_response')}${this.attacker?(this.$t('choose_response_to')+this.attacker+attCard):''}${(this.mancato_needed>1)?(` (${this.$t('choose_response_needed')} ` + this.mancato_needed + ')'):''}`
|
||||||
},
|
},
|
||||||
showScrapScreen() {
|
showScrapScreen() {
|
||||||
return this.isEndingTurn && !this.canEndTurn && this.is_my_turn;
|
return this.isEndingTurn && !this.canEndTurn && this.is_my_turn;
|
||||||
|
@ -653,7 +653,7 @@
|
|||||||
},
|
},
|
||||||
"Jacky Murieta": {
|
"Jacky Murieta": {
|
||||||
"name": "Jacky Murieta",
|
"name": "Jacky Murieta",
|
||||||
"desc": "During his turn he can pay 2 gold nuggets to shoot another BANG!"
|
"desc": "During his turn he can pay 2 gold nuggets for the ability to shoot another BANG!"
|
||||||
},
|
},
|
||||||
"Josh McCloud": {
|
"Josh McCloud": {
|
||||||
"name": "Josh McCloud",
|
"name": "Josh McCloud",
|
||||||
|
@ -653,7 +653,7 @@
|
|||||||
},
|
},
|
||||||
"Jacky Murieta": {
|
"Jacky Murieta": {
|
||||||
"name": "Jacky Murieta",
|
"name": "Jacky Murieta",
|
||||||
"desc": "Durante il suo turno può pagare 2 pepite per sparare un bang, può farlo più volte per turno"
|
"desc": "Durante il suo turno può pagare 2 pepite per poter sparare un altro bang, può farlo più volte per turno"
|
||||||
},
|
},
|
||||||
"Josh McCloud": {
|
"Josh McCloud": {
|
||||||
"name": "Josh McCloud",
|
"name": "Josh McCloud",
|
||||||
|
@ -3516,9 +3516,10 @@ eventemitter3@^4.0.0:
|
|||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
|
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
|
||||||
|
|
||||||
events@^3.0.0:
|
"events@^3.0.0":
|
||||||
version "3.3.0"
|
"integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
|
||||||
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
"resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
||||||
|
"version" "3.3.0"
|
||||||
|
|
||||||
eventsource@^1.0.7:
|
eventsource@^1.0.7:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user