added vote buttons

This commit is contained in:
Steffen 2020-06-03 22:25:23 +02:00
parent 952cb1f0a5
commit 31b4cd5183
No known key found for this signature in database
GPG Key ID: 764D74E98267DFC6
6 changed files with 144 additions and 19 deletions

View File

@ -7,7 +7,8 @@
{ {
dead: player.isDead, dead: player.isDead,
'no-vote': player.isVoteless, 'no-vote': player.isVoteless,
you: player.id === session.playerId you: player.id === session.playerId,
'voted-yes': session.votes[index]
}, },
player.role.team player.role.team
]" ]"
@ -40,6 +41,12 @@
/> />
<!-- Overlay icons --> <!-- Overlay icons -->
<font-awesome-icon
icon="skull"
class="vote"
title="Vote"
@click="vote()"
/>
<font-awesome-icon <font-awesome-icon
icon="times-circle" icon="times-circle"
class="cancel" class="cancel"
@ -71,7 +78,7 @@
<!-- Ghost vote icon --> <!-- Ghost vote icon -->
<font-awesome-icon <font-awesome-icon
icon="vote-yea" icon="vote-yea"
class="vote" class="has-vote"
v-if="player.isDead && !player.isVoteless" v-if="player.isDead && !player.isVoteless"
@click="updatePlayer('isVoteless', true)" @click="updatePlayer('isVoteless', true)"
title="Ghost vote" title="Ghost vote"
@ -163,6 +170,9 @@ export default {
} }
}, },
computed: { computed: {
index: function() {
return this.$store.state.players.players.indexOf(this.player);
},
...mapState(["grimoire", "session"]), ...mapState(["grimoire", "session"]),
...mapGetters({ nightOrder: "players/nightOrder" }) ...mapGetters({ nightOrder: "players/nightOrder" })
}, },
@ -235,6 +245,10 @@ export default {
claimSeat() { claimSeat() {
this.isMenuOpen = false; this.isMenuOpen = false;
this.$emit("trigger", ["claimSeat"]); this.$emit("trigger", ["claimSeat"]);
},
vote() {
if (this.player.id !== this.session.playerId) return;
this.$store.commit("session/vote", [this.index]);
} }
} }
}; };
@ -402,6 +416,7 @@ export default {
&.swap, &.swap,
&.move, &.move,
&.nominate, &.nominate,
&.vote,
&.cancel { &.cancel {
top: 9%; top: 9%;
left: 20%; left: 20%;
@ -417,6 +432,17 @@ export default {
} }
} }
#townsquare.vote .player.voted-yes > svg.vote {
color: $demon;
opacity: 0.5;
transform: scale(1);
}
#townsquare.vote .player.you.voted-yes > svg.vote {
opacity: 1;
transform: scale(1);
}
li.from:not(.nominate) .player > svg.cancel { li.from:not(.nominate) .player > svg.cancel {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -432,7 +458,7 @@ li.move:not(.from) .player > svg.move {
} }
/****** Vote icon ********/ /****** Vote icon ********/
.player .vote { .player .has-vote {
position: absolute; position: absolute;
right: 2px; right: 2px;
bottom: 45px; bottom: 45px;

View File

@ -4,7 +4,8 @@
class="square" class="square"
v-bind:class="{ v-bind:class="{
public: grimoire.isPublic, public: grimoire.isPublic,
spectator: session.isSpectator spectator: session.isSpectator,
vote: session.nomination
}" }"
v-bind:style="{ zoom: grimoire.zoom }" v-bind:style="{ zoom: grimoire.zoom }"
> >

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="vote"> <div id="vote">
<div class="arrows"> <div class="arrows">
<span class="nominee" :style="nomineeStyle"></span> <span class="nominee" :style="nomineeStyle"></span>
<span class="nominator" :style="nominatorStyle"></span> <span class="nominator" :style="nominatorStyle"></span>
@ -10,17 +10,29 @@
>! >!
<br /> <br />
<template v-if="nominee.role.team !== 'traveler'"> <template v-if="nominee.role.team !== 'traveler'">
<em class="blue">{{ Math.ceil(alive / 2) }} votes</em> required for an <em class="blue">{{ Math.ceil(alive / 2) }} votes</em> required to
<em>execution</em> <em>execute</em>.
</template> </template>
<template v-else> <template v-else>
<em>{{ Math.ceil(players.length / 2) }} votes</em> required for an <em>{{ Math.ceil(players.length / 2) }} votes</em> required to
<em>exile</em> <em>exile</em>.
</template> </template>
<div class="button-group"> <div class="button-group" v-if="!session.isSpectator">
<div class="button">Start Vote</div> <div class="button">Start Vote</div>
<div class="button" @click="finish">Finish Vote</div> <div class="button" @click="finish">Finish Vote</div>
</div> </div>
<div
class="button-group"
v-else-if="
player && (!player.isVoteless || nominee.role.team === 'traveler')
"
>
<div class="button vote-no" @click="vote(false)">Vote NO</div>
<div class="button vote-yes" @click="vote(true)">Vote YES</div>
</div>
<div v-else-if="!player">
Please claim a seat to vote.
</div>
</div> </div>
</div> </div>
</template> </template>
@ -54,6 +66,10 @@ export default {
transform: `rotate(${Math.round((nomination / players) * 360)}deg)` transform: `rotate(${Math.round((nomination / players) * 360)}deg)`
}; };
}, },
player: function() {
const id = this.$store.state.session.playerId;
return this.$store.state.players.players.find(p => p.id === id);
},
...mapState("players", ["players"]), ...mapState("players", ["players"]),
...mapState(["session"]), ...mapState(["session"]),
...mapGetters({ alive: "players/alive" }) ...mapGetters({ alive: "players/alive" })
@ -61,6 +77,12 @@ export default {
methods: { methods: {
finish() { finish() {
this.$store.commit("session/nomination", false); this.$store.commit("session/nomination", false);
},
vote(vote) {
const index = this.players.findIndex(p => p.id === this.session.playerId);
if (index >= 0 && !!this.session.votes[index] !== vote) {
this.$store.commit("session/vote", [index, vote]);
}
} }
} }
}; };
@ -69,7 +91,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../vars.scss"; @import "../vars.scss";
.vote { #vote {
position: absolute; position: absolute;
width: 20%; width: 20%;
z-index: 20; z-index: 20;
@ -151,4 +173,30 @@ export default {
animation: arrow-cw 1s ease-out; animation: arrow-cw 1s ease-out;
} }
} }
.button.vote-no {
background: radial-gradient(
at 0 -15%,
rgba(255, 255, 255, 0.07) 70%,
rgba(255, 255, 255, 0) 71%
)
0 0/80% 90% no-repeat content-box,
linear-gradient(#0031ad, rgba(5, 0, 0, 0.22)) content-box,
linear-gradient(#292929, #001142) border-box;
box-shadow: inset 0 1px 1px #002c9c, 0 0 10px #000;
&:hover {
color: #008cf7;
}
}
.button.vote-yes {
background: radial-gradient(
at 0 -15%,
rgba(255, 255, 255, 0.07) 70%,
rgba(255, 255, 255, 0) 71%
)
0 0/80% 90% no-repeat content-box,
linear-gradient(#ad0000, rgba(5, 0, 0, 0.22)) content-box,
linear-gradient(#292929, #420000) border-box;
box-shadow: inset 0 1px 1px #9c0000, 0 0 10px #000;
}
</style> </style>

View File

@ -10,6 +10,7 @@ const faIcons = [
"BroadcastTower", "BroadcastTower",
"Camera", "Camera",
"CheckSquare", "CheckSquare",
"Skull",
"Cog", "Cog",
"Copy", "Copy",
"ExchangeAlt", "ExchangeAlt",

View File

@ -4,7 +4,9 @@ const state = () => ({
playerCount: 0, playerCount: 0,
playerId: "", playerId: "",
claimedSeat: -1, claimedSeat: -1,
nomination: false nomination: false,
votes: [],
lockedVote: -1
}); });
const getters = {}; const getters = {};
@ -29,6 +31,11 @@ const mutations = {
}, },
nomination(state, nomination) { nomination(state, nomination) {
state.nomination = nomination; state.nomination = nomination;
state.votes = [];
},
vote(state, [index, vote]) {
state.votes = [...state.votes];
state.votes[index] = vote === undefined ? !state.votes[index] : vote;
} }
}; };

View File

@ -99,6 +99,12 @@ class LiveSession {
case "ping": case "ping":
this._handlePing(params); this._handlePing(params);
break; break;
case "nomination":
this._store.commit("session/nomination", params);
break;
case "vote":
this._store.commit("session/vote", params);
break;
case "bye": case "bye":
this._handleBye(params); this._handleBye(params);
break; break;
@ -291,7 +297,7 @@ class LiveSession {
} }
// remove claimed seats from players that are no longer connected // remove claimed seats from players that are no longer connected
this._store.state.players.players.forEach(player => { this._store.state.players.players.forEach(player => {
if (player.id && !this._players[player.id]) { if (!this._isSpectator && player.id && !this._players[player.id]) {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "id", property: "id",
@ -336,20 +342,50 @@ class LiveSession {
* @private * @private
*/ */
_updateSeat([index, value]) { _updateSeat([index, value]) {
if (this._isSpectator) return;
const property = "id"; const property = "id";
const players = this._store.state.players.players;
// remove previous seat // remove previous seat
const player = this._store.state.players.players.find( const oldIndex = players.findIndex(({ id }) => id === value);
({ id }) => id === value if (oldIndex >= 0 && oldIndex !== index) {
); this._store.commit("players/update", {
if (player) { player: players[oldIndex],
this._store.commit("players/update", { player, property, value: "" }); property,
value: ""
});
} }
// add playerId to new seat // add playerId to new seat
if (index >= 0) { if (index >= 0) {
const player = this._store.state.players.players[index]; const player = players[index];
if (!player) return; if (!player) return;
this._store.commit("players/update", { player, property, value }); this._store.commit("players/update", { player, property, value });
} }
// update player session list as if this was a ping
this._handlePing([true, value]);
}
/**
* A player nomination.
* @param nomination [nominator, nominee]
*/
nomination(nomination) {
if (this._isSpectator) return;
const players = this._store.state.players.players;
if (
!nomination ||
(players.length > nomination[0] && players.length > nomination[1])
) {
this._send("nomination", nomination);
}
}
/**
* Send a vote.
* @param index
*/
vote([index]) {
if (!this._isSpectator) return;
this._send("vote", [index, this._store.state.session.votes[index]]);
} }
} }
@ -371,6 +407,12 @@ module.exports = store => {
case "session/claimSeat": case "session/claimSeat":
session.claimSeat(payload); session.claimSeat(payload);
break; break;
case "session/nomination":
session.nomination(payload);
break;
case "session/vote":
session.vote(payload);
break;
case "players/set": case "players/set":
case "players/swap": case "players/swap":
case "players/move": case "players/move":