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 @@
@@ -31,8 +34,12 @@ }}
- + + + + + + Screenshot -
  • +
  • Remove
  • - Claim seat + + +
  • @@ -121,7 +136,7 @@ {{ reminder.name }} -
    +
    @@ -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":