diff --git a/src/components/Player.vue b/src/components/Player.vue
index 6d9f0f6..0773569 100644
--- a/src/components/Player.vue
+++ b/src/components/Player.vue
@@ -3,11 +3,14 @@
+
@@ -198,14 +213,18 @@ export default {
},
swapPlayer(player) {
this.isMenuOpen = false;
- this.$emit("swap-player", player);
+ this.$emit("trigger", ["swapPlayer", player]);
},
movePlayer(player) {
this.isMenuOpen = false;
- this.$emit("move-player", player);
+ this.$emit("trigger", ["movePlayer", player]);
},
cancel() {
- this.$emit("cancel");
+ this.$emit("trigger", ["cancel"]);
+ },
+ claimSeat() {
+ this.isMenuOpen = false;
+ this.$emit("trigger", ["claimSeat"]);
}
}
};
@@ -407,7 +426,6 @@ li.move:not(.from) .player > svg.move {
bottom: 45px;
color: #fff;
filter: drop-shadow(0 0 3px black);
- cursor: pointer;
transition: opacity 250ms;
#townsquare.public & {
@@ -416,6 +434,50 @@ li.move:not(.from) .player > svg.move {
}
}
+@mixin glow($name, $color) {
+ @keyframes #{$name}-glow {
+ 0% {
+ box-shadow: 0 0 rgba($color, 1);
+ border-color: $color;
+ }
+ 50% {
+ border-color: black;
+ }
+ 100% {
+ box-shadow: 0 0 20px 16px transparent;
+ border-color: $color;
+ }
+ }
+
+ .player.you.#{$name} .token {
+ animation: #{$name}-glow 2s ease-in-out infinite;
+ }
+}
+
+@include glow("townsfolk", $townsfolk);
+@include glow("outsider", $outsider);
+@include glow("demon", $demon);
+@include glow("minion", $minion);
+@include glow("traveler", $traveler);
+
+.player.you .token {
+ animation: townsfolk-glow 2s ease-in-out infinite;
+}
+
+/****** Seat icon ********/
+.player .seat {
+ position: absolute;
+ left: 2px;
+ bottom: 45px;
+ color: #fff;
+ filter: drop-shadow(0 0 3px black);
+ cursor: default;
+}
+
+.player.you .seat {
+ color: $townsfolk;
+}
+
/***** Player name *****/
.player > .name {
font-size: 120%;
diff --git a/src/components/TownSquare.vue b/src/components/TownSquare.vue
index 1d75313..d2dbcb4 100644
--- a/src/components/TownSquare.vue
+++ b/src/components/TownSquare.vue
@@ -13,13 +13,8 @@
v-for="(player, index) in players"
:key="index"
:player="player"
- @add-reminder="openReminderModal(index)"
- @set-role="openRoleModal(index)"
- @remove-player="removePlayer(index)"
- @cancel="cancel(index)"
- @swap-player="swapPlayer(index, $event)"
- @move-player="movePlayer(index, $event)"
@screenshot="$emit('screenshot', $event)"
+ @trigger="handleTrigger(index, $event)"
v-bind:class="{
from: Math.max(swap, move, nominate) === index,
swap: swap > -1,
@@ -80,6 +75,19 @@ export default {
const { width, height, x, y } = this.$refs.bluffs.getBoundingClientRect();
this.$emit("screenshot", { width, height, x, y });
},
+ handleTrigger(playerIndex, [method, params]) {
+ if (typeof this[method] === "function") {
+ this[method](playerIndex, params);
+ }
+ },
+ claimSeat(playerIndex) {
+ if (!this.session.isSpectator) return;
+ if (this.session.playerId === this.players[playerIndex].id) {
+ this.$store.commit("session/claimSeat", -1);
+ } else {
+ this.$store.commit("session/claimSeat", playerIndex);
+ }
+ },
openReminderModal(playerIndex) {
this.selectedPlayer = playerIndex;
this.$store.commit("toggleModal", "reminder");
diff --git a/src/store/modules/players.js b/src/store/modules/players.js
index b27af71..f1af219 100644
--- a/src/store/modules/players.js
+++ b/src/store/modules/players.js
@@ -1,4 +1,6 @@
const NEWPLAYER = {
+ name: "",
+ id: "",
role: {},
reminders: [],
isVoteless: false,
@@ -60,9 +62,9 @@ const actions = {
});
} else {
players = state.players.map(({ name, id }) => ({
+ ...NEWPLAYER,
name,
- id,
- ...NEWPLAYER
+ id
}));
}
commit("set", players);
@@ -84,8 +86,8 @@ const mutations = {
},
add(state, name) {
state.players.push({
- name,
- ...NEWPLAYER
+ ...NEWPLAYER,
+ name
});
},
remove(state, index) {
diff --git a/src/store/modules/session.js b/src/store/modules/session.js
index 6998b1c..7bfe604 100644
--- a/src/store/modules/session.js
+++ b/src/store/modules/session.js
@@ -22,6 +22,9 @@ const mutations = {
},
setPlayerCount(state, playerCount) {
state.playerCount = playerCount;
+ },
+ claimSeat(state, claimedSeat) {
+ state.claimedSeat = claimedSeat;
}
};
diff --git a/src/store/socket.js b/src/store/socket.js
index aff0333..14a1552 100644
--- a/src/store/socket.js
+++ b/src/store/socket.js
@@ -93,6 +93,9 @@ class LiveSession {
case "player":
this._updatePlayer(params);
break;
+ case "claim":
+ this._updateSeat(params);
+ break;
case "ping":
this._handlePing(params);
break;
@@ -304,6 +307,40 @@ class LiveSession {
Object.keys(this._players).length
);
}
+
+ /**
+ * Claim a seat, needs to be confirmed by the Storyteller.
+ * @param seat either -1 or the index of the seat claimed
+ */
+ claimSeat(seat) {
+ if (!this._isSpectator) return;
+ if (this._store.state.players.players.length > seat) {
+ this._send("claim", [seat, this._store.state.session.playerId]);
+ }
+ }
+
+ /**
+ * Update a player id associated with that seat.
+ * @param index seat index or -1
+ * @param value playerId to add / remove
+ * @private
+ */
+ _updateSeat([index, value]) {
+ const property = "id";
+ // remove previous seat
+ const player = this._store.state.players.players.find(
+ ({ id }) => id === value
+ );
+ if (player) {
+ this._store.commit("players/update", { player, property, value: "" });
+ }
+ // add playerId to new seat
+ if (index >= 0) {
+ const player = this._store.state.players.players[index];
+ if (!player) return;
+ this._store.commit("players/update", { player, property, value });
+ }
+ }
}
module.exports = store => {
@@ -321,6 +358,9 @@ module.exports = store => {
session.disconnect();
}
break;
+ case "session/claimSeat":
+ session.claimSeat(payload);
+ break;
case "players/set":
case "players/swap":
case "players/move":