diff --git a/src/assets/icons/organgrinder.png b/src/assets/icons/organgrinder.png new file mode 100644 index 0000000..81394a5 Binary files /dev/null and b/src/assets/icons/organgrinder.png differ diff --git a/src/components/Gradients.vue b/src/components/Gradients.vue index a9b792b..5d16a35 100644 --- a/src/components/Gradients.vue +++ b/src/components/Gradients.vue @@ -28,6 +28,7 @@ export default { gradients: [ ["demon", "#ce0100", "#000"], ["townsfolk", "#1f65ff", "#000"], + ["minion", "#ff6900", "#000"], ["default", "#4E4E4E", "#000"] ] }; diff --git a/src/components/Menu.vue b/src/components/Menu.vue index a06f530..0da6b82 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -70,6 +70,17 @@ [B] +
  • + {{ locale.menu.grimoire.organGrinder }} + + + +
  • {{ locale.menu.grimoire.order }} @@ -382,6 +393,9 @@ export default { this.$store.commit("session/setMarkedPlayer", -1); } }, + toggleOrganVoteMode() { + this.$store.commit("toggleOrganVoteMode"); + }, toggleRinging() { this.$store.commit("toggleRinging", true); setTimeout(this.$store.commit, 4000, "toggleRinging", false); diff --git a/src/components/Player.vue b/src/components/Player.vue index c0e9482..f62d5a2 100644 --- a/src/components/Player.vue +++ b/src/components/Player.vue @@ -45,17 +45,49 @@
    + + {{ nominee.name }}!
    - {{ voters.length }} {{ locale.vote.votes }} + + {{ voters.length }} {{ locale.vote.votes }} + + ? {{ locale.vote.votes }} {{ locale.vote.inFavor }} ({{ locale.vote.majorityIs }} {{ Math.ceil(alive / 2) }}) diff --git a/src/components/modals/VoteHistoryModal.vue b/src/components/modals/VoteHistoryModal.vue index 2e26bf1..1a4816c 100644 --- a/src/components/modals/VoteHistoryModal.vue +++ b/src/components/modals/VoteHistoryModal.vue @@ -65,7 +65,7 @@ {{ vote.nominee }} {{ vote.type }} - {{ vote.votes.length }} + {{ vote.votes == null ? "?" : vote.votes.length }} @@ -73,12 +73,20 @@ - {{ vote.votes.join(", ") }} + {{ + vote.votes == null + ? locale.modal.voteHistory.hiddenVote + : vote.votes.join(", ") + }} diff --git a/src/main.js b/src/main.js index 5357881..53b8c72 100644 --- a/src/main.js +++ b/src/main.js @@ -30,6 +30,7 @@ const faIcons = [ "Image", "Link", "MinusCircle", + "MinusSquare", "Music", "PeopleArrows", "PlusCircle", diff --git a/src/store/index.js b/src/store/index.js index 885f300..d8d71dc 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -111,6 +111,7 @@ export default new Vuex.Store({ isMuted: false, isImageOptIn: false, isStreamerMode: false, + isOrganVoteMode: false, zoom: 0, background: "", timer: { @@ -185,6 +186,7 @@ export default new Vuex.Store({ toggleGrimoire: toggle("isPublic"), toggleImageOptIn: toggle("isImageOptIn"), toggleStreamerMode: toggle("isStreamerMode"), + toggleOrganVoteMode: toggle("isOrganVoteMode"), setTimer(state, timer) { state.grimoire.timer = timer; }, diff --git a/src/store/locale/en/hatred.json b/src/store/locale/en/hatred.json index a30bb1c..86ac4ce 100644 --- a/src/store/locale/en/hatred.json +++ b/src/store/locale/en/hatred.json @@ -372,5 +372,30 @@ "reason": "If the Lleech has poisoned the Heretic then the Lleech dies, the Heretic remains poisoned." } ] + }, + { + "id": "Organ Grinder", + "hatred": [ + { + "id": "Butler", + "reason": "If the Organ Grinder is causing eyes closed voting, the Butler may raise their hand to vote but their vote is only counted if their master voted too." + }, + { + "id": "Flowergirl", + "reason": "If players' eyes were closed during the nominations, the Flowergirl learns how many times the Demon voted." + }, + { + "id": "Lil' Monsta", + "reason": "Votes for the Organ Grinder count if the Organ Grinder is babysitting Lil' Monsta." + }, + { + "id": "Minstrel", + "reason": "Only 1 jinxed character can be in play. Evil players start knowing which player and character it is." + }, + { + "id": "Preacher", + "reason": "Only 1 jinxed character can be in play. Evil players start knowing which player and character it is." + } + ] } ] diff --git a/src/store/locale/en/roles.json b/src/store/locale/en/roles.json index 238966e..87f8a1b 100644 --- a/src/store/locale/en/roles.json +++ b/src/store/locale/en/roles.json @@ -1637,6 +1637,20 @@ "setup": false, "ability": "If you are executed, all but 3 players die. 1 minute later, the player with the most players pointing at them dies." }, + { + "id": "organgrinder", + "name": "Organ Grinder", + "edition": "", + "team": "minion", + "firstNight": 0, + "firstNightReminder": "", + "otherNight": 0, + "otherNightReminder": "", + "reminders": ["About to die", + "Used vote"], + "setup": false, + "ability": "All players keep their eyes closed when voting & the vote tally is secret. Votes for you only count if you vote." + }, { "id": "lilmonsta", "name": "Lil' Monsta", diff --git a/src/store/locale/en/ui.json b/src/store/locale/en/ui.json index 39eb349..a3c0216 100644 --- a/src/store/locale/en/ui.json +++ b/src/store/locale/en/ui.json @@ -13,7 +13,8 @@ "streamerMode": "Streamer Mode", "animations": "Disable Animations", "mute": "Mute Sounds", - "ringBell": "Ring Bell" + "ringBell": "Ring Bell", + "organGrinder ": "Organ Grinder Vote" }, "session":{ "title":{ @@ -238,7 +239,8 @@ "type": "Type", "votes": "Votes", "majority": "Majority", - "voters": "Voters" + "voters": "Voters", + "hiddenVote": "The result is hidden because of the Organ Grinder" } } } diff --git a/src/store/locale/fr/hatred.json b/src/store/locale/fr/hatred.json index ad644ec..3a73cfd 100644 --- a/src/store/locale/fr/hatred.json +++ b/src/store/locale/fr/hatred.json @@ -359,5 +359,30 @@ "reason": "Si le Tueur tire sur l'hôte de la Sangsue, l'hôte meurt. " } ] + }, + { + "id": "Organ Grinder", + "hatred": [ + { + "id": "Butler", + "reason": "Si les votes ont lieu à bulletin secret à cause de l'Organiste, le Majordome peut lever sa main mais son vote ne compte que si son maître la lève aussi." + }, + { + "id": "Flowergirl", + "reason": "Si les votes ont eu lieu à bulletin secret, la Fleuriste apprend combien de fois le Démon a voté." + }, + { + "id": "Lil' Monsta", + "reason": "Les votes contre l'Organsite comptent si l'Organiste baby-sitte le Bébé Monstre." + }, + { + "id": "Minstrel", + "reason": "Un seul personnage maudit peut être en jeu à la fois. Les joueurs Mauvais commencent la partie en sachant de quel joueur et quel rôle il s'agit." + }, + { + "id": "Preacher", + "reason": "Un seul personnage maudit peut être en jeu à la fois. Les joueurs Mauvais commencent la partie en sachant de quel joueur et quel rôle il s'agit." + } + ] } ] diff --git a/src/store/locale/fr/roles.json b/src/store/locale/fr/roles.json index f8c1a06..d1157df 100644 --- a/src/store/locale/fr/roles.json +++ b/src/store/locale/fr/roles.json @@ -1811,6 +1811,22 @@ "setup": false, "ability": "Si vous êtes exécuté, tous les joueurs sauf 3 meurent. 1 minute plus tard, le joueur le plus accusé meurt." }, + { + "id": "organgrinder", + "name": "Organiste", + "edition": "", + "team": "minion", + "firstNight": 0, + "firstNightReminder": "", + "otherNight": 0, + "otherNightReminder": "", + "reminders": [ + "Condamné", + "Vote utilisé" + ], + "setup": false, + "ability": "Les votes ont lieu à bulletin secret. Vous ne pouvez être exécuté que si vous levez la main." + }, { "id": "lilmonsta", "name": "Bébé monstre", diff --git a/src/store/locale/fr/ui.json b/src/store/locale/fr/ui.json index 9fb0129..b0495a9 100644 --- a/src/store/locale/fr/ui.json +++ b/src/store/locale/fr/ui.json @@ -13,7 +13,8 @@ "streamerMode": "Mode Streamer", "animations": "Effets réduits", "mute": "Silencieux", - "ringBell": "Sonner Clocher" + "ringBell": "Sonner Clocher", + "organGrinder": "Vote d'Organiste" }, "session":{ "title":{ @@ -238,7 +239,8 @@ "type": "Type", "votes": "Voix", "majority": "Majorité", - "voters": "Votants" + "voters": "Votants", + "hiddenVote": "Résultat caché par l'Organiste" } } } diff --git a/src/store/modules/session.js b/src/store/modules/session.js index 884117a..6e98f35 100644 --- a/src/store/modules/session.js +++ b/src/store/modules/session.js @@ -1,3 +1,5 @@ +import gameInfo from "../index.js"; + /** * Handle a vote request. * If the vote is from a seat that is already locked, ignore it. @@ -71,6 +73,7 @@ const mutations = { /** * Create an entry in the vote history log. Requires current player array because it might change later in the game. * Only stores votes that were completed. + * If the Organ Grinder is active, save the votes only for the Story Teller * @param state * @param players */ @@ -78,17 +81,23 @@ const mutations = { if (!state.isVoteHistoryAllowed && state.isSpectator) return; if (!state.nomination || state.lockedVote <= players.length) return; const isExile = players[state.nomination[1]].role.team === "traveler"; + const organGrinder = gameInfo.state.grimoire.isOrganVoteMode && !isExile; state.voteHistory.push({ timestamp: new Date(), nominator: players[state.nomination[0]].name, nominee: players[state.nomination[1]].name, - type: isExile ? "Exile" : "Execution", + type: isExile + ? "Exile" + : "Execution" + (organGrinder && !state.isSpectator ? "*" : ""), majority: Math.ceil( players.filter(player => !player.isDead || isExile).length / 2 ), - votes: players - .filter((player, index) => state.votes[index]) - .map(({ name }) => name) + votes: + organGrinder && state.isSpectator + ? null + : players + .filter((player, index) => state.votes[index]) + .map(({ name }) => name) }); }, clearVoteHistory(state) { diff --git a/src/store/socket.js b/src/store/socket.js index 36b3e9d..f505d6f 100644 --- a/src/store/socket.js +++ b/src/store/socket.js @@ -176,6 +176,10 @@ class LiveSession { if (!this._isSpectator) return; this._store.commit("toggleNight", params); break; + case "isOrganVoteMode": + if (!this._isSpectator) return; + this._store.commit("toggleOrganVoteMode", params); + break; case "isRinging": if (!this._isSpectator) return; this._store.commit("toggleRinging", params); @@ -288,6 +292,7 @@ class LiveSession { isRinging: grimoire.isRinging, timer: grimoire.timer, isVoteHistoryAllowed: session.isVoteHistoryAllowed, + isOrganVoteMode: grimoire.isOrganVoteMode, nomination: session.nomination, votingSpeed: session.votingSpeed, lockedVote: session.lockedVote, @@ -312,6 +317,7 @@ class LiveSession { isNight, isVoteHistoryAllowed, isRinging, + isOrganVoteMode, timer, nomination, votingSpeed, @@ -368,6 +374,7 @@ class LiveSession { this._store.commit("toggleRinging", !!isRinging); this._store.commit("toggleNight", !!isNight); this._store.commit("session/setVoteHistoryAllowed", isVoteHistoryAllowed); + this._store.commit("toggleOrganVoteMode", !!isOrganVoteMode); this._store.commit("session/nomination", { nomination, votes, @@ -725,6 +732,14 @@ class LiveSession { this._send("isRinging", this._store.state.grimoire.isRinging); } + /** + * Send the isOrganVoteMode status. ST only + */ + setIsOrganVoteMode() { + if (this._isSpectator) return; + this._send("isOrganVoteMode", this._store.state.grimoire.isOrganVoteMode); + } + /** * Start or stop a timer */ @@ -914,6 +929,9 @@ export default store => { case "toggleNight": session.setIsNight(); break; + case "toggleOrganVoteMode": + session.setIsOrganVoteMode(); + break; case "toggleRinging": session.setIsRinging(); break;