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]
branches:
- main
- develop
jobs:
build:
name: Check Actions

View File

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

View File

@ -1,11 +1,14 @@
# Release Notes
- added record vote history toggle to session menu, and clear vote history button
- add support for custom Fabled characters
---
### Version 2.12.0
- tweak reference sheet to better fit screen in single column layout
- 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 `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.
- It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
@ -30,6 +33,9 @@ Before submitting your contribution, please make sure to take a moment and read
- If fixing a bug:
- 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.
- 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

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
- **setup**: whether this token affects setup (orange leaf), like the Drunk or Baron
- **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
## [Code of Conduct](CODE_OF_CONDUCT.md)

16
package-lock.json generated
View File

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

View File

@ -1,6 +1,6 @@
{
"name": "townsquare",
"version": "2.12.0",
"version": "2.13.0",
"description": "Blood on the Clocktower Town Square",
"author": "Steffen Baumgart",
"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",
"edition": "tb",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -18,7 +18,7 @@
"name": "Librarian",
"edition": "tb",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -32,7 +32,7 @@
"name": "Investigator",
"edition": "tb",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -46,7 +46,7 @@
"name": "Chef",
"edition": "tb",
"team": "townsfolk",
"firstNight": 28,
"firstNight": 29,
"firstNightReminder": "Show the finger signal (0, 1, 2, \u2026) for the number of pairs of neighbouring evil players.",
"otherNight": 0,
"otherNightReminder": "",
@ -59,9 +59,9 @@
"name": "Empath",
"edition": "tb",
"team": "townsfolk",
"firstNight": 29,
"firstNight": 30,
"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.",
"reminders": [],
"setup": false,
@ -72,9 +72,9 @@
"name": "Fortune Teller",
"edition": "tb",
"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. ",
"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.",
"reminders": ["Red herring"],
"setup": false,
@ -87,7 +87,7 @@
"team": "townsfolk",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 50,
"otherNight": 49,
"otherNightReminder": "If a player was executed today: Show that player\u2019s character token.",
"reminders": ["Executed"],
"setup": false,
@ -113,7 +113,7 @@
"team": "townsfolk",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 45,
"otherNight": 38,
"otherNightReminder": "If the Ravenkeeper died tonight: The Ravenkeeper points to a player. Show that player\u2019s character token.",
"reminders": [],
"setup": false,
@ -176,9 +176,9 @@
"name": "Butler",
"edition": "tb",
"team": "outsider",
"firstNight": 31,
"firstNight": 32,
"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'.",
"reminders": ["Master"],
"setup": false,
@ -229,7 +229,7 @@
"name": "Poisoner",
"edition": "tb",
"team": "minion",
"firstNight": 13,
"firstNight": 14,
"firstNightReminder": "The Poisoner points to a player. That player is poisoned.",
"otherNight": 7,
"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",
"edition": "tb",
"team": "minion",
"firstNight": 39,
"firstNight": 40,
"firstNightReminder": "Show the Grimoire to the Spy for as long as they need.",
"otherNight": 60,
"otherNightReminder": "Show the Grimoire to the Spy for as long as they need.",
@ -359,9 +359,9 @@
"name": "Grandmother",
"edition": "bmr",
"team": "townsfolk",
"firstNight": 32,
"firstNight": 33,
"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.",
"reminders": ["Grandchild"],
"setup": false,
@ -372,7 +372,7 @@
"name": "Sailor",
"edition": "bmr",
"team": "townsfolk",
"firstNight": 8,
"firstNight": 9,
"firstNightReminder": "The Sailor points to a living player. Either the Sailor, or the chosen player, is drunk.",
"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.",
@ -385,7 +385,7 @@
"name": "Chambermaid",
"edition": "bmr",
"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.",
"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.",
@ -440,7 +440,7 @@
"team": "townsfolk",
"firstNight": 0,
"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.",
"reminders": ["Dead"],
"setup": false,
@ -451,7 +451,7 @@
"name": "Courtier",
"edition": "bmr",
"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.",
"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.",
@ -469,7 +469,7 @@
"team": "townsfolk",
"firstNight": 0,
"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.",
"reminders": ["Alive",
"No ability"],
@ -535,7 +535,7 @@
"team": "outsider",
"firstNight": 0,
"firstNightReminder": "The Tinker might die.",
"otherNight": 41,
"otherNight": 43,
"otherNightReminder": "The Tinker might die.",
"reminders": ["Dead"],
"setup": false,
@ -548,7 +548,7 @@
"team": "outsider",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 42,
"otherNight": 44,
"otherNightReminder": "If the Moonchild used their ability to target a player today: If that player is good, they die.",
"reminders": ["Dead"],
"setup": false,
@ -587,7 +587,7 @@
"name": "Godfather",
"edition": "bmr",
"team": "minion",
"firstNight": 17,
"firstNight": 18,
"firstNightReminder": "Show each of the Outsider tokens in play.",
"otherNight": 34,
"otherNightReminder": "If an Outsider died today: The Godfather points to a player. That player dies.",
@ -601,7 +601,7 @@
"name": "Devil's Advocate",
"edition": "bmr",
"team": "minion",
"firstNight": 18,
"firstNight": 19,
"firstNightReminder": "The Devil\u2019s Advocate points to a living player. That player survives execution tomorrow.",
"otherNight": 13,
"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",
"edition": "bmr",
"team": "demon",
"firstNight": 23,
"firstNight": 24,
"firstNightReminder": "The Pukka points to a player. That player is poisoned.",
"otherNight": 24,
"otherNightReminder": "The poisoned player dies. The Pukka points to a player. That player is poisoned.",
@ -763,7 +763,7 @@
"name": "Clockmaker",
"edition": "snv",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -776,9 +776,9 @@
"name": "Dreamer",
"edition": "snv",
"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.",
"otherNight": 51,
"otherNight": 50,
"otherNightReminder": "The Dreamer points to a player. Show 1 good and 1 evil character token; one of these is correct.",
"reminders": [],
"setup": false,
@ -789,7 +789,7 @@
"name": "Snake Charmer",
"edition": "snv",
"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.",
"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.",
@ -802,7 +802,7 @@
"name": "Mathematician",
"edition": "snv",
"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.",
"otherNight": 62,
"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",
"firstNight": 0,
"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).",
"reminders": ["Demon voted",
"Demon not voted"],
@ -831,7 +831,7 @@
"team": "townsfolk",
"firstNight": 0,
"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).",
"reminders": ["Minions not nominated",
"Minion nominated"],
@ -845,7 +845,7 @@
"team": "townsfolk",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 54,
"otherNight": 53,
"otherNightReminder": "Show the hand signal for the number (0, 1, 2, etc.) of dead evil players.",
"reminders": [],
"setup": false,
@ -869,9 +869,9 @@
"name": "Seamstress",
"edition": "snv",
"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.",
"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.",
"reminders": ["No ability"],
"setup": false,
@ -911,7 +911,7 @@
"team": "townsfolk",
"firstNight": 0,
"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.",
"reminders": ["Correct"],
"setup": false,
@ -924,7 +924,7 @@
"team": "townsfolk",
"firstNight": 0,
"firstNightReminder": "",
"otherNight": 38,
"otherNight": 39,
"otherNightReminder": "If the Sage was killed by a Demon: Point to two players, one of which is that Demon.",
"reminders": [],
"setup": false,
@ -987,7 +987,7 @@
"name": "Evil Twin",
"edition": "snv",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -1000,7 +1000,7 @@
"name": "Witch",
"edition": "snv",
"team": "minion",
"firstNight": 20,
"firstNight": 21,
"firstNightReminder": "The Witch points to a player. If that player nominates tomorrow they die immediately.",
"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.",
@ -1013,7 +1013,7 @@
"name": "Cerenovus",
"edition": "snv",
"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.",
"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.",
@ -1161,7 +1161,7 @@
"name": "Bounty Hunter",
"edition": "",
"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.",
"otherNight": 58,
"otherNightReminder": "If the known evil player has died, point to another evil player. ",
@ -1174,7 +1174,7 @@
"name": "Pixie",
"edition": "",
"team": "townsfolk",
"firstNight": 24,
"firstNight": 25,
"firstNightReminder": "Show the Pixie 1 in-play Townsfolk character token.",
"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.",
@ -1188,7 +1188,7 @@
"name": "General",
"edition": "",
"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.",
"otherNight": 61,
"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",
"edition": "",
"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.",
"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.",
@ -1209,14 +1209,27 @@
"setup": false,
"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",
"name": "Balloonist",
"edition": "",
"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.",
"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.",
"reminders": ["Seen Townsfolk",
"Seen Outsider",
@ -1231,7 +1244,7 @@
"name": "Cult Leader",
"edition": "",
"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.",
"otherNight": 59,
"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",
"edition": "",
"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.",
"otherNight": 5,
"otherNightReminder": "If the Amnesiac's ability causes them to wake tonight: Wake the Amnesiac and run their ability.",
@ -1278,6 +1291,19 @@
"setup": false,
"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",
"name": "Poppy Grower",
@ -1362,7 +1388,7 @@
"name": "Widow",
"edition": "",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -1389,7 +1415,7 @@
"name": "Mephit",
"edition": "",
"team": "minion",
"firstNight": 22,
"firstNight": 23,
"firstNightReminder": "Show the Mephit their secret word.",
"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.",
@ -1403,7 +1429,7 @@
"name": "Marionette",
"edition": "",
"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.",
"otherNight": 0,
"otherNightReminder": "",
@ -1417,7 +1443,7 @@
"name": "Lil Monsta",
"edition": "",
"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.",
"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.",
@ -1446,7 +1472,7 @@
"name": "Leviathan",
"edition": "",
"team": "demon",
"firstNight": 44,
"firstNight": 45,
"firstNightReminder": "Place the Leviathan 'Day 1' marker. Announce 'The Leviathan is in play; this is Day 1.'",
"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.'.",
@ -1458,6 +1484,19 @@
"Good player executed"],
"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."
},
{
"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
*/
setCustomRoles(state, roles) {
state.roles = new Map(
roles
// replace numerical role object keys with matching key names
.map(role => {
if (role[0]) {
const customKeys = Object.keys(customRole);
const mappedRole = {};
for (let prop in role) {
if (customKeys[prop]) {
mappedRole[customKeys[prop]] = role[prop];
}
const processedRoles = roles
// replace numerical role object keys with matching key names
.map(role => {
if (role[0]) {
const customKeys = Object.keys(customRole);
const mappedRole = {};
for (let prop in role) {
if (customKeys[prop]) {
mappedRole[customKeys[prop]] = role[prop];
}
return mappedRole;
} else {
return role;
}
})
// clean up role.id
.map(role => {
role.id = role.id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
return mappedRole;
} else {
return role;
})
// map existing roles to base definition or pre-populate custom roles to ensure all properties
.map(
role =>
rolesJSONbyId.get(role.id) ||
state.roles.get(role.id) ||
Object.assign({}, customRole, role)
)
// default empty icons and placeholders
.map(role => {
if (rolesJSONbyId.get(role.id)) return role;
role.imageAlt = // map team to generic icon
{
townsfolk: "good",
outsider: "outsider",
minion: "minion",
demon: "evil"
}[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
}
})
// clean up role.id
.map(role => {
role.id = role.id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
return role;
})
// map existing roles to base definition or pre-populate custom roles to ensure all properties
.map(
role =>
rolesJSONbyId.get(role.id) ||
state.roles.get(role.id) ||
Object.assign({}, customRole, role)
)
// default empty icons and placeholders
.map(role => {
if (rolesJSONbyId.get(role.id)) return role;
role.imageAlt = // map team to generic icon
{
townsfolk: "good",
outsider: "outsider",
minion: "minion",
demon: "evil",
fabled: "fabled"
}[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])
);
// 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
state.otherTravelers = new Map(
rolesJSON

View File

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

View File

@ -278,7 +278,7 @@ class LiveSession {
votingSpeed: session.votingSpeed,
lockedVote: session.lockedVote,
isVoteInProgress: session.isVoteInProgress,
fabled: fabled.map(({ id }) => id),
fabled: fabled.map(f => (f.isCustom ? f : { id: f.id })),
...(session.nomination ? { votes: session.votes } : {})
});
}
@ -356,7 +356,7 @@ class LiveSession {
isVoteInProgress
});
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;
this._send(
"fabled",
fabled.map(({ id }) => id)
fabled.map(f => (f.isCustom ? f : { id: f.id }))
);
}
@ -427,7 +427,7 @@ class LiveSession {
_updateFabled(fabled) {
if (!this._isSpectator) return;
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)
});
}