mirror of https://github.com/bra1n/townsquare.git
Merge pull request #171 from bra1n/jinxes
show jinxed interactions on character reference modal
This commit is contained in:
commit
2d98f44d14
|
@ -6,6 +6,7 @@
|
|||
- added global animation toggle for better performance
|
||||
- added record vote history toggle to session menu, and clear vote history button
|
||||
- add support for custom Fabled characters
|
||||
- show Jinxed interactions on character reference list
|
||||
- add 'marked for execution' indicator
|
||||
|
||||
---
|
||||
|
|
|
@ -50,6 +50,40 @@
|
|||
<li :class="[team]"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="team jinxed" v-if="jinxed.length">
|
||||
<aside>
|
||||
<h4>Jinxed</h4>
|
||||
</aside>
|
||||
<ul>
|
||||
<li v-for="(jinx, index) in jinxed" :key="index">
|
||||
<span
|
||||
class="icon"
|
||||
:style="{
|
||||
backgroundImage: `url(${require('../../assets/icons/' +
|
||||
jinx.first.id +
|
||||
'.png')})`
|
||||
}"
|
||||
></span>
|
||||
<span
|
||||
class="icon"
|
||||
:style="{
|
||||
backgroundImage: `url(${require('../../assets/icons/' +
|
||||
jinx.second.id +
|
||||
'.png')})`
|
||||
}"
|
||||
></span>
|
||||
<div class="role">
|
||||
<span class="name"
|
||||
>{{ jinx.first.name }} & {{ jinx.second.name }}</span
|
||||
>
|
||||
<span class="ability">{{ jinx.reason }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
|
@ -62,6 +96,27 @@ export default {
|
|||
Modal
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Return a list of jinxes in the form of role IDs and a reason
|
||||
* @returns {*[]} [{first, second, reason}]
|
||||
*/
|
||||
jinxed: function() {
|
||||
const jinxed = [];
|
||||
this.roles.forEach(role => {
|
||||
if (this.jinxes.get(role.id)) {
|
||||
this.jinxes.get(role.id).forEach((reason, second) => {
|
||||
if (this.roles.get(second)) {
|
||||
jinxed.push({
|
||||
first: role,
|
||||
second: this.roles.get(second),
|
||||
reason
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return jinxed;
|
||||
},
|
||||
rolesGrouped: function() {
|
||||
const rolesGrouped = {};
|
||||
this.roles.forEach(role => {
|
||||
|
@ -85,7 +140,7 @@ export default {
|
|||
});
|
||||
return players;
|
||||
},
|
||||
...mapState(["roles", "modals", "edition", "grimoire"]),
|
||||
...mapState(["roles", "modals", "edition", "grimoire", "jinxes"]),
|
||||
...mapState("players", ["players"])
|
||||
},
|
||||
methods: {
|
||||
|
@ -147,6 +202,15 @@ h3 {
|
|||
}
|
||||
}
|
||||
|
||||
.jinxed {
|
||||
.name {
|
||||
color: $fabled;
|
||||
}
|
||||
aside {
|
||||
background: linear-gradient(-90deg, $fabled, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.team {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
@ -180,6 +244,12 @@ h3 {
|
|||
transform-origin: center;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
&.jinxed {
|
||||
.icon {
|
||||
margin: 0 -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
[{
|
||||
"id": "Chambermaid",
|
||||
"hatred": [{
|
||||
"id": "Mathematician",
|
||||
"reason": "The Chambermaid learns if the Mathematician wakes tonight or not, even though the Chambermaid wakes first."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Butler",
|
||||
"hatred": [{
|
||||
"id": "Cannibal",
|
||||
"reason": "If the Cannibal gains the Butler ability, the Cannibal learns this."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Mutant",
|
||||
"hatred": [{
|
||||
"id": "Undertaker",
|
||||
"reason": "If the Mutant causes a second execution, the Undertaker learns either one or both executed characters (Storyteller's choice)."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Lunatic",
|
||||
"hatred": [{
|
||||
"id": "Mathematician",
|
||||
"reason": "The Mathematician learns if the Lunatic attacks a different player(s) than the real Demon attacked."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Pit-Hag",
|
||||
"hatred": [{
|
||||
"id": "Politician",
|
||||
"reason": "A Pit-Hag can not create an evil Politician."
|
||||
},
|
||||
{
|
||||
"id": "Heretic",
|
||||
"reason": "A Pit-Hag can not create a Heretic. "
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"id": "Cerenovus",
|
||||
"hatred": [{
|
||||
"id": "Undertaker",
|
||||
"reason": "If the Cerenovus causes a second execution, the Undertaker learns either one or both executed characters (Storyteller's choice)."
|
||||
},
|
||||
{
|
||||
"id": "Goblin",
|
||||
"reason": "The Cerenovus may choose to make a player mad that they are the Goblin."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Leviathan",
|
||||
"hatred": [{
|
||||
"id": "Soldier",
|
||||
"reason": "If Leviathan nominates and executes the Soldier, the Soldier does not die."
|
||||
},
|
||||
{
|
||||
"id": "Monk",
|
||||
"reason": "If Leviathan nominates and executes the player the Monk chose, that player does not die."
|
||||
},
|
||||
{
|
||||
"id": "Innkeeper",
|
||||
"reason": "If Leviathan nominates and executes a player the Innkeeper chose, that player does not die."
|
||||
},
|
||||
{
|
||||
"id": "Ravenkeeper",
|
||||
"reason": "If Leviathan is in play & the Ravenkeeper dies by execution, they wake that night to use their ability."
|
||||
},
|
||||
{
|
||||
"id": "Sage",
|
||||
"reason": "If Leviathan is in play & the Sage dies by execution, they wake that night to use their ability."
|
||||
},
|
||||
{
|
||||
"id": "Mayor",
|
||||
"reason": "If Leviathan is in play & no execution occurs on day 5, good wins."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Lil' Monsta",
|
||||
"hatred": [{
|
||||
"id": "Scarlet Woman",
|
||||
"reason": "If there are 5 or more players alive and the player holding the Lil' Monsta token dies, the Scarlet Woman is given the Lil' Monsta token tonight."
|
||||
},
|
||||
{
|
||||
"id": "Poppy Grower",
|
||||
"reason": "If the Poppy Grower is in play, Minions don't wake together. They are woken one by one, until one of them chooses to take the Lil' Monsta token."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Lycanthrope",
|
||||
"hatred": [{
|
||||
"id": "Gambler",
|
||||
"reason": "If the Lycanthrope is alive and the Gambler kills themself at night, no other players can die tonight."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Legion",
|
||||
"hatred": [{
|
||||
"id": "Preacher",
|
||||
"reason": "Only 1 jinxed character can be in play. Evil players start knowing which player and character it is."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Fang Gu",
|
||||
"hatred": [{
|
||||
"id": "Scarlet Woman",
|
||||
"reason": "If the Fang Gu chooses an Outsider and dies, the Scarlet Woman does not become the Fang Gu."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Spy",
|
||||
"hatred": [{
|
||||
"id": "Poppy Grower",
|
||||
"reason": "If the Poppy Grower is in play, the Spy does not see the Grimoire until the Poppy Grower dies."
|
||||
},
|
||||
{
|
||||
"id": "Heretic",
|
||||
"reason": "Only 1 jinxed character can be in play."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Widow",
|
||||
"hatred": [{
|
||||
"id": "Poppy Grower",
|
||||
"reason": "If the Poppy Grower is in play, the Widow does not see the Grimoire until the Poppy Grower dies."
|
||||
},
|
||||
{
|
||||
"id": "Heretic",
|
||||
"reason": "Only 1 jinxed character can be in play."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Godfather",
|
||||
"hatred": [{
|
||||
"id": "Heretic",
|
||||
"reason": "Only 1 jinxed character can be in play."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "Marionette",
|
||||
"hatred": [
|
||||
{
|
||||
"id": "Lil' Monsta",
|
||||
"reason": "The Marionette neighbors a Minion, not the Demon. The Marionette is not woken to choose who takes the Lil' Monsta token."
|
||||
},
|
||||
{
|
||||
"id": "Poppy Grower",
|
||||
"reason": "When the Poppy Grower dies, the Demon learns the Marionette but the Marionette learns nothing."
|
||||
},
|
||||
{
|
||||
"id": "Balloonist",
|
||||
"reason": "If the Marionette thinks that they are the Balloonist, +1 Outsider was added."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -7,16 +7,10 @@ import session from "./modules/session";
|
|||
import editionJSON from "../editions.json";
|
||||
import rolesJSON from "../roles.json";
|
||||
import fabledJSON from "../fabled.json";
|
||||
import jinxesJSON from "../hatred.json";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
// global data maps
|
||||
const editionJSONbyId = new Map(
|
||||
editionJSON.map(edition => [edition.id, edition])
|
||||
);
|
||||
const rolesJSONbyId = new Map(rolesJSON.map(role => [role.id, role]));
|
||||
const fabled = new Map(fabledJSON.map(role => [role.id, role]));
|
||||
|
||||
// helper functions
|
||||
const getRolesByEdition = (edition = editionJSON[0]) => {
|
||||
return new Map(
|
||||
|
@ -52,6 +46,33 @@ const toggle = key => ({ grimoire }, val) => {
|
|||
}
|
||||
};
|
||||
|
||||
const clean = id => id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
|
||||
|
||||
// global data maps
|
||||
const editionJSONbyId = new Map(
|
||||
editionJSON.map(edition => [edition.id, edition])
|
||||
);
|
||||
const rolesJSONbyId = new Map(rolesJSON.map(role => [role.id, role]));
|
||||
const fabled = new Map(fabledJSON.map(role => [role.id, role]));
|
||||
|
||||
// jinxes
|
||||
let jinxes = {};
|
||||
try {
|
||||
// Note: can't fetch live list due to lack of CORS headers
|
||||
// fetch("https://bloodontheclocktower.com/script/data/hatred.json")
|
||||
// .then(res => res.json())
|
||||
// .then(jinxesJSON => {
|
||||
jinxes = new Map(
|
||||
jinxesJSON.map(({ id, hatred }) => [
|
||||
clean(id),
|
||||
new Map(hatred.map(({ id, reason }) => [clean(id), reason]))
|
||||
])
|
||||
);
|
||||
// });
|
||||
} catch (e) {
|
||||
console.error("couldn't load jinxes", e);
|
||||
}
|
||||
|
||||
// base definition for custom roles
|
||||
const customRole = {
|
||||
id: "",
|
||||
|
@ -101,7 +122,8 @@ export default new Vuex.Store({
|
|||
edition: editionJSONbyId.get("tb"),
|
||||
roles: getRolesByEdition(),
|
||||
otherTravelers: getTravelersNotInEdition(),
|
||||
fabled
|
||||
fabled,
|
||||
jinxes
|
||||
},
|
||||
getters: {
|
||||
/**
|
||||
|
@ -182,7 +204,7 @@ export default new Vuex.Store({
|
|||
})
|
||||
// clean up role.id
|
||||
.map(role => {
|
||||
role.id = role.id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
|
||||
role.id = clean(role.id);
|
||||
return role;
|
||||
})
|
||||
// map existing roles to base definition or pre-populate custom roles to ensure all properties
|
||||
|
|
Loading…
Reference in New Issue