add 'on the block' indicator

- after vote, ST chooses to put onto block / empty block / no change to block
- player menu has add / remove from block
- players are automatically removed from the block when (i) they die (ii) another player is put onto block
- fixed crash on add/remove/etc player mid vote
This commit is contained in:
nicfreeman1209 2021-05-02 22:52:20 +01:00
parent 3ae36e2f0f
commit 943d228e98
8 changed files with 115 additions and 25 deletions

View File

@ -1,5 +1,7 @@
# Release Notes # Release Notes
- add 'on the block' indicator
### Version 2.12.0 ### Version 2.12.0
- tweak reference sheet to better fit screen in single column layout - tweak reference sheet to better fit screen in single column layout
- add warning icon overlay for setup roles on character assignment modal - add warning icon overlay for setup roles on character assignment modal

View File

@ -143,7 +143,7 @@
</template> </template>
</template> </template>
<template v-if="tab === 'players' && !session.isSpectator"> <template v-if="tab === 'players' && !session.isSpectator && !session.nomination">
<!-- Users --> <!-- Users -->
<li class="headline">Players</li> <li class="headline">Players</li>
<li @click="addPlayer" v-if="players.length < 20">Add<em>[A]</em></li> <li @click="addPlayer" v-if="players.length < 20">Add<em>[A]</em></li>

View File

@ -97,6 +97,11 @@
@click="updatePlayer('isVoteless', true)" @click="updatePlayer('isVoteless', true)"
title="Ghost vote" title="Ghost vote"
/> />
<font-awesome-icon
icon="crosshairs"
v-if="player.isOnBlock"
class="on-block"
/>
<div <div
class="name" class="name"
@click="isMenuOpen = !isMenuOpen" @click="isMenuOpen = !isMenuOpen"
@ -124,10 +129,11 @@
<li @click="changeName"> <li @click="changeName">
<font-awesome-icon icon="user-edit" />Rename <font-awesome-icon icon="user-edit" />Rename
</li> </li>
<li v-if="!session.nomination" @click="nominatePlayer()"> <li v-if="!session.nomination" @click="removePlayer">
<font-awesome-icon icon="hand-point-right" /> <font-awesome-icon icon="times-circle" />
Nomination Remove
</li> </li>
<template v-if="!session.nomination">
<li @click="movePlayer()"> <li @click="movePlayer()">
<font-awesome-icon icon="redo-alt" /> <font-awesome-icon icon="redo-alt" />
Move player Move player
@ -136,6 +142,15 @@
<font-awesome-icon icon="exchange-alt" /> <font-awesome-icon icon="exchange-alt" />
Swap seats Swap seats
</li> </li>
<li @click="toggleOnBlock()">
<font-awesome-icon icon="crosshairs" />
{{ player.isOnBlock ? "Take off block" : "Put on block" }}
</li>
<li @click="nominatePlayer()">
<font-awesome-icon icon="hand-point-right" />
Nomination
</li>
</template>
<li <li
@click="updatePlayer('id', '', true)" @click="updatePlayer('id', '', true)"
v-if="player.id && session.sessionId" v-if="player.id && session.sessionId"
@ -143,10 +158,6 @@
<font-awesome-icon icon="chair" /> <font-awesome-icon icon="chair" />
Empty seat Empty seat
</li> </li>
<li @click="removePlayer">
<font-awesome-icon icon="times-circle" />
Remove
</li>
</template> </template>
<li <li
@click="claimSeat" @click="claimSeat"
@ -257,6 +268,9 @@ export default {
if (this.grimoire.isPublic) { if (this.grimoire.isPublic) {
if (!this.player.isDead) { if (!this.player.isDead) {
this.updatePlayer("isDead", true); this.updatePlayer("isDead", true);
if (this.player.isOnBlock) {
this.toggleOnBlock();
}
} else if (this.player.isVoteless) { } else if (this.player.isVoteless) {
this.updatePlayer("isVoteless", false); this.updatePlayer("isVoteless", false);
this.updatePlayer("isDead", false); this.updatePlayer("isDead", false);
@ -265,6 +279,9 @@ export default {
} }
} else { } else {
this.updatePlayer("isDead", !this.player.isDead); this.updatePlayer("isDead", !this.player.isDead);
if (this.player.isOnBlock) {
this.toggleOnBlock();
}
if (this.player.isVoteless) { if (this.player.isVoteless) {
this.updatePlayer("isVoteless", false); this.updatePlayer("isVoteless", false);
} }
@ -319,6 +336,10 @@ export default {
this.isMenuOpen = false; this.isMenuOpen = false;
this.$emit("trigger", ["claimSeat"]); this.$emit("trigger", ["claimSeat"]);
}, },
toggleOnBlock() {
this.isMenuOpen = false;
this.$emit("trigger", ["toggleOnBlock"]);
},
/** /**
* Allow the ST to override a locked vote. * Allow the ST to override a locked vote.
*/ */
@ -578,10 +599,7 @@ li.move:not(.from) .player .overlay svg.move {
} }
/****** Vote icon ********/ /****** Vote icon ********/
.player .has-vote { .player .has-vote .on-block {
position: absolute;
right: 2px;
margin-top: -15%;
color: #fff; color: #fff;
filter: drop-shadow(0 0 3px black); filter: drop-shadow(0 0 3px black);
transition: opacity 250ms; transition: opacity 250ms;
@ -593,6 +611,19 @@ li.move:not(.from) .player .overlay svg.move {
} }
} }
.has-vote {
position: absolute;
margin-top: -15%;
right: 2px;
}
.on-block {
position: absolute;
margin-top: -15%;
left: 3px;
color: darkred;
}
/****** Session seat glow *****/ /****** Session seat glow *****/
@mixin glow($name, $color) { @mixin glow($name, $color) {
@keyframes #{$name}-glow { @keyframes #{$name}-glow {

View File

@ -201,6 +201,13 @@ export default {
this.move = -1; this.move = -1;
this.swap = -1; this.swap = -1;
this.nominate = -1; this.nominate = -1;
},
toggleOnBlock(playerIndex) {
if (this.players[playerIndex].isOnBlock) {
this.$store.commit("players/setOnBlock", -1);
} else {
this.$store.commit("players/setOnBlock", playerIndex);
}
} }
} }
}; };

View File

@ -62,6 +62,19 @@
<div class="button demon" @click="finish">Close</div> <div class="button demon" @click="finish">Close</div>
</div> </div>
</template> </template>
<template v-if="!session.isSpectator">
<div
class="button-group"
v-if="session.lockedVote && !session.isVoteInProgress"
>
<div class="button demon" @click="setOnBlock">
Put on block
</div>
<div class="button demon" @click="emptyBlock">
Empty block
</div>
</div>
</template>
<template v-else-if="canVote"> <template v-else-if="canVote">
<div v-if="!session.isVoteInProgress"> <div v-if="!session.isVoteInProgress">
{{ session.votingSpeed / 1000 }} seconds between votes {{ session.votingSpeed / 1000 }} seconds between votes
@ -235,6 +248,14 @@ export default {
if (speed > 0) { if (speed > 0) {
this.$store.commit("session/setVotingSpeed", speed); this.$store.commit("session/setVotingSpeed", speed);
} }
},
setOnBlock() {
this.$store.commit("players/setOnBlock", this.session.nomination[1]);
this.finish();
},
emptyBlock() {
this.$store.commit("players/setOnBlock", -1);
this.finish();
} }
} }
}; };

View File

@ -16,6 +16,7 @@ const faIcons = [
"CloudMoon", "CloudMoon",
"Cog", "Cog",
"Copy", "Copy",
"Crosshairs",
"Dice", "Dice",
"Dragon", "Dragon",
"ExchangeAlt", "ExchangeAlt",

View File

@ -5,6 +5,7 @@ const NEWPLAYER = {
reminders: [], reminders: [],
isVoteless: false, isVoteless: false,
isDead: false, isDead: false,
isOnBlock: false,
pronouns: "" pronouns: ""
}; };
@ -136,6 +137,14 @@ const mutations = {
move(state, [from, to]) { move(state, [from, to]) {
state.players.splice(to, 0, state.players.splice(from, 1)[0]); state.players.splice(to, 0, state.players.splice(from, 1)[0]);
}, },
setOnBlock(state, playerIndex) {
state.players.forEach(player => {
player.isOnBlock = false;
});
if (playerIndex >= 0) {
state.players[playerIndex].isOnBlock = true;
}
},
setBluff(state, { index, role } = {}) { setBluff(state, { index, role } = {}) {
if (index !== undefined) { if (index !== undefined) {
state.bluffs.splice(index, 1, role); state.bluffs.splice(index, 1, role);

View File

@ -168,6 +168,10 @@ class LiveSession {
if (!this._isSpectator) return; if (!this._isSpectator) return;
this._store.commit("players/remove", params); this._store.commit("players/remove", params);
break; break;
case "onBlock":
if (!this._isSpectator) return;
this._store.commit("players/setOnBlock", params);
break;
case "isNight": case "isNight":
if (!this._isSpectator) return; if (!this._isSpectator) return;
this._store.commit("toggleNight", params); this._store.commit("toggleNight", params);
@ -251,6 +255,7 @@ class LiveSession {
id: player.id, id: player.id,
isDead: player.isDead, isDead: player.isDead,
isVoteless: player.isVoteless, isVoteless: player.isVoteless,
isOnBlock: player.isOnBlock,
pronouns: player.pronouns, pronouns: player.pronouns,
...(player.role && player.role.team === "traveler" ...(player.role && player.role.team === "traveler"
? { roleId: player.role.id } ? { roleId: player.role.id }
@ -312,12 +317,14 @@ class LiveSession {
const player = players[x]; const player = players[x];
const { roleId } = state; const { roleId } = state;
// update relevant properties // update relevant properties
["name", "id", "isDead", "isVoteless", "pronouns"].forEach(property => { ["name", "id", "isDead", "isVoteless", "isOnBlock", "pronouns"].forEach(
property => {
const value = state[property]; const value = state[property];
if (player[property] !== value) { if (player[property] !== value) {
this._store.commit("players/update", { player, property, value }); this._store.commit("players/update", { player, property, value });
} }
}); }
);
// roles are special, because of travelers // roles are special, because of travelers
if (roleId && player.role.id !== roleId) { if (roleId && player.role.id !== roleId) {
const role = const role =
@ -697,6 +704,15 @@ class LiveSession {
} }
} }
/**
* Set which player is on the block. ST only
* @param id, player id or -1 for empty
*/
setOnBlock(playerIndex) {
if (this._isSpectator) return;
this._send("onBlock", playerIndex);
}
/** /**
* Clear the vote history for everyone. ST only * Clear the vote history for everyone. ST only
*/ */
@ -849,6 +865,9 @@ export default store => {
case "players/setFabled": case "players/setFabled":
session.sendFabled(); session.sendFabled();
break; break;
case "players/setOnBlock":
session.setOnBlock(payload);
break;
case "players/swap": case "players/swap":
session.swapPlayer(payload); session.swapPlayer(payload);
break; break;