Merge branch 'develop' into main

This commit is contained in:
Steffen 2021-05-09 21:58:31 +02:00 committed by GitHub
commit c6209a2510
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 179 additions and 117 deletions

View File

@ -4,6 +4,7 @@ on:
types: [assigned, opened, synchronize, reopened, labeled, unlabeled] types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
branches: branches:
- main - main
- develop
jobs: jobs:
build: build:
name: Check Actions name: Check Actions

View File

@ -13,10 +13,10 @@ name: "CodeQL"
on: on:
push: push:
branches: [ main ] branches: [ main, develop ]
pull_request: pull_request:
# The branches below must be a subset of the branches above # The branches below must be a subset of the branches above
branches: [ main ] branches: [ main, develop ]
schedule: schedule:
- cron: '27 22 * * 1' - cron: '27 22 * * 1'

View File

@ -1,11 +1,14 @@
# Release Notes # Release Notes
- added record vote history toggle to session menu, and clear vote history button - added record vote history toggle to session menu, and clear vote history button
- add support for custom Fabled characters
---
### 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
- added Heretic and Marionette to list of available characters - added Heretic and Marionette plus King/Choirboy and the Gangster to list of available characters
--- ---

View File

@ -19,6 +19,9 @@ Before submitting your contribution, please make sure to take a moment and read
- The `main` branch is what is currently deployed to the website. All development should be done in dedicated branches. - The `main` branch is what is currently deployed to the website. All development should be done in dedicated branches.
- The `develop` branch contains the changes that will be deployed to main next. In order to prepare a release, development
branches should have their Pull Request against `develop` and only releases should be merged from `develop` into `main`.
- Work in the `src` folder and **DO NOT** checkin `dist` in the commits. - Work in the `src` folder and **DO NOT** checkin `dist` in the commits.
- It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging. - It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
@ -31,6 +34,9 @@ Before submitting your contribution, please make sure to take a moment and read
- If you are resolving a special issue, add `(fix #xxxx[,#xxxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`. - If you are resolving a special issue, add `(fix #xxxx[,#xxxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`.
- Provide a detailed description of the bug in the PR. Live demo preferred. - Provide a detailed description of the bug in the PR. Live demo preferred.
- You'll need to update the `CHANGELOG.md` with a description of your changes before you open a pull request and your code
should pass the lint check.
## Development Setup ## Development Setup
You will need [Node.js](http://nodejs.org) **version 8+** and a Chrome browser. You will need [Node.js](http://nodejs.org) **version 8+** and a Chrome browser.

View File

@ -96,7 +96,8 @@ For base game characters, it is sufficient to only provide the ID, similar to wh
- **remindersGlobal**: global reminder tokens that will always be available, no matter if the character is assigned to a player or not - **remindersGlobal**: global reminder tokens that will always be available, no matter if the character is assigned to a player or not
- **setup**: whether this token affects setup (orange leaf), like the Drunk or Baron - **setup**: whether this token affects setup (orange leaf), like the Drunk or Baron
- **name**: the displayed name of this character - **name**: the displayed name of this character
- **team**: the team of the character, has to be one of `townsfolk`, `outsider`, `minion`, `demon` or `traveler` - **team**: the team of the character, has to be one of `townsfolk`, `outsider`, `minion`, `demon`, `traveler` or `fabled`<br>
_Note_: if you create a custom Fabled character, it will be automatically added to the game when the custom script is loaded
- **ability**: the displayed ability text of the character - **ability**: the displayed ability text of the character
## [Code of Conduct](CODE_OF_CONDUCT.md) ## [Code of Conduct](CODE_OF_CONDUCT.md)

16
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "townsquare", "name": "townsquare",
"version": "2.12.0", "version": "2.13.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "2.12.0", "version": "2.13.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32", "@fortawesome/fontawesome-svg-core": "^1.2.32",
@ -1628,9 +1628,9 @@
} }
}, },
"node_modules/cacache/node_modules/ssri": { "node_modules/cacache/node_modules/ssri": {
"version": "6.0.1", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
"dependencies": { "dependencies": {
"figgy-pudding": "^3.5.1" "figgy-pudding": "^3.5.1"
} }
@ -13477,9 +13477,9 @@
}, },
"dependencies": { "dependencies": {
"ssri": { "ssri": {
"version": "6.0.1", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
"requires": { "requires": {
"figgy-pudding": "^3.5.1" "figgy-pudding": "^3.5.1"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "townsquare", "name": "townsquare",
"version": "2.12.0", "version": "2.13.0",
"description": "Blood on the Clocktower Town Square", "description": "Blood on the Clocktower Town Square",
"author": "Steffen Baumgart", "author": "Steffen Baumgart",
"scripts": { "scripts": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
src/assets/icons/fabled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
src/assets/icons/king.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -4,7 +4,7 @@
"name": "Washerwoman", "name": "Washerwoman",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 25, "firstNight": 26,
"firstNightReminder": "Show the character token of a Townsfolk in play. Point to two players, one of which is that character.", "firstNightReminder": "Show the character token of a Townsfolk in play. Point to two players, one of which is that character.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -18,7 +18,7 @@
"name": "Librarian", "name": "Librarian",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 26, "firstNight": 27,
"firstNightReminder": "Show the character token of an Outsider in play. Point to two players, one of which is that character.", "firstNightReminder": "Show the character token of an Outsider in play. Point to two players, one of which is that character.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -32,7 +32,7 @@
"name": "Investigator", "name": "Investigator",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 27, "firstNight": 28,
"firstNightReminder": "Show the character token of a Minion in play. Point to two players, one of which is that character.", "firstNightReminder": "Show the character token of a Minion in play. Point to two players, one of which is that character.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -46,7 +46,7 @@
"name": "Chef", "name": "Chef",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 28, "firstNight": 29,
"firstNightReminder": "Show the finger signal (0, 1, 2, \u2026) for the number of pairs of neighbouring evil players.", "firstNightReminder": "Show the finger signal (0, 1, 2, \u2026) for the number of pairs of neighbouring evil players.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -59,9 +59,9 @@
"name": "Empath", "name": "Empath",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 29, "firstNight": 30,
"firstNightReminder": "Show the finger signal (0, 1, 2) for the number of evil alive neighbours of the Empath.", "firstNightReminder": "Show the finger signal (0, 1, 2) for the number of evil alive neighbours of the Empath.",
"otherNight": 47, "otherNight": 46,
"otherNightReminder": "Show the finger signal (0, 1, 2) for the number of evil neighbours.", "otherNightReminder": "Show the finger signal (0, 1, 2) for the number of evil neighbours.",
"reminders": [], "reminders": [],
"setup": false, "setup": false,
@ -72,9 +72,9 @@
"name": "Fortune Teller", "name": "Fortune Teller",
"edition": "tb", "edition": "tb",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 30, "firstNight": 31,
"firstNightReminder": "The Fortune Teller points to two players. Give the head signal (nod yes, shake no) for whether one of those players is the Demon. ", "firstNightReminder": "The Fortune Teller points to two players. Give the head signal (nod yes, shake no) for whether one of those players is the Demon. ",
"otherNight": 48, "otherNight": 47,
"otherNightReminder": "The Fortune Teller points to two players. Show the head signal (nod 'yes', shake 'no') for whether one of those players is the Demon.", "otherNightReminder": "The Fortune Teller points to two players. Show the head signal (nod 'yes', shake 'no') for whether one of those players is the Demon.",
"reminders": ["Red herring"], "reminders": ["Red herring"],
"setup": false, "setup": false,
@ -87,7 +87,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 50, "otherNight": 49,
"otherNightReminder": "If a player was executed today: Show that player\u2019s character token.", "otherNightReminder": "If a player was executed today: Show that player\u2019s character token.",
"reminders": ["Executed"], "reminders": ["Executed"],
"setup": false, "setup": false,
@ -113,7 +113,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 45, "otherNight": 38,
"otherNightReminder": "If the Ravenkeeper died tonight: The Ravenkeeper points to a player. Show that player\u2019s character token.", "otherNightReminder": "If the Ravenkeeper died tonight: The Ravenkeeper points to a player. Show that player\u2019s character token.",
"reminders": [], "reminders": [],
"setup": false, "setup": false,
@ -176,9 +176,9 @@
"name": "Butler", "name": "Butler",
"edition": "tb", "edition": "tb",
"team": "outsider", "team": "outsider",
"firstNight": 31, "firstNight": 32,
"firstNightReminder": "The Butler points to a player. Mark that player as 'Master'.", "firstNightReminder": "The Butler points to a player. Mark that player as 'Master'.",
"otherNight": 49, "otherNight": 48,
"otherNightReminder": "The Butler points to a player. Mark that player as 'Master'.", "otherNightReminder": "The Butler points to a player. Mark that player as 'Master'.",
"reminders": ["Master"], "reminders": ["Master"],
"setup": false, "setup": false,
@ -229,7 +229,7 @@
"name": "Poisoner", "name": "Poisoner",
"edition": "tb", "edition": "tb",
"team": "minion", "team": "minion",
"firstNight": 13, "firstNight": 14,
"firstNightReminder": "The Poisoner points to a player. That player is poisoned.", "firstNightReminder": "The Poisoner points to a player. That player is poisoned.",
"otherNight": 7, "otherNight": 7,
"otherNightReminder": "The previously poisoned player is no longer poisoned. The Poisoner points to a player. That player is poisoned.", "otherNightReminder": "The previously poisoned player is no longer poisoned. The Poisoner points to a player. That player is poisoned.",
@ -242,7 +242,7 @@
"name": "Spy", "name": "Spy",
"edition": "tb", "edition": "tb",
"team": "minion", "team": "minion",
"firstNight": 39, "firstNight": 40,
"firstNightReminder": "Show the Grimoire to the Spy for as long as they need.", "firstNightReminder": "Show the Grimoire to the Spy for as long as they need.",
"otherNight": 60, "otherNight": 60,
"otherNightReminder": "Show the Grimoire to the Spy for as long as they need.", "otherNightReminder": "Show the Grimoire to the Spy for as long as they need.",
@ -359,9 +359,9 @@
"name": "Grandmother", "name": "Grandmother",
"edition": "bmr", "edition": "bmr",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 32, "firstNight": 33,
"firstNightReminder": "Show the marked character token. Point to the marked player.", "firstNightReminder": "Show the marked character token. Point to the marked player.",
"otherNight": 43, "otherNight": 45,
"otherNightReminder": "If the Grandmother\u2019s grandchild was killed by the Demon tonight: The Grandmother dies.", "otherNightReminder": "If the Grandmother\u2019s grandchild was killed by the Demon tonight: The Grandmother dies.",
"reminders": ["Grandchild"], "reminders": ["Grandchild"],
"setup": false, "setup": false,
@ -372,7 +372,7 @@
"name": "Sailor", "name": "Sailor",
"edition": "bmr", "edition": "bmr",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 8, "firstNight": 9,
"firstNightReminder": "The Sailor points to a living player. Either the Sailor, or the chosen player, is drunk.", "firstNightReminder": "The Sailor points to a living player. Either the Sailor, or the chosen player, is drunk.",
"otherNight": 4, "otherNight": 4,
"otherNightReminder": "The previously drunk player is no longer drunk. The Sailor points to a living player. Either the Sailor, or the chosen player, is drunk.", "otherNightReminder": "The previously drunk player is no longer drunk. The Sailor points to a living player. Either the Sailor, or the chosen player, is drunk.",
@ -385,7 +385,7 @@
"name": "Chambermaid", "name": "Chambermaid",
"edition": "bmr", "edition": "bmr",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 42, "firstNight": 43,
"firstNightReminder": "The Chambermaid points to two players. Show the number signal (0, 1, 2, \u2026) for how many of those players wake tonight for their ability.", "firstNightReminder": "The Chambermaid points to two players. Show the number signal (0, 1, 2, \u2026) for how many of those players wake tonight for their ability.",
"otherNight": 63, "otherNight": 63,
"otherNightReminder": "The Chambermaid points to two players. Show the number signal (0, 1, 2, \u2026) for how many of those players wake tonight for their ability.", "otherNightReminder": "The Chambermaid points to two players. Show the number signal (0, 1, 2, \u2026) for how many of those players wake tonight for their ability.",
@ -440,7 +440,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 40, "otherNight": 42,
"otherNightReminder": "If the Gossip\u2019s public statement was true: Choose a player not protected from dying tonight. That player dies.", "otherNightReminder": "If the Gossip\u2019s public statement was true: Choose a player not protected from dying tonight. That player dies.",
"reminders": ["Dead"], "reminders": ["Dead"],
"setup": false, "setup": false,
@ -451,7 +451,7 @@
"name": "Courtier", "name": "Courtier",
"edition": "bmr", "edition": "bmr",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 15, "firstNight": 16,
"firstNightReminder": "The Courtier either shows a 'no' head signal, or points to a character on the sheet. If the Courtier used their ability: If that character is in play, that player is drunk.", "firstNightReminder": "The Courtier either shows a 'no' head signal, or points to a character on the sheet. If the Courtier used their ability: If that character is in play, that player is drunk.",
"otherNight": 9, "otherNight": 9,
"otherNightReminder": "Reduce the remaining number of days the marked player is poisoned. If the Courtier has not yet used their ability: The Courtier either shows a 'no' head signal, or points to a character on the sheet. If the Courtier used their ability: If that character is in play, that player is drunk.", "otherNightReminder": "Reduce the remaining number of days the marked player is poisoned. If the Courtier has not yet used their ability: The Courtier either shows a 'no' head signal, or points to a character on the sheet. If the Courtier used their ability: If that character is in play, that player is drunk.",
@ -469,7 +469,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 39, "otherNight": 41,
"otherNightReminder": "If the Professor has not used their ability: The Professor either shakes their head no, or points to a player. If that player is a Townsfolk, they are now alive.", "otherNightReminder": "If the Professor has not used their ability: The Professor either shakes their head no, or points to a player. If that player is a Townsfolk, they are now alive.",
"reminders": ["Alive", "reminders": ["Alive",
"No ability"], "No ability"],
@ -535,7 +535,7 @@
"team": "outsider", "team": "outsider",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "The Tinker might die.", "firstNightReminder": "The Tinker might die.",
"otherNight": 41, "otherNight": 43,
"otherNightReminder": "The Tinker might die.", "otherNightReminder": "The Tinker might die.",
"reminders": ["Dead"], "reminders": ["Dead"],
"setup": false, "setup": false,
@ -548,7 +548,7 @@
"team": "outsider", "team": "outsider",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 42, "otherNight": 44,
"otherNightReminder": "If the Moonchild used their ability to target a player today: If that player is good, they die.", "otherNightReminder": "If the Moonchild used their ability to target a player today: If that player is good, they die.",
"reminders": ["Dead"], "reminders": ["Dead"],
"setup": false, "setup": false,
@ -587,7 +587,7 @@
"name": "Godfather", "name": "Godfather",
"edition": "bmr", "edition": "bmr",
"team": "minion", "team": "minion",
"firstNight": 17, "firstNight": 18,
"firstNightReminder": "Show each of the Outsider tokens in play.", "firstNightReminder": "Show each of the Outsider tokens in play.",
"otherNight": 34, "otherNight": 34,
"otherNightReminder": "If an Outsider died today: The Godfather points to a player. That player dies.", "otherNightReminder": "If an Outsider died today: The Godfather points to a player. That player dies.",
@ -601,7 +601,7 @@
"name": "Devil's Advocate", "name": "Devil's Advocate",
"edition": "bmr", "edition": "bmr",
"team": "minion", "team": "minion",
"firstNight": 18, "firstNight": 19,
"firstNightReminder": "The Devil\u2019s Advocate points to a living player. That player survives execution tomorrow.", "firstNightReminder": "The Devil\u2019s Advocate points to a living player. That player survives execution tomorrow.",
"otherNight": 13, "otherNight": 13,
"otherNightReminder": "The Devil\u2019s Advocate points to a living player, different from the previous night. That player survives execution tomorrow.", "otherNightReminder": "The Devil\u2019s Advocate points to a living player, different from the previous night. That player survives execution tomorrow.",
@ -655,7 +655,7 @@
"name": "Pukka", "name": "Pukka",
"edition": "bmr", "edition": "bmr",
"team": "demon", "team": "demon",
"firstNight": 23, "firstNight": 24,
"firstNightReminder": "The Pukka points to a player. That player is poisoned.", "firstNightReminder": "The Pukka points to a player. That player is poisoned.",
"otherNight": 24, "otherNight": 24,
"otherNightReminder": "The poisoned player dies. The Pukka points to a player. That player is poisoned.", "otherNightReminder": "The poisoned player dies. The Pukka points to a player. That player is poisoned.",
@ -763,7 +763,7 @@
"name": "Clockmaker", "name": "Clockmaker",
"edition": "snv", "edition": "snv",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 33, "firstNight": 34,
"firstNightReminder": "Show the hand signal for the number (1, 2, 3, etc.) of places from Demon to closest Minion.", "firstNightReminder": "Show the hand signal for the number (1, 2, 3, etc.) of places from Demon to closest Minion.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -776,9 +776,9 @@
"name": "Dreamer", "name": "Dreamer",
"edition": "snv", "edition": "snv",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 34, "firstNight": 35,
"firstNightReminder": "The Dreamer points to a player. Show 1 good and 1 evil character token; one of these is correct.", "firstNightReminder": "The Dreamer points to a player. Show 1 good and 1 evil character token; one of these is correct.",
"otherNight": 51, "otherNight": 50,
"otherNightReminder": "The Dreamer points to a player. Show 1 good and 1 evil character token; one of these is correct.", "otherNightReminder": "The Dreamer points to a player. Show 1 good and 1 evil character token; one of these is correct.",
"reminders": [], "reminders": [],
"setup": false, "setup": false,
@ -789,7 +789,7 @@
"name": "Snake Charmer", "name": "Snake Charmer",
"edition": "snv", "edition": "snv",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 16, "firstNight": 17,
"firstNightReminder": "The Snake Charmer points to a player. If that player is the Demon: swap the Demon and Snake Charmer character and alignments. Wake each player to inform them of their new role and alignment. The new Snake Charmer is poisoned.", "firstNightReminder": "The Snake Charmer points to a player. If that player is the Demon: swap the Demon and Snake Charmer character and alignments. Wake each player to inform them of their new role and alignment. The new Snake Charmer is poisoned.",
"otherNight": 11, "otherNight": 11,
"otherNightReminder": "The Snake Charmer points to a player. If that player is the Demon: swap the Demon and Snake Charmer character and alignments. Wake each player to inform them of their new role and alignment. The new Snake Charmer is poisoned.", "otherNightReminder": "The Snake Charmer points to a player. If that player is the Demon: swap the Demon and Snake Charmer character and alignments. Wake each player to inform them of their new role and alignment. The new Snake Charmer is poisoned.",
@ -802,7 +802,7 @@
"name": "Mathematician", "name": "Mathematician",
"edition": "snv", "edition": "snv",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 41, "firstNight": 42,
"firstNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of players whose ability malfunctioned due to other abilities.", "firstNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of players whose ability malfunctioned due to other abilities.",
"otherNight": 62, "otherNight": 62,
"otherNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of players whose ability malfunctioned due to other abilities.", "otherNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of players whose ability malfunctioned due to other abilities.",
@ -817,7 +817,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "Place the 'Demon not voted' marker.", "firstNightReminder": "Place the 'Demon not voted' marker.",
"otherNight": 52, "otherNight": 51,
"otherNightReminder": "Nod 'yes' or shake head 'no' for whether the Demon voted today. Place the 'Demon not voted' marker (remove 'Demon voted', if any).", "otherNightReminder": "Nod 'yes' or shake head 'no' for whether the Demon voted today. Place the 'Demon not voted' marker (remove 'Demon voted', if any).",
"reminders": ["Demon voted", "reminders": ["Demon voted",
"Demon not voted"], "Demon not voted"],
@ -831,7 +831,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "Place the 'Minions not nominated' marker.", "firstNightReminder": "Place the 'Minions not nominated' marker.",
"otherNight": 53, "otherNight": 52,
"otherNightReminder": "Nod 'yes' or shake head 'no' for whether a Minion nominated today. Place the 'Minion not nominated' marker (remove 'Minion nominated', if any).", "otherNightReminder": "Nod 'yes' or shake head 'no' for whether a Minion nominated today. Place the 'Minion not nominated' marker (remove 'Minion nominated', if any).",
"reminders": ["Minions not nominated", "reminders": ["Minions not nominated",
"Minion nominated"], "Minion nominated"],
@ -845,7 +845,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 54, "otherNight": 53,
"otherNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of dead evil players.", "otherNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of dead evil players.",
"reminders": [], "reminders": [],
"setup": false, "setup": false,
@ -869,9 +869,9 @@
"name": "Seamstress", "name": "Seamstress",
"edition": "snv", "edition": "snv",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 35, "firstNight": 36,
"firstNightReminder": "The Seamstress either shows a 'no' head signal, or points to two other players. If the Seamstress chose players , nod 'yes' or shake 'no' for whether they are of same alignment.", "firstNightReminder": "The Seamstress either shows a 'no' head signal, or points to two other players. If the Seamstress chose players , nod 'yes' or shake 'no' for whether they are of same alignment.",
"otherNight": 55, "otherNight": 54,
"otherNightReminder": "If the Seamstress has not yet used their ability: the Seamstress either shows a 'no' head signal, or points to two other players. If the Seamstress chose players , nod 'yes' or shake 'no' for whether they are of same alignment.", "otherNightReminder": "If the Seamstress has not yet used their ability: the Seamstress either shows a 'no' head signal, or points to two other players. If the Seamstress chose players , nod 'yes' or shake 'no' for whether they are of same alignment.",
"reminders": ["No ability"], "reminders": ["No ability"],
"setup": false, "setup": false,
@ -911,7 +911,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 56, "otherNight": 55,
"otherNightReminder": "If today was the Juggler\u2019s first day: Show the hand signal for the number (0, 1, 2, etc.) of 'Correct' markers. Remove markers.", "otherNightReminder": "If today was the Juggler\u2019s first day: Show the hand signal for the number (0, 1, 2, etc.) of 'Correct' markers. Remove markers.",
"reminders": ["Correct"], "reminders": ["Correct"],
"setup": false, "setup": false,
@ -924,7 +924,7 @@
"team": "townsfolk", "team": "townsfolk",
"firstNight": 0, "firstNight": 0,
"firstNightReminder": "", "firstNightReminder": "",
"otherNight": 38, "otherNight": 39,
"otherNightReminder": "If the Sage was killed by a Demon: Point to two players, one of which is that Demon.", "otherNightReminder": "If the Sage was killed by a Demon: Point to two players, one of which is that Demon.",
"reminders": [], "reminders": [],
"setup": false, "setup": false,
@ -987,7 +987,7 @@
"name": "Evil Twin", "name": "Evil Twin",
"edition": "snv", "edition": "snv",
"team": "minion", "team": "minion",
"firstNight": 19, "firstNight": 20,
"firstNightReminder": "Wake the Evil Twin and their twin. Confirm that they have acknowledged each other. Point to the Evil Twin. Show their Evil Twin token to the twin player. Point to the twin. Show their character token to the Evil Twin player.", "firstNightReminder": "Wake the Evil Twin and their twin. Confirm that they have acknowledged each other. Point to the Evil Twin. Show their Evil Twin token to the twin player. Point to the twin. Show their character token to the Evil Twin player.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -1000,7 +1000,7 @@
"name": "Witch", "name": "Witch",
"edition": "snv", "edition": "snv",
"team": "minion", "team": "minion",
"firstNight": 20, "firstNight": 21,
"firstNightReminder": "The Witch points to a player. If that player nominates tomorrow they die immediately.", "firstNightReminder": "The Witch points to a player. If that player nominates tomorrow they die immediately.",
"otherNight": 14, "otherNight": 14,
"otherNightReminder": "If there are 4 or more players alive: The Witch points to a player. If that player nominates tomorrow they die immediately.", "otherNightReminder": "If there are 4 or more players alive: The Witch points to a player. If that player nominates tomorrow they die immediately.",
@ -1013,7 +1013,7 @@
"name": "Cerenovus", "name": "Cerenovus",
"edition": "snv", "edition": "snv",
"team": "minion", "team": "minion",
"firstNight": 21, "firstNight": 22,
"firstNightReminder": "The Cerenovus points to a player, then to a character on their sheet. Wake that player. Show the 'This character selected you' card, then the Cerenovus token. Show the selected character token. If the player is not mad about being that character tomorrow, they can be executed.", "firstNightReminder": "The Cerenovus points to a player, then to a character on their sheet. Wake that player. Show the 'This character selected you' card, then the Cerenovus token. Show the selected character token. If the player is not mad about being that character tomorrow, they can be executed.",
"otherNight": 15, "otherNight": 15,
"otherNightReminder": "The Cerenovus points to a player, then to a character on their sheet. Wake that player. Show the 'This character selected you' card, then the Cerenovus token. Show the selected character token. If the player is not mad about being that character tomorrow, they can be executed.", "otherNightReminder": "The Cerenovus points to a player, then to a character on their sheet. Wake that player. Show the 'This character selected you' card, then the Cerenovus token. Show the selected character token. If the player is not mad about being that character tomorrow, they can be executed.",
@ -1161,7 +1161,7 @@
"name": "Bounty Hunter", "name": "Bounty Hunter",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 37, "firstNight": 38,
"firstNightReminder": "Point to 1 evil player. Wake the townsfolk who is evil and show them the 'You are' card and the thumbs down evil sign.", "firstNightReminder": "Point to 1 evil player. Wake the townsfolk who is evil and show them the 'You are' card and the thumbs down evil sign.",
"otherNight": 58, "otherNight": 58,
"otherNightReminder": "If the known evil player has died, point to another evil player. ", "otherNightReminder": "If the known evil player has died, point to another evil player. ",
@ -1174,7 +1174,7 @@
"name": "Pixie", "name": "Pixie",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 24, "firstNight": 25,
"firstNightReminder": "Show the Pixie 1 in-play Townsfolk character token.", "firstNightReminder": "Show the Pixie 1 in-play Townsfolk character token.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "If the Pixie has been mad they were the relevant character and that player has died, treat the Pixie as if they have the relevant character ability.", "otherNightReminder": "If the Pixie has been mad they were the relevant character and that player has died, treat the Pixie as if they have the relevant character ability.",
@ -1188,7 +1188,7 @@
"name": "General", "name": "General",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 40, "firstNight": 41,
"firstNightReminder": "Show the General thumbs up for good winning, thumbs down for evil winning or thumb to the side for neither.", "firstNightReminder": "Show the General thumbs up for good winning, thumbs down for evil winning or thumb to the side for neither.",
"otherNight": 61, "otherNight": 61,
"otherNightReminder": "Show the General thumbs up for good winning, thumbs down for evil winning or thumb to the side for neither.", "otherNightReminder": "Show the General thumbs up for good winning, thumbs down for evil winning or thumb to the side for neither.",
@ -1201,7 +1201,7 @@
"name": "Preacher", "name": "Preacher",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 11, "firstNight": 12,
"firstNightReminder": "The Preacher chooses a player. If a Minion is chosen, wake the Minion and show the 'This character selected you' card and then the Preacher token.", "firstNightReminder": "The Preacher chooses a player. If a Minion is chosen, wake the Minion and show the 'This character selected you' card and then the Preacher token.",
"otherNight": 6, "otherNight": 6,
"otherNightReminder": "The Preacher chooses a player. If a Minion is chosen, wake the Minion and show the 'This character selected you' card and then the Preacher token.", "otherNightReminder": "The Preacher chooses a player. If a Minion is chosen, wake the Minion and show the 'This character selected you' card and then the Preacher token.",
@ -1209,14 +1209,27 @@
"setup": false, "setup": false,
"ability": "Each night, choose a player: a Minion, if chosen, learns this. All chosen Minions have no ability." "ability": "Each night, choose a player: a Minion, if chosen, learns this. All chosen Minions have no ability."
}, },
{
"id": "king",
"name": "King",
"edition": "",
"team": "townsfolk",
"firstNight": 8,
"firstNightReminder": "Wake the Demon, show them the 'This character selected you' card, show the King token and point to the King player.",
"otherNight": 57,
"otherNightReminder": "If there are more dead than living, show the King a character token of a living player.",
"reminders": [],
"setup": false,
"ability": "Each night, if the dead outnumber the living, you learn 1 alive character. The Demon knows who you are."
},
{ {
"id": "balloonist", "id": "balloonist",
"name": "Balloonist", "name": "Balloonist",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 36, "firstNight": 37,
"firstNightReminder": "Choose a character type. Point to a player whose character is of that type. Place the Balloonist's Seen reminder next to that character.", "firstNightReminder": "Choose a character type. Point to a player whose character is of that type. Place the Balloonist's Seen reminder next to that character.",
"otherNight": 57, "otherNight": 56,
"otherNightReminder": "Choose a character type that does not yet have a Seen reminder next to a character of that type. Point to a player whose character is of that type, if there are any. Place the Balloonist's Seen reminder next to that character.", "otherNightReminder": "Choose a character type that does not yet have a Seen reminder next to a character of that type. Point to a player whose character is of that type, if there are any. Place the Balloonist's Seen reminder next to that character.",
"reminders": ["Seen Townsfolk", "reminders": ["Seen Townsfolk",
"Seen Outsider", "Seen Outsider",
@ -1231,7 +1244,7 @@
"name": "Cult Leader", "name": "Cult Leader",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 38, "firstNight": 39,
"firstNightReminder": "If the cult leader changed alignment, show them the thumbs up good signal of the thumbs down evil signal accordingly.", "firstNightReminder": "If the cult leader changed alignment, show them the thumbs up good signal of the thumbs down evil signal accordingly.",
"otherNight": 59, "otherNight": 59,
"otherNightReminder": "If the cult leader changed alignment, show them the thumbs up good signal of the thumbs down evil signal accordingly.", "otherNightReminder": "If the cult leader changed alignment, show them the thumbs up good signal of the thumbs down evil signal accordingly.",
@ -1257,7 +1270,7 @@
"name": "Amnesiac", "name": "Amnesiac",
"edition": "", "edition": "",
"team": "townsfolk", "team": "townsfolk",
"firstNight": 10, "firstNight": 11,
"firstNightReminder": "Decide the Amnesiac's entire ability. If the Amnesiac's ability causes them to wake tonight: Wake the Amnesiac and run their ability.", "firstNightReminder": "Decide the Amnesiac's entire ability. If the Amnesiac's ability causes them to wake tonight: Wake the Amnesiac and run their ability.",
"otherNight": 5, "otherNight": 5,
"otherNightReminder": "If the Amnesiac's ability causes them to wake tonight: Wake the Amnesiac and run their ability.", "otherNightReminder": "If the Amnesiac's ability causes them to wake tonight: Wake the Amnesiac and run their ability.",
@ -1278,6 +1291,19 @@
"setup": false, "setup": false,
"ability": "Once per game, during the day, visit the Storyteller for some advice to help you win." "ability": "Once per game, during the day, visit the Storyteller for some advice to help you win."
}, },
{
"id": "choirboy",
"name": "Choirboy",
"edition": "",
"team": "townsfolk",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 40,
"otherNightReminder": "If the King was killed by the Demon, wake the Choirboy and point to the Demon player.",
"reminders": [],
"setup": true,
"ability": "If the Demon kills the King, you learn which player is the Demon. [+ the King]"
},
{ {
"id": "poppygrower", "id": "poppygrower",
"name": "Poppy Grower", "name": "Poppy Grower",
@ -1362,7 +1388,7 @@
"name": "Widow", "name": "Widow",
"edition": "", "edition": "",
"team": "minion", "team": "minion",
"firstNight": 14, "firstNight": 15,
"firstNightReminder": "Show the Grimoire to the Widow for as long as they need. The Widow points to a player. That player is poisoned. Wake a good player. Show the 'These characters are in play' card, then the Widow character token.", "firstNightReminder": "Show the Grimoire to the Widow for as long as they need. The Widow points to a player. That player is poisoned. Wake a good player. Show the 'These characters are in play' card, then the Widow character token.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -1389,7 +1415,7 @@
"name": "Mephit", "name": "Mephit",
"edition": "", "edition": "",
"team": "minion", "team": "minion",
"firstNight": 22, "firstNight": 23,
"firstNightReminder": "Show the Mephit their secret word.", "firstNightReminder": "Show the Mephit their secret word.",
"otherNight": 17, "otherNight": 17,
"otherNightReminder": "Wake the 1st good player that said the Mephit's secret word and show them the 'You are' card and the thumbs down evil signal.", "otherNightReminder": "Wake the 1st good player that said the Mephit's secret word and show them the 'You are' card and the thumbs down evil signal.",
@ -1403,7 +1429,7 @@
"name": "Marionette", "name": "Marionette",
"edition": "", "edition": "",
"team": "minion", "team": "minion",
"firstNight": 9, "firstNight": 10,
"firstNightReminder": "Select one of the good players next to the Demon and place the Is the Marionette reminder token. Wake the Demon and show them the Marionette.", "firstNightReminder": "Select one of the good players next to the Demon and place the Is the Marionette reminder token. Wake the Demon and show them the Marionette.",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "", "otherNightReminder": "",
@ -1417,7 +1443,7 @@
"name": "Lil Monsta", "name": "Lil Monsta",
"edition": "", "edition": "",
"team": "demon", "team": "demon",
"firstNight": 12, "firstNight": 13,
"firstNightReminder": "Wake all Minions together, allow them to vote by pointing at who they want to babysit Lil' Monsta.", "firstNightReminder": "Wake all Minions together, allow them to vote by pointing at who they want to babysit Lil' Monsta.",
"otherNight": 32, "otherNight": 32,
"otherNightReminder": "Wake all Minions together, allow them to vote by pointing at who they want to babysit Lil' Monsta. Choose a player, that player dies.", "otherNightReminder": "Wake all Minions together, allow them to vote by pointing at who they want to babysit Lil' Monsta. Choose a player, that player dies.",
@ -1446,7 +1472,7 @@
"name": "Leviathan", "name": "Leviathan",
"edition": "", "edition": "",
"team": "demon", "team": "demon",
"firstNight": 44, "firstNight": 45,
"firstNightReminder": "Place the Leviathan 'Day 1' marker. Announce 'The Leviathan is in play; this is Day 1.'", "firstNightReminder": "Place the Leviathan 'Day 1' marker. Announce 'The Leviathan is in play; this is Day 1.'",
"otherNight": 0, "otherNight": 0,
"otherNightReminder": "Place the next Leviathan 'Day n' marker, where 'n' is the next day number. Announce 'The Leviathan is in play; this is Day n.'.", "otherNightReminder": "Place the next Leviathan 'Day n' marker, where 'n' is the next day number. Announce 'The Leviathan is in play; this is Day n.'.",
@ -1458,6 +1484,19 @@
"Good player executed"], "Good player executed"],
"setup": false, "setup": false,
"ability": "If more than 1 good player is executed, you win. All players know you are in play. After day 5, evil wins." "ability": "If more than 1 good player is executed, you win. All players know you are in play. After day 5, evil wins."
},
{
"id": "gangster",
"name": "Gangster",
"edition": "",
"team": "traveler",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 0,
"otherNightReminder": "",
"reminders": [],
"setup": false,
"ability": "Once per day, you may choose to kill a living neighbour, if your other living neighbour agrees."
} }
] ]

View File

@ -162,54 +162,62 @@ export default new Vuex.Store({
* @param roles Array of role IDs or full role definitions * @param roles Array of role IDs or full role definitions
*/ */
setCustomRoles(state, roles) { setCustomRoles(state, roles) {
state.roles = new Map( const processedRoles = roles
roles // replace numerical role object keys with matching key names
// replace numerical role object keys with matching key names .map(role => {
.map(role => { if (role[0]) {
if (role[0]) { const customKeys = Object.keys(customRole);
const customKeys = Object.keys(customRole); const mappedRole = {};
const mappedRole = {}; for (let prop in role) {
for (let prop in role) { if (customKeys[prop]) {
if (customKeys[prop]) { mappedRole[customKeys[prop]] = role[prop];
mappedRole[customKeys[prop]] = role[prop];
}
} }
return mappedRole;
} else {
return role;
} }
}) return mappedRole;
// clean up role.id } else {
.map(role => {
role.id = role.id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
return role; return role;
}) }
// map existing roles to base definition or pre-populate custom roles to ensure all properties })
.map( // clean up role.id
role => .map(role => {
rolesJSONbyId.get(role.id) || role.id = role.id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
state.roles.get(role.id) || return role;
Object.assign({}, customRole, role) })
) // map existing roles to base definition or pre-populate custom roles to ensure all properties
// default empty icons and placeholders .map(
.map(role => { role =>
if (rolesJSONbyId.get(role.id)) return role; rolesJSONbyId.get(role.id) ||
role.imageAlt = // map team to generic icon state.roles.get(role.id) ||
{ Object.assign({}, customRole, role)
townsfolk: "good", )
outsider: "outsider", // default empty icons and placeholders
minion: "minion", .map(role => {
demon: "evil" if (rolesJSONbyId.get(role.id)) return role;
}[role.team] || "custom"; role.imageAlt = // map team to generic icon
return role; {
}) townsfolk: "good",
// filter out roles that don't match an existing role and also don't have name/ability/team outsider: "outsider",
.filter(role => role.name && role.ability && role.team) minion: "minion",
// sort by team demon: "evil",
.sort((a, b) => b.team.localeCompare(a.team)) fabled: "fabled"
// convert to Map }[role.team] || "custom";
return role;
})
// filter out roles that don't match an existing role and also don't have name/ability/team
.filter(role => role.name && role.ability && role.team)
// sort by team
.sort((a, b) => b.team.localeCompare(a.team));
// convert to Map without Fabled
state.roles = new Map(
processedRoles
.filter(role => role.team !== "fabled")
.map(role => [role.id, role]) .map(role => [role.id, role])
); );
// update Fabled to include custom Fabled from this script
state.fabled = new Map([
...processedRoles.filter(r => r.team === "fabled").map(r => [r.id, r]),
...fabledJSON.map(role => [role.id, role])
]);
// update extraTravelers map to only show travelers not in this script // update extraTravelers map to only show travelers not in this script
state.otherTravelers = new Map( state.otherTravelers = new Map(
rolesJSON rolesJSON

View File

@ -39,8 +39,8 @@ module.exports = store => {
} }
if (localStorage.fabled !== undefined) { if (localStorage.fabled !== undefined) {
store.commit("players/setFabled", { store.commit("players/setFabled", {
fabled: JSON.parse(localStorage.fabled).map(id => fabled: JSON.parse(localStorage.fabled).map(
store.state.fabled.get(id) fabled => store.state.fabled.get(fabled.id) || fabled
) )
}); });
} }
@ -127,7 +127,11 @@ module.exports = store => {
case "players/setFabled": case "players/setFabled":
localStorage.setItem( localStorage.setItem(
"fabled", "fabled",
JSON.stringify(state.players.fabled.map(({ id }) => id)) JSON.stringify(
state.players.fabled.map(fabled =>
fabled.isCustom ? fabled : { id: fabled.id }
)
)
); );
break; break;
case "players/add": case "players/add":

View File

@ -278,7 +278,7 @@ class LiveSession {
votingSpeed: session.votingSpeed, votingSpeed: session.votingSpeed,
lockedVote: session.lockedVote, lockedVote: session.lockedVote,
isVoteInProgress: session.isVoteInProgress, isVoteInProgress: session.isVoteInProgress,
fabled: fabled.map(({ id }) => id), fabled: fabled.map(f => (f.isCustom ? f : { id: f.id })),
...(session.nomination ? { votes: session.votes } : {}) ...(session.nomination ? { votes: session.votes } : {})
}); });
} }
@ -356,7 +356,7 @@ class LiveSession {
isVoteInProgress isVoteInProgress
}); });
this._store.commit("players/setFabled", { this._store.commit("players/setFabled", {
fabled: fabled.map(id => this._store.state.fabled.get(id)) fabled: fabled.map(f => this._store.state.fabled.get(f.id) || f)
}); });
} }
} }
@ -415,7 +415,7 @@ class LiveSession {
const { fabled } = this._store.state.players; const { fabled } = this._store.state.players;
this._send( this._send(
"fabled", "fabled",
fabled.map(({ id }) => id) fabled.map(f => (f.isCustom ? f : { id: f.id }))
); );
} }
@ -427,7 +427,7 @@ class LiveSession {
_updateFabled(fabled) { _updateFabled(fabled) {
if (!this._isSpectator) return; if (!this._isSpectator) return;
this._store.commit("players/setFabled", { this._store.commit("players/setFabled", {
fabled: fabled.map(id => this._store.state.fabled.get(id)) fabled: fabled.map(f => this._store.state.fabled.get(f.id) || f)
}); });
} }