Merge branch 'main' into dev

This commit is contained in:
Giulio 2023-03-09 10:13:06 +01:00
commit e69f214959
13 changed files with 171 additions and 45 deletions

View File

@ -82,13 +82,17 @@ def bang_handler(func):
logging.exception(e)
print(traceback.format_exc())
send_to_debug(traceback.format_exc())
save_lock = False
finally:
save_lock = False
return wrapper_func
def count_bots_in_game(game):
return sum(1 for p in game.players if p.is_bot)
def advertise_lobbies():
open_lobbies = [g for g in games.values() if 0 < len(g.players) < 10 and not g.is_hidden][-10:]
sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in open_lobbies if not g.started])
sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'locked': g.password != ''} for g in open_lobbies if g.started])
sio.emit('lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'bots': count_bots_in_game(g), 'locked': g.password != '', 'expansions': g.expansions} for g in open_lobbies if not g.started])
sio.emit('spectate_lobbies', room='lobby', data=[{'name': g.name, 'players': len(g.players), 'bots': count_bots_in_game(g), 'locked': g.password != '', 'expansions': g.expansions} for g in open_lobbies if g.started])
Metrics.send_metric('lobbies', points=[sum(not g.is_replay for g in games.values())])
Metrics.send_metric('online_players', points=[online_players])
@ -268,6 +272,7 @@ def private(sid):
def toggle_expansion(sid, expansion_name):
g = sio.get_session(sid).game
g.toggle_expansion(expansion_name)
advertise_lobbies()
@sio.event
@bang_handler
@ -476,6 +481,7 @@ def chat_message(sid, msg, pl=None):
while any((p for p in ses.game.players if p.name == bot.name)):
bot = Player(f'AI_{random.randint(0,10)}', 'bot', bot=True)
ses.game.add_player(bot)
advertise_lobbies()
sio.start_background_task(bot.bot_spin)
return
if '/replay' in msg and not '/replayspeed' in msg and not '/replaypov' in msg:
@ -515,6 +521,7 @@ def chat_message(sid, msg, pl=None):
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()
advertise_lobbies()
return
elif '/togglecomp' in msg and ses.game:
ses.game.toggle_competitive()

View File

@ -13,7 +13,7 @@
</li>
</ul>
</div>
<help v-if="showHelp"/>
<help :inGame="true" v-if="showHelp"/>
<div style="position:fixed;bottom:4pt;right:4pt;display:flex;z-index:10">
<input v-if="connect_dev" type=button class="btn" style="min-width:28pt;cursor:pointer;" @click="resetConnection" :value="'💚'+connect_dev" />
<input type=button class="btn" style="min-width:28pt;cursor:pointer;" @click="()=>{sending_report = true}" :value=" $t('report') " />

View File

@ -137,6 +137,53 @@ export default {
);
border: 2pt solid rgb(50 122 172);
}
.card.back.high-noon{
color:var(--bg-color);
background: repeating-linear-gradient(
45deg,
rgb(198 78 45),
rgb(198 78 45) 5px,
rgb(178 58 25) 5px,
rgb(178 58 25) 10px
);
border: 2pt solid rgb(198 78 45);
}
.card.back.the-valley-of-shadows{
color:var(--bg-color);
background: repeating-linear-gradient(
45deg,
rgb(98 88 85),
rgb(98 88 85) 5px,
rgb(78 68 65) 5px,
rgb(78 68 65) 10px
);
box-shadow:
0 0 0 3pt rgb(98 88 85),
0 0 0 6pt var(--bg-color),
0 0 5pt 6pt #aaa;
}
.card.back.cant-play {
transform: scale(0.9);
filter: grayscale(1);
opacity: 0.5;
}
.card.back.cant-play:hover {
transform: scale(0.95);
filter: grayscale(0.6);
opacity: 0.8;
}
.beta::after {
content: "Beta";
position: absolute;
bottom: -12pt;
right: -12pt;
background: red;
font-size: 10pt;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
padding: 4pt;
border-radius: 12pt;
}
.avatar {
position: absolute;
width: 36pt;
@ -166,6 +213,9 @@ export default {
top: -10pt;
font-size: 11pt;
}
.card.back h4{
font-size: 12pt;
}
.card .emoji {
position: absolute;
text-align: center;
@ -180,7 +230,7 @@ export default {
.card.must-be-used {
filter: drop-shadow(0 0 5px red);
}
.fistful-of-cards .emoji, .high-noon .emoji{
.fistful-of-cards .emoji, .high-noon .emoji, .exp-pack .emoji{
top:auto !important;
bottom:15% !important;
}

View File

@ -1,11 +1,10 @@
<template>
<div >
<div class="deck">
<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 class="deck" :style="`position:relative;${goldRushShopOpen?'border: 2px dashed #6a6a6a42;border-radius:8pt':''}`" v-if="goldRushCards.length > 0" >
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[0])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(95deg) translate(30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 0" :key="goldRushCards[0].name" :card="goldRushCards[0]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[0].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(0)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[1])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(90deg) translate(0, -40px) scale(0.6)`" v-if="goldRushCards.length > 1" :key="goldRushCards[1].name" :card="goldRushCards[1]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[1].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(1)}"/>
<card @pointerenter.native="()=>{setGoldRushDesc(goldRushCards[2])}" @pointerleave.native="goldRushDesc=null" :style="goldRushShopOpen?``:`position:absolute; top:0; right:0; transform: rotate(85deg) translate(-30px, -40px) scale(0.6)`" v-if="goldRushCards.length > 2" :key="goldRushCards[2].name" :card="goldRushCards[2]" :class="{'shop-open':goldRushShopOpen, 'cant-play': pending_action !==2 || gold_nuggets < goldRushCards[2].number - gold_rush_discount}" @click.native="() => {buy_gold_rush_card(2)}"/>
<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;"/>
@ -17,16 +16,19 @@
<div class="card fistful-of-cards" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="eventCard" :key="eventCard.name" :class="eventClasses" @click.native="event"/>
</div>
<div style="position:relative" id="actual-deck">
<div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/>
<div class="card back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="card" :donotlocalize="true" :class="{back:true, pick:pending_action === 0, draw:pending_action === 1}" @click.native="action"/>
</div>
<div style="position:relative;" id="actual-scrap">
<card v-if="previousScrap" :card="previousScrap" style="top: 1.5pt;right: -1.5pt;"/>
<card v-else :card="card" class="back" style="opacity:0"/>
<card v-if="lastScrap" :card="lastScrap" :key="lastScrap.name+lastScrap.number" class="last-scrap" @click.native="action('scrap')"
@pointerenter.native="setdesc" @pointerleave.native="desc=''" />
<div style="position:relative" class="deck">
<div style="position:relative" id="actual-deck">
<div class="card back" style="position:absolute; bottom:-3pt;right:-3pt;"/>
<div class="card back" style="position:absolute; bottom:-1.5pt;right:-1.5pt;"/>
<card :card="card" :donotlocalize="true" :class="{back:true, pick:pending_action === 0, draw:pending_action === 1}" @click.native="action"/>
</div>
<div style="position:relative;" id="actual-scrap">
<card v-if="previousScrap" :card="previousScrap" style="top: 1.5pt;right: -1.5pt;"/>
<card v-else :card="card" class="back" style="opacity:0"/>
<card v-if="lastScrap" :card="lastScrap" :key="lastScrap.name+lastScrap.number" class="last-scrap" @click.native="action('scrap')"
@pointerenter.native="setdesc" @pointerleave.native="desc=''" />
</div>
<card v-if="endTurnAction && isPlaying" :donotlocalize="true" v-show="pending_action == 2" :card="endTurnCard" class="end-turn" @click.native="endTurnAction"/>
</div>
</div>
<transition name="list">

View File

@ -1,5 +1,6 @@
<template>
<div>
<a v-if="!inGame" href=".."><p>Home</p></a>
<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>
@ -127,6 +128,9 @@ export default {
components: {
Card,
},
props: {
inGame: Boolean
},
data:()=>({
cardBack: {
name: 'PewPew!',

View File

@ -20,24 +20,6 @@
<!-- :style="p.style"/> -->
<!-- </div> -->
<!-- </div> -->
<div v-if="!started">
<h3>{{$t("expansions")}}</h3>
<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}}
<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>
</div>
<h3>{{$t('mods')}}</h3>
<PrettyCheck @click.native="toggleCompetitive" :disabled="!isRoomOwner" v-model="is_competitive" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('mod_comp')}}</PrettyCheck>
<h3>{{$t('bots')}}</h3>
<input type="button" class="btn" :value="$t('add_bot')" :disabled="!isRoomOwner || players.length > 7" @click="(e)=>{this.$socket.emit('chat_message', '/addbot'); e.preventDefault()}"/>
<input type="button" class="btn" style="margin-left: 10pt;" :value="$t('remove_bot')" :disabled="!isRoomOwner || !isThereAnyBot" @click="(e)=>{this.$socket.emit('chat_message', '/removebot'); e.preventDefault()}"/>
<!-- <br> -->
<!-- <PrettyCheck @click.native="toggleReplaceWithBot" :disabled="!isRoomOwner" v-model="disconnect_bot" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('disconnect_bot')}}</PrettyCheck> -->
<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>
<div style="position:relative;">
<div v-if="showTurnFlow" id="turn-indicator" :class="{reversed:turnReversed}"/>
<transition-group name="list" tag="div" class="players-table">
@ -74,6 +56,22 @@
<Card v-if="startGameCard" key="_shuffle_players_" :donotlocalize="true" :card="shufflePlayersCard" @click.native="shufflePlayers" class="fistful-of-cards"/>
</transition-group>
</div>
<div v-if="!started">
<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>
<h3>{{$t("expansions")}}</h3>
<div class="players-table" style="justify-content: flex-start;">
<card v-for="ex in expansionsStatus" v-bind:key="ex.id" :card="ex.card" :class="{'cant-play':!ex.enabled, 'beta':ex.is_beta, ...ex.card.classes}" :donotlocalize="true" @click.native="toggleExpansions(ex.id)"/>
</div>
<p v-if="isRoomOwner">{{$t('click_to_toggle')}}</p>
<h3>{{$t('mods')}}</h3>
<PrettyCheck @click.native="toggleCompetitive" :disabled="!isRoomOwner" v-model="is_competitive" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('mod_comp')}}</PrettyCheck>
<h3>{{$t('bots')}}</h3>
<input type="button" class="btn" :value="$t('add_bot')" :disabled="!isRoomOwner || players.length > 7" @click="(e)=>{this.$socket.emit('chat_message', '/addbot'); e.preventDefault()}"/>
<input type="button" class="btn" style="margin-left: 10pt;" :value="$t('remove_bot')" :disabled="!isRoomOwner || !isThereAnyBot" @click="(e)=>{this.$socket.emit('chat_message', '/removebot'); e.preventDefault()}"/>
<!-- <br> -->
<!-- <PrettyCheck @click.native="toggleReplaceWithBot" :disabled="!isRoomOwner" v-model="disconnect_bot" class="p-switch p-fill" style="margin-top:5px; margin-bottom:3px;">{{$t('disconnect_bot')}}</PrettyCheck> -->
</div>
<AnimatedCard v-for="c in cardsToAnimate" v-bind:key="c.key" :card="c.card" :startPosition="c.startPosition" :midPosition="c.midPosition" :endPosition="c.endPosition"/>
<div v-if="started">
<deck :endTurnAction="()=>{wantsToEndTurn = true}"/>
@ -113,6 +111,8 @@ import FullScreenInput from './FullScreenInput.vue'
import Status from './Status.vue'
import DeadRoleNotification from './DeadRoleNotification.vue'
import AnimatedCard from './AnimatedCard.vue'
import { emojiMap } from '@/utils/emoji-map.js'
import { expansionsMap } from '@/utils/expansions-map.js'
const cumulativeOffset = function(element) {
var top = 0, left = 0;
@ -140,7 +140,8 @@ export default {
FullScreenInput,
Status,
DeadRoleNotification,
AnimatedCard
AnimatedCard,
Card
},
data: () => ({
username: '',
@ -313,7 +314,9 @@ export default {
id: x,
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,
emoji: emojiMap[x],
card: this.getExpansionCard(x),
}
})
},
@ -366,6 +369,15 @@ export default {
}
},
methods: {
getExpansionCard(id) {
let ex = expansionsMap[id]
ex.classes = {
back: true,
'exp-pack': true
}
ex.classes[ex.expansion] = true
return ex
},
is_toggled_expansion(ex) {
if (Vue.config.devtools)
console.log(ex+' '+ this.expansions+ (this.expansions.indexOf(ex) !== -1))

View File

@ -15,10 +15,10 @@
<div v-if="!didSetUsername">
<p id="choose_username">{{$t("choose_username")}}</p>
<form @submit="setUsername" class="form" style="display:flex">
<input id="username" v-model="username" />
<input id="_username" v-model="username" />
<input type="submit" class="btn" :value="$t('submit')"/>
<input type="button" class="btn" @click="discordLogin" value="Login with Discord"/>
</form>
<a class="btn" :href="redirectUrl">Login with Discord</a>
<p v-if="onlinePlayers > 0">{{$t("online_players")}}{{onlinePlayers}}</p>
</div>
<div v-else>
@ -44,7 +44,7 @@
</div>
</div>
</div>
<label for="username" style="opacity:0">Username</label>
<label for="_username" style="opacity:0">Username</label>
<label for="lobbyname" style="opacity:0">Lobby Name</label>
<div>
Still new to the game? Read the rules <a href="./help">here</a> or press the question mark in the bottom right corner anytime during your matches.
@ -58,6 +58,7 @@ import Card from '@/components/Card.vue'
import TinyHand from '@/components/TinyHand.vue'
// import Lobby from './components/Lobby.vue'
import { datadogRum } from '@datadog/browser-rum';
import { emojiMap } from '@/utils/emoji-map.js'
export default {
name: 'App',
@ -134,11 +135,15 @@ export default {
e.preventDefault();
}
},
discordLogin() {
window.location = this.redirectUrl;
},
getLobbyCard(lobby) {
return {
name: lobby.name,
icon: "💥",
number: `${lobby.players}🤠 ${lobby.locked?'🔐':''}`,
number: `${lobby.players-lobby.bots}🤠${lobby.bots > 0 ? ' '+lobby.bots+'🤖':''} ${lobby.locked?'🔐':''}`,
alt_text: lobby.expansions?.map(e => emojiMap[e]).join(''),
is_equipment: true,
}
},
@ -146,7 +151,8 @@ export default {
return {
name: lobby.name,
icon: "👁️",
number: `${lobby.players}🤠 ${lobby.locked?'🔐':''}`,
number: `${lobby.players-lobby.bots}🤠${lobby.bots > 0 ? ' '+lobby.bots+'🤖':''} ${lobby.locked?'🔐':''}`,
alt_text: lobby.expansions?.map(e => emojiMap[e]).join(''),
usable_next_turn: true,
}
},

View File

@ -26,6 +26,7 @@
"end_turn": "Konec tahu!",
"start_game": "Začít!",
"expansions": "Rozšíření",
"click_to_toggle": "Klikněte pro přepnutí",
"details": "Podrobnosti",
"ok": "OK",
"you": "TY",

View File

@ -26,6 +26,7 @@
"end_turn": "End Turn!",
"start_game": "Start!",
"expansions": "Expansions",
"click_to_toggle": "Click to toggle",
"details": "Details",
"ok": "OK",
"you": "YOU",

View File

@ -26,6 +26,7 @@
"end_turn": "Termina turno!",
"start_game": "Avvia!",
"expansions": "Espansioni",
"click_to_toggle": "Clicca per attivare/disattivare",
"details": "Dettagli",
"ok": "OK",
"you": "TU",

View File

@ -1,5 +1,8 @@
import Vue from 'vue'
import App from './App.vue'
import { registerSW } from 'virtual:pwa-register'
registerSW({ immediate: true })
Vue.config.productionTip = false
import VueSocketIO from 'bang-vue-socket.io'

View File

@ -0,0 +1,7 @@
export const emojiMap = {
'dodge_city': '🐄',
'gold_rush': '🤑',
'high_noon': '🔥',
'fistful_of_cards': '🎴',
'the_valley_of_shadows': '👻',
}

View File

@ -0,0 +1,32 @@
export const expansionsMap = {
'dodge_city' : {
name: 'Dodge City',
icon: '🐄',
back: true,
expansion: 'dodge-city',
},
'fistful_of_cards' : {
name: 'Fistful of Cards',
icon: '🎴',
back: true,
expansion: 'fistful-of-cards',
},
'high_noon': {
name: 'High Noon',
icon: '🔥',
back: true,
expansion: 'high-noon',
},
'gold_rush': {
name: 'Gold Rush',
icon: '🤑',
back: true,
expansion: 'gold-rush',
},
'the_valley_of_shadows': {
name: 'The Valley of Shadows',
icon: '👻',
back: true,
expansion: 'the-valley-of-shadows',
}
}