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"
|
||||
}
|
11
.github/workflows/test-backend.yml
vendored
11
.github/workflows/test-backend.yml
vendored
@ -25,13 +25,4 @@ jobs:
|
||||
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 }}
|
||||
python -m pytest -p no:warnings
|
@ -1,5 +1,5 @@
|
||||
# build Vue frontend
|
||||
FROM node:lts-alpine as builder
|
||||
FROM node:16-alpine as builder
|
||||
COPY ./frontend .
|
||||
RUN npm install
|
||||
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
|
||||
if player.character.check(player.game, chars.WillyTheKid):
|
||||
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 False
|
||||
|
||||
@ -350,7 +350,7 @@ class Gatling(Card):
|
||||
|
||||
def play_card(self, player, against, _with=None):
|
||||
super().play_card(player, against=against)
|
||||
player.game.attack_others(player)
|
||||
player.game.attack_others(player, card_name=self.name)
|
||||
return True
|
||||
|
||||
|
||||
@ -385,7 +385,7 @@ class Mancato(Card):
|
||||
data=f'_special_calamity|{player.name}|{self.name}|{against}')
|
||||
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 False
|
||||
|
||||
|
@ -25,7 +25,7 @@ class Pugno(Card):
|
||||
def play_card(self, player, against, _with=None):
|
||||
if against != None:
|
||||
super().play_card(player, against=against)
|
||||
player.game.attack(player, against)
|
||||
player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -106,7 +106,7 @@ class SpringField(Card):
|
||||
if against != None and _with != None:
|
||||
player.game.deck.scrap(_with)
|
||||
super().play_card(player, against=against)
|
||||
player.game.attack(player, against)
|
||||
player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -367,7 +367,7 @@ class Pepperbox(Bang):
|
||||
if self.can_be_used_now:
|
||||
if against != None:
|
||||
Card.play_card(self, player, against=against)
|
||||
player.game.attack(player, against)
|
||||
player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
@ -392,7 +392,7 @@ class FucileDaCaccia(Card):
|
||||
if self.can_be_used_now:
|
||||
if against != None:
|
||||
super().play_card(player, against=against)
|
||||
player.game.attack(player, against)
|
||||
player.game.attack(player, against, card_name=self.name)
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
|
@ -303,7 +303,7 @@ class Game:
|
||||
self.players[i].notify_self()
|
||||
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
|
||||
attacker.pending_action = pl.PendingAction.WAIT
|
||||
attacker.notify_self()
|
||||
@ -311,7 +311,7 @@ class Game:
|
||||
self.ready_count = 0
|
||||
for p in self.get_alive_players():
|
||||
if p != attacker:
|
||||
if p.get_banged(attacker=attacker):
|
||||
if p.get_banged(attacker=attacker, card_name=card_name):
|
||||
self.waiting_for += 1
|
||||
p.notify_self()
|
||||
if self.waiting_for == 0:
|
||||
@ -339,8 +339,8 @@ class Game:
|
||||
if self.pending_winners and not self.someone_won:
|
||||
return self.announces_winners()
|
||||
|
||||
def attack(self, attacker: pl.Player, target_username:str, double:bool=False):
|
||||
if self.get_player_named(target_username).get_banged(attacker=attacker, double=double):
|
||||
def attack(self, attacker: pl.Player, target_username:str, double:bool=False, card_name:str=None):
|
||||
if self.get_player_named(target_username).get_banged(attacker=attacker, double=double, card_name=card_name):
|
||||
self.ready_count = 0
|
||||
self.waiting_for = 1
|
||||
attacker.pending_action = pl.PendingAction.WAIT
|
||||
|
@ -57,6 +57,7 @@ class Player:
|
||||
self.on_failed_response_cb = None
|
||||
self.event_type: str = None
|
||||
self.expected_response = []
|
||||
self.attacking_card = None
|
||||
self.attacker: Player = None
|
||||
self.target_p: str = None
|
||||
self.is_drawing = False
|
||||
@ -960,8 +961,9 @@ class Player:
|
||||
self.on_failed_response_cb = self.take_no_damage_response
|
||||
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.attacking_card = card_name
|
||||
print(f'attacker -> {attacker}')
|
||||
self.mancato_needed = 1 if not double else 2
|
||||
if card_index != None:
|
||||
@ -1016,6 +1018,7 @@ class Player:
|
||||
|
||||
def get_indians(self, attacker):
|
||||
self.attacker = attacker
|
||||
self.attacking_card = "Indiani!"
|
||||
if self.character.check(self.game, chd.ApacheKid) or len([c for c in self.gold_rush_equipment if isinstance(c, grc.Calumet)]) > 0: return False
|
||||
if not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0:
|
||||
print('Cant defend')
|
||||
@ -1033,6 +1036,7 @@ class Player:
|
||||
|
||||
def get_dueled(self, attacker):
|
||||
self.attacker = attacker
|
||||
self.attacking_card = "Duello"
|
||||
if (self.game.check_event(ceh.Sermone) and self.is_my_turn) or (not self.game.is_competitive and len([c for c in self.hand if isinstance(c, cs.Bang) or (self.character.check(self.game, chars.CalamityJanet) and isinstance(c, cs.Mancato))]) == 0):
|
||||
print('Cant defend')
|
||||
self.take_damage_response()
|
||||
@ -1086,6 +1090,7 @@ class Player:
|
||||
self.heal_if_needed()
|
||||
self.mancato_needed = 0
|
||||
self.expected_response = []
|
||||
self.attacking_card = None
|
||||
self.event_type = ''
|
||||
self.notify_self()
|
||||
self.attacker = None
|
||||
@ -1096,6 +1101,7 @@ class Player:
|
||||
self.dmg_card_index = -1
|
||||
self.mancato_needed = 0
|
||||
self.expected_response = []
|
||||
self.attacking_card = None
|
||||
self.event_type = ''
|
||||
self.notify_self()
|
||||
self.attacker = None
|
||||
|
@ -567,8 +567,11 @@ def chat_message(sid, msg, pl=None):
|
||||
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}'})
|
||||
# get a color from sid
|
||||
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:
|
||||
Metrics.send_metric('chat_message', points=[1], tags=[f'game:{ses.game.name.replace(" ","_")}'])
|
||||
|
||||
|
@ -1,21 +1,29 @@
|
||||
<template>
|
||||
<div class="chat">
|
||||
<h4 v-if="spectators > 0">{{$tc("chat.spectators", spectators)}}</h4>
|
||||
<h3>{{$t("chat.chat")}}</h3>
|
||||
<transition-group name="message" tag="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 class="end" key="end" style="color:#0000">.</p>
|
||||
<!-- </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 class="chat" :style="`${collapsed?'min-width:0':''}`">
|
||||
<div class="chat-header">
|
||||
<div style="display:flex;align-items: center;max-height: 20pt;">
|
||||
<h3>{{$t("chat.chat")}}</h3>
|
||||
<button class="btn" @click="collapsed = !collapsed" style="max-height:20pt;">{{collapsed?">>":"X"}}</button>
|
||||
</div>
|
||||
<h4 v-if="spectators > 0">{{$tc("chat.spectators", spectators)}}</h4>
|
||||
</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 class="cont">
|
||||
<transition-group name="message" tag="div" id="chatbox" :style="`${collapsed?'display:none':''}`">
|
||||
<p style="margin:1pt;" class="chat-message" v-for="(msg, i) in messages" v-bind:key="`${i}-c`" :style="`color:${msg.color};background:${msg.bgcolor}${msg.bgcolor?';border-left: medium solid '+msg.color+';padding-left:2pt;':''}`">{{msg.text}}</p>
|
||||
<p 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>
|
||||
</template>
|
||||
|
||||
@ -34,9 +42,11 @@ export default {
|
||||
},
|
||||
data: () => ({
|
||||
messages: [],
|
||||
toasts: [],
|
||||
text: '',
|
||||
spectators: 0,
|
||||
commands: [{cmd:'/debug', help:'Toggles the debug mode'}],
|
||||
collapsed: false,
|
||||
}),
|
||||
computed: {
|
||||
commandSuggestion() {
|
||||
@ -52,8 +62,10 @@ export default {
|
||||
// console.log(msg)
|
||||
if ((typeof msg === "string" && msg.indexOf('_') === 0) || (msg.color != null && msg.text.indexOf('_') === 0)) {
|
||||
let t_color = null
|
||||
let bg_color = null
|
||||
if (msg.color != null) {
|
||||
t_color = msg.color
|
||||
bg_color = msg.bgcolor
|
||||
msg = msg.text
|
||||
}
|
||||
let params = msg.split('|')
|
||||
@ -75,7 +87,7 @@ export default {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
this.messages.push({text:this.$t(`chat.${type}`, params)});
|
||||
}
|
||||
@ -95,6 +107,10 @@ export default {
|
||||
} else { // a chat message
|
||||
(new Audio(message_sfx)).play();
|
||||
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");
|
||||
container.scrollTop = container.scrollHeight;
|
||||
@ -149,7 +165,7 @@ input {
|
||||
.std-text {
|
||||
color: var(--font-color);
|
||||
}
|
||||
.chat {
|
||||
.chat, .cont {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -167,7 +183,10 @@ input {
|
||||
transform: translateX(30px);
|
||||
}
|
||||
@media only screen and (min-width:1000px) {
|
||||
.chat {
|
||||
.chat-header {
|
||||
margin-left: 10pt;
|
||||
}
|
||||
.chat, .cont {
|
||||
height: 90vh;
|
||||
margin-left: 10pt;
|
||||
}
|
||||
@ -176,6 +195,12 @@ input {
|
||||
margin-right: -5pt;
|
||||
}
|
||||
}
|
||||
#toast-chatbox {
|
||||
position: fixed;
|
||||
bottom: 30pt;
|
||||
left: 0;
|
||||
background: --var(--bg-color);
|
||||
}
|
||||
@media only screen and (max-width:1000px) {
|
||||
#msg-form {
|
||||
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-else style="min-height: 19px;"> </p>
|
||||
</div>
|
||||
<transition-group name="list" tag="div" class="players-table">
|
||||
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
||||
<transition-group v-if="p.gold_nuggets && p.gold_nuggets > 0" name="list" tag="div" style="position: absolute;top: -10pt; font-size:9pt;">
|
||||
<span v-for="(n, i) in p.gold_nuggets" v-bind:key="i" :alt="i">💵️</span>
|
||||
</transition-group>
|
||||
<transition-group v-if="p.max_lives && !p.is_ghost" name="list" tag="div" class="tiny-health">
|
||||
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i">❤️</span>
|
||||
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</span>
|
||||
</transition-group>
|
||||
<div v-else-if="p.is_ghost" class="tiny-health">
|
||||
<span>👻</span>
|
||||
<div style="position:relative;">
|
||||
<div v-if="showTurnFlow" id="turn-indicator" :class="{reversed:turnReversed}"/>
|
||||
<transition-group name="list" tag="div" class="players-table">
|
||||
<Card v-if="startGameCard" key="_start_game_" :donotlocalize="true" :card="startGameCard" @click.native="startGame"/>
|
||||
<div v-for="p in playersTable" v-bind:key="p.card.name" style="position:relative;">
|
||||
<transition-group v-if="p.gold_nuggets && p.gold_nuggets > 0" name="list" tag="div" style="position: absolute;top: -10pt; font-size:9pt;">
|
||||
<span v-for="(n, i) in p.gold_nuggets" v-bind:key="i" :alt="i">💵️</span>
|
||||
</transition-group>
|
||||
<transition-group v-if="p.max_lives && !p.is_ghost" name="list" tag="div" class="tiny-health">
|
||||
<span v-for="(n, i) in p.lives" v-bind:key="i" :alt="i">❤️</span>
|
||||
<span v-for="(n, i) in (p.max_lives-p.lives)" v-bind:key="`${i}-sk`" :alt="i">💀</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>
|
||||
<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>
|
||||
</transition-group>
|
||||
</transition-group>
|
||||
</div>
|
||||
<div v-if="started">
|
||||
<deck :endTurnAction="()=>{wantsToEndTurn = true}"/>
|
||||
<player :isEndingTurn="wantsToEndTurn" :cancelEndingTurn="()=>{wantsToEndTurn = false}" :chooseCardFromPlayer="choose" :cancelChooseCardFromPlayer="()=>{hasToChoose=false}"/>
|
||||
@ -129,6 +132,9 @@ export default {
|
||||
is_competitive: false,
|
||||
disconnect_bot: false,
|
||||
debug_mode: false,
|
||||
showTurnFlow: false,
|
||||
turnReversed: false,
|
||||
turn: -1,
|
||||
}),
|
||||
sockets: {
|
||||
room(data) {
|
||||
@ -347,7 +353,18 @@ export default {
|
||||
privateRoom(old, _new) {
|
||||
if (this.isRoomOwner && old !== _new)
|
||||
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() {
|
||||
if (Vue.config.devtools)
|
||||
@ -409,6 +426,39 @@ export default {
|
||||
justify-content: space-evenly;
|
||||
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 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -115,6 +115,7 @@ export default {
|
||||
can_target_sheriff: true,
|
||||
show_role: false,
|
||||
attacker: undefined,
|
||||
attacking_card: undefined,
|
||||
notifycard: null,
|
||||
desc: '',
|
||||
scrapHand: [],
|
||||
@ -175,6 +176,7 @@ export default {
|
||||
this.sight = self.sight
|
||||
this.sight_extra = self.sight_extra
|
||||
this.attacker = self.attacker
|
||||
this.attacking_card = self.attacking_card
|
||||
this.mancato_needed = self.mancato_needed
|
||||
this.is_ghost = self.is_ghost
|
||||
if (this.pending_action == 5 && self.target_p) {
|
||||
@ -215,7 +217,8 @@ export default {
|
||||
},
|
||||
computed:{
|
||||
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() {
|
||||
return this.isEndingTurn && !this.canEndTurn && this.is_my_turn;
|
||||
|
@ -653,7 +653,7 @@
|
||||
},
|
||||
"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": {
|
||||
"name": "Josh McCloud",
|
||||
|
@ -653,7 +653,7 @@
|
||||
},
|
||||
"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": {
|
||||
"name": "Josh McCloud",
|
||||
|
@ -3516,9 +3516,10 @@ eventemitter3@^4.0.0:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
|
||||
|
||||
events@^3.0.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
||||
"events@^3.0.0":
|
||||
"integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
|
||||
"resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
||||
"version" "3.3.0"
|
||||
|
||||
eventsource@^1.0.7:
|
||||
version "1.1.1"
|
||||
|
Loading…
Reference in New Issue
Block a user