mirror of https://github.com/bra1n/townsquare.git
Merge de29b3845f
into d9c2b17dc9
This commit is contained in:
commit
ec0d45e179
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -31,7 +31,7 @@
|
||||||
### Version 2.15.2
|
### Version 2.15.2
|
||||||
- added mobile web application support
|
- added mobile web application support
|
||||||
- show correct number of leaves on roles with global reminders
|
- show correct number of leaves on roles with global reminders
|
||||||
- fixed a bug with traveler list showing up when assigning demon bluffs
|
- fixed a bug with traveller list showing up when assigning demon bluffs
|
||||||
- fixed a bug with homebrew scripts that contained negative night order positions
|
- fixed a bug with homebrew scripts that contained negative night order positions
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -130,14 +130,14 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 2.5.0
|
## Version 2.5.0
|
||||||
- all travelers from the base editions are now optionally available (thanks @davotronic5000)
|
- all travellers from the base editions are now optionally available (thanks @davotronic5000)
|
||||||
- night order shows player names near roles now
|
- night order shows player names near roles now
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 2.4.0
|
## Version 2.4.0
|
||||||
- added spoiler role (Pixie!)
|
- added spoiler role (Pixie!)
|
||||||
- fixed bug with ST sending out roles that are not part of the current edition / script (ie. travelers or base set roles)
|
- fixed bug with ST sending out roles that are not part of the current edition / script (ie. travellers or base set roles)
|
||||||
- better Lycanthrope icon (thanks @AWConant)
|
- better Lycanthrope icon (thanks @AWConant)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -187,20 +187,20 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 2.0.4
|
## Version 2.0.4
|
||||||
- fix bug with live sessions that contain travelers from a different set
|
- fix bug with live sessions that contain travellers from a different set
|
||||||
- fix server channel cleanup
|
- fix server channel cleanup
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 2.0.3
|
## Version 2.0.3
|
||||||
- load roles that belong to different editions (like travelers) from gamestate
|
- load roles that belong to different editions (like travellers) from gamestate
|
||||||
- close session when missing custom roles and open edition modal
|
- close session when missing custom roles and open edition modal
|
||||||
- added a few more metrics
|
- added a few more metrics
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version 2.0.2
|
## Version 2.0.2
|
||||||
- fix nomination history type not detecting travelers
|
- fix nomination history type not detecting travellers
|
||||||
- fix live session domain whitelist
|
- fix live session domain whitelist
|
||||||
- fix build path
|
- fix build path
|
||||||
- fix changelog version numbering
|
- fix changelog version numbering
|
||||||
|
|
|
@ -22,7 +22,7 @@ If you want to learn more about how to use the app as a player, [JayBotC](https:
|
||||||
- Public Town Square and Storyteller Grimoire (toggle with **shortcut \[G\]**)
|
- Public Town Square and Storyteller Grimoire (toggle with **shortcut \[G\]**)
|
||||||
- Supports custom script JSON generated by the [Script Tool](https://bloodontheclocktower.com/script)
|
- Supports custom script JSON generated by the [Script Tool](https://bloodontheclocktower.com/script)
|
||||||
- Live Session for Storyteller / Players including live voting and character distribution!
|
- Live Session for Storyteller / Players including live voting and character distribution!
|
||||||
- Includes all 3 base editions, Travelers and Fabled plus all officially spoiled characters so far!
|
- Includes all 3 base editions, Travellers and Fabled plus all officially spoiled characters so far!
|
||||||
- Night sheet and reminder text for each character ability to help storytellers
|
- Night sheet and reminder text for each character ability to help storytellers
|
||||||
- Full homebrew support for hosting and playing games with your own sets of characters
|
- Full homebrew support for hosting and playing games with your own sets of characters
|
||||||
- Many other customization options!
|
- Many other customization options!
|
||||||
|
@ -99,7 +99,7 @@ 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`, `traveler` or `fabled`<br>
|
- **team**: the team of the character, has to be one of `townsfolk`, `outsider`, `minion`, `demon`, `traveller` 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
|
_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
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.traveler .life {
|
&.traveller .life {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,13 +487,13 @@ export default {
|
||||||
transform: perspective(400px) rotateY(0deg);
|
transform: perspective(400px) rotateY(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.traveler:not(.dead) .token {
|
&.traveller:not(.dead) .token {
|
||||||
transform: perspective(400px) scale(0.8);
|
transform: perspective(400px) scale(0.8);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition-delay: 0s;
|
transition-delay: 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.traveler.dead .token {
|
&.traveller.dead .token {
|
||||||
transition-delay: 0s;
|
transition-delay: 0s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -636,7 +636,7 @@ li.move:not(.from) .player .overlay svg.move {
|
||||||
@include glow("outsider", $outsider);
|
@include glow("outsider", $outsider);
|
||||||
@include glow("demon", $demon);
|
@include glow("demon", $demon);
|
||||||
@include glow("minion", $minion);
|
@include glow("minion", $minion);
|
||||||
@include glow("traveler", $traveler);
|
@include glow("traveller", $traveller);
|
||||||
|
|
||||||
.player.you .token {
|
.player.you .token {
|
||||||
animation: townsfolk-glow 5s ease-in-out infinite;
|
animation: townsfolk-glow 5s ease-in-out infinite;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
})`
|
})`
|
||||||
}"
|
}"
|
||||||
></li>
|
></li>
|
||||||
<li v-if="players.length - teams.traveler < 5">
|
<li v-if="players.length - teams.traveller < 5">
|
||||||
Please add more players!
|
Please add more players!
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
{{ teams.votes }} <font-awesome-icon class="votes" icon="vote-yea" />
|
{{ teams.votes }} <font-awesome-icon class="votes" icon="vote-yea" />
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="players.length - teams.traveler >= 5">
|
<li v-if="players.length - teams.traveller >= 5">
|
||||||
<span>
|
<span>
|
||||||
{{ teams.townsfolk }}
|
{{ teams.townsfolk }}
|
||||||
<font-awesome-icon class="townsfolk" icon="user-friends" />
|
<font-awesome-icon class="townsfolk" icon="user-friends" />
|
||||||
|
@ -56,11 +56,11 @@
|
||||||
:icon="teams.demon > 1 ? 'user-friends' : 'user'"
|
:icon="teams.demon > 1 ? 'user-friends' : 'user'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="teams.traveler">
|
<span v-if="teams.traveller">
|
||||||
{{ teams.traveler }}
|
{{ teams.traveller }}
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
class="traveler"
|
class="traveller"
|
||||||
:icon="teams.traveler > 1 ? 'user-friends' : 'user'"
|
:icon="teams.traveller > 1 ? 'user-friends' : 'user'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="grimoire.isNight">
|
<span v-if="grimoire.isNight">
|
||||||
|
@ -79,11 +79,11 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
teams: function() {
|
teams: function() {
|
||||||
const { players } = this.$store.state.players;
|
const { players } = this.$store.state.players;
|
||||||
const nonTravelers = this.$store.getters["players/nonTravelers"];
|
const nonTravellers = this.$store.getters["players/nonTravellers"];
|
||||||
const alive = players.filter(player => player.isDead !== true).length;
|
const alive = players.filter(player => player.isDead !== true).length;
|
||||||
return {
|
return {
|
||||||
...gameJSON[nonTravelers - 5],
|
...gameJSON[nonTravellers - 5],
|
||||||
traveler: players.length - nonTravelers,
|
traveller: players.length - nonTravellers,
|
||||||
alive,
|
alive,
|
||||||
votes:
|
votes:
|
||||||
alive +
|
alive +
|
||||||
|
@ -160,8 +160,8 @@ export default {
|
||||||
.demon {
|
.demon {
|
||||||
color: $demon;
|
color: $demon;
|
||||||
}
|
}
|
||||||
.traveler {
|
.traveller {
|
||||||
color: $traveler;
|
color: $traveller;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ export default {
|
||||||
},
|
},
|
||||||
openRoleModal(playerIndex) {
|
openRoleModal(playerIndex) {
|
||||||
const player = this.players[playerIndex];
|
const player = this.players[playerIndex];
|
||||||
if (this.session.isSpectator && player && player.role.team === "traveler")
|
if (this.session.isSpectator && player && player.role.team === "traveller")
|
||||||
return;
|
return;
|
||||||
this.selectedPlayer = playerIndex;
|
this.selectedPlayer = playerIndex;
|
||||||
this.$store.commit("toggleModal", "role");
|
this.$store.commit("toggleModal", "role");
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
{{ voters.length }} vote{{ voters.length !== 1 ? "s" : "" }}
|
{{ voters.length }} vote{{ voters.length !== 1 ? "s" : "" }}
|
||||||
</em>
|
</em>
|
||||||
in favor
|
in favor
|
||||||
<em v-if="nominee.role.team !== 'traveler'">
|
<em v-if="nominee.role.team !== 'traveller'">
|
||||||
(majority is {{ Math.ceil(alive / 2) }})
|
(majority is {{ Math.ceil(alive / 2) }})
|
||||||
</em>
|
</em>
|
||||||
<em v-else>(majority is {{ Math.ceil(players.length / 2) }})</em>
|
<em v-else>(majority is {{ Math.ceil(players.length / 2) }})</em>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
</template>
|
</template>
|
||||||
<div class="button demon" @click="finish">Close</div>
|
<div class="button demon" @click="finish">Close</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-group mark" v-if="nominee.role.team !== 'traveler'">
|
<div class="button-group mark" v-if="nominee.role.team !== 'traveller'">
|
||||||
<div
|
<div
|
||||||
class="button"
|
class="button"
|
||||||
:class="{
|
:class="{
|
||||||
|
@ -155,7 +155,7 @@ export default {
|
||||||
},
|
},
|
||||||
canVote: function() {
|
canVote: function() {
|
||||||
if (!this.player) return false;
|
if (!this.player) return false;
|
||||||
if (this.player.isVoteless && this.nominee.role.team !== "traveler")
|
if (this.player.isVoteless && this.nominee.role.team !== "traveller")
|
||||||
return false;
|
return false;
|
||||||
const session = this.session;
|
const session = this.session;
|
||||||
const players = this.players.length;
|
const players = this.players.length;
|
||||||
|
|
|
@ -137,7 +137,7 @@ export default {
|
||||||
}
|
}
|
||||||
this.roles.forEach(role => {
|
this.roles.forEach(role => {
|
||||||
const players = this.players.filter(p => p.role.id === role.id);
|
const players = this.players.filter(p => p.role.id === role.id);
|
||||||
if (role.firstNight && (role.team !== "traveler" || players.length)) {
|
if (role.firstNight && (role.team !== "traveller" || players.length)) {
|
||||||
rolesFirstNight.push(Object.assign({ players }, role));
|
rolesFirstNight.push(Object.assign({ players }, role));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -153,7 +153,7 @@ export default {
|
||||||
const rolesOtherNight = [];
|
const rolesOtherNight = [];
|
||||||
this.roles.forEach(role => {
|
this.roles.forEach(role => {
|
||||||
const players = this.players.filter(p => p.role.id === role.id);
|
const players = this.players.filter(p => p.role.id === role.id);
|
||||||
if (role.otherNight && (role.team !== "traveler" || players.length)) {
|
if (role.otherNight && (role.team !== "traveller" || players.length)) {
|
||||||
rolesOtherNight.push(Object.assign({ players }, role));
|
rolesOtherNight.push(Object.assign({ players }, role));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -125,13 +125,13 @@ export default {
|
||||||
}
|
}
|
||||||
rolesGrouped[role.team].push(role);
|
rolesGrouped[role.team].push(role);
|
||||||
});
|
});
|
||||||
delete rolesGrouped["traveler"];
|
delete rolesGrouped["traveller"];
|
||||||
return rolesGrouped;
|
return rolesGrouped;
|
||||||
},
|
},
|
||||||
playersByRole: function() {
|
playersByRole: function() {
|
||||||
const players = {};
|
const players = {};
|
||||||
this.players.forEach(({ name, role }) => {
|
this.players.forEach(({ name, role }) => {
|
||||||
if (role && role.id && role.team !== "traveler") {
|
if (role && role.id && role.team !== "traveller") {
|
||||||
if (!players[role.id]) {
|
if (!players[role.id]) {
|
||||||
players[role.id] = [];
|
players[role.id] = [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,8 @@ export default {
|
||||||
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
|
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
|
||||||
});
|
});
|
||||||
|
|
||||||
// add out of script traveler reminders
|
// add out of script traveller reminders
|
||||||
this.$store.state.otherTravelers.forEach(role => {
|
this.$store.state.otherTravellers.forEach(role => {
|
||||||
if (players.some(p => p.role.id === role.id)) {
|
if (players.some(p => p.role.id === role.id)) {
|
||||||
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
|
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
: "bluffing"
|
: "bluffing"
|
||||||
}}
|
}}
|
||||||
</h3>
|
</h3>
|
||||||
<ul class="tokens" v-if="tab === 'editionRoles' || !otherTravelers.size">
|
<ul class="tokens" v-if="tab === 'editionRoles' || !otherTravellers.size">
|
||||||
<li
|
<li
|
||||||
v-for="role in availableRoles"
|
v-for="role in availableRoles"
|
||||||
:class="[role.team]"
|
:class="[role.team]"
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
<Token :role="role" />
|
<Token :role="role" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="tokens" v-if="tab === 'otherTravelers' && otherTravelers.size">
|
<ul class="tokens" v-if="tab === 'otherTravellers' && otherTravellers.size">
|
||||||
<li
|
<li
|
||||||
v-for="role in otherTravelers.values()"
|
v-for="role in otherTravellers.values()"
|
||||||
:class="[role.team]"
|
:class="[role.team]"
|
||||||
:key="role.id"
|
:key="role.id"
|
||||||
@click="setRole(role)"
|
@click="setRole(role)"
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<div
|
<div
|
||||||
class="button-group"
|
class="button-group"
|
||||||
v-if="playerIndex >= 0 && otherTravelers.size && !session.isSpectator"
|
v-if="playerIndex >= 0 && otherTravellers.size && !session.isSpectator"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="button"
|
class="button"
|
||||||
|
@ -40,9 +40,9 @@
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="button"
|
class="button"
|
||||||
:class="{ townsfolk: tab === 'otherTravelers' }"
|
:class="{ townsfolk: tab === 'otherTravellers' }"
|
||||||
@click="tab = 'otherTravelers'"
|
@click="tab = 'otherTravellers'"
|
||||||
>Other Travelers</span
|
>Other Travellers</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -75,7 +75,7 @@ export default {
|
||||||
},
|
},
|
||||||
...mapState(["modals", "roles", "session"]),
|
...mapState(["modals", "roles", "session"]),
|
||||||
...mapState("players", ["players"]),
|
...mapState("players", ["players"]),
|
||||||
...mapState(["otherTravelers"])
|
...mapState(["otherTravellers"])
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -91,7 +91,7 @@ export default {
|
||||||
role
|
role
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (this.session.isSpectator && role.team === "traveler") return;
|
if (this.session.isSpectator && role.team === "traveller") return;
|
||||||
// assign to player
|
// assign to player
|
||||||
const player = this.$store.state.players.players[this.playerIndex];
|
const player = this.$store.state.players.players[this.playerIndex];
|
||||||
this.$store.commit("players/update", {
|
this.$store.commit("players/update", {
|
||||||
|
@ -133,8 +133,8 @@ ul.tokens li {
|
||||||
&.demon {
|
&.demon {
|
||||||
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
||||||
}
|
}
|
||||||
&.traveler {
|
&.traveller {
|
||||||
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
|
box-shadow: 0 0 10px $traveller, 0 0 10px $traveller;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
|
@ -142,7 +142,7 @@ ul.tokens li {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#townsquare.spectator ul.tokens li.traveler {
|
#townsquare.spectator ul.tokens li.traveller {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
class="roles"
|
class="roles"
|
||||||
v-if="modals.roles && nonTravelers >= 5"
|
v-if="modals.roles && nonTravellers >= 5"
|
||||||
@close="toggleModal('roles')"
|
@close="toggleModal('roles')"
|
||||||
>
|
>
|
||||||
<h3>Select the characters for {{ nonTravelers }} players:</h3>
|
<h3>Select the characters for {{ nonTravellers }} players:</h3>
|
||||||
<ul class="tokens" v-for="(teamRoles, team) in roleSelection" :key="team">
|
<ul class="tokens" v-for="(teamRoles, team) in roleSelection" :key="team">
|
||||||
<li class="count" :class="[team]">
|
<li class="count" :class="[team]">
|
||||||
{{ teamRoles.reduce((a, { selected }) => a + selected, 0) }} /
|
{{ teamRoles.reduce((a, { selected }) => a + selected, 0) }} /
|
||||||
{{ game[nonTravelers - 5][team] }}
|
{{ game[nonTravellers - 5][team] }}
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-for="role in teamRoles"
|
v-for="role in teamRoles"
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
class="button"
|
class="button"
|
||||||
@click="assignRoles"
|
@click="assignRoles"
|
||||||
:class="{
|
:class="{
|
||||||
disabled: selectedRoles > nonTravelers || !selectedRoles
|
disabled: selectedRoles > nonTravellers || !selectedRoles
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="people-arrows" />
|
<font-awesome-icon icon="people-arrows" />
|
||||||
|
@ -92,7 +92,7 @@ export default {
|
||||||
},
|
},
|
||||||
...mapState(["roles", "modals"]),
|
...mapState(["roles", "modals"]),
|
||||||
...mapState("players", ["players"]),
|
...mapState("players", ["players"]),
|
||||||
...mapGetters({ nonTravelers: "players/nonTravelers" })
|
...mapGetters({ nonTravellers: "players/nonTravellers" })
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
selectRandomRoles() {
|
selectRandomRoles() {
|
||||||
|
@ -104,8 +104,8 @@ export default {
|
||||||
this.roleSelection[role.team].push(role);
|
this.roleSelection[role.team].push(role);
|
||||||
this.$set(role, "selected", 0);
|
this.$set(role, "selected", 0);
|
||||||
});
|
});
|
||||||
delete this.roleSelection["traveler"];
|
delete this.roleSelection["traveller"];
|
||||||
const playerCount = Math.max(5, this.nonTravelers);
|
const playerCount = Math.max(5, this.nonTravellers);
|
||||||
const composition = this.game[playerCount - 5];
|
const composition = this.game[playerCount - 5];
|
||||||
Object.keys(composition).forEach(team => {
|
Object.keys(composition).forEach(team => {
|
||||||
for (let x = 0; x < composition[team]; x++) {
|
for (let x = 0; x < composition[team]; x++) {
|
||||||
|
@ -121,7 +121,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
assignRoles() {
|
assignRoles() {
|
||||||
if (this.selectedRoles <= this.nonTravelers && this.selectedRoles) {
|
if (this.selectedRoles <= this.nonTravellers && this.selectedRoles) {
|
||||||
// generate list of selected roles and randomize it
|
// generate list of selected roles and randomize it
|
||||||
const roles = Object.values(this.roleSelection)
|
const roles = Object.values(this.roleSelection)
|
||||||
.map(roles =>
|
.map(roles =>
|
||||||
|
@ -135,7 +135,7 @@ export default {
|
||||||
.sort((a, b) => a[0] - b[0])
|
.sort((a, b) => a[0] - b[0])
|
||||||
.map(a => a[1]);
|
.map(a => a[1]);
|
||||||
this.players.forEach(player => {
|
this.players.forEach(player => {
|
||||||
if (player.role.team !== "traveler" && roles.length) {
|
if (player.role.team !== "traveller" && roles.length) {
|
||||||
const value = roles.pop();
|
const value = roles.pop();
|
||||||
this.$store.commit("players/update", {
|
this.$store.commit("players/update", {
|
||||||
player,
|
player,
|
||||||
|
@ -194,8 +194,8 @@ ul.tokens {
|
||||||
&.demon {
|
&.demon {
|
||||||
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
||||||
}
|
}
|
||||||
&.traveler {
|
&.traveller {
|
||||||
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
|
box-shadow: 0 0 10px $traveller, 0 0 10px $traveller;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"id": "bmr",
|
"id": "bmr",
|
||||||
"name": "Bad Moon Rising",
|
"name": "Bad Moon Rising",
|
||||||
"author": "The Pandemonium Institute",
|
"author": "The Pandemonium Institute",
|
||||||
"description": "The sun is swallowed by a jagged horizon as another winter's day surrenders to the night. Flecks of orange and red decay into deeper browns, the forest transforming in silent anticipation of the coming snow.\n\nRavenous wolves howl from the bowels of a rocky crevasse beyond the town borders, sending birds scattering from their cozy rooks. Travelers hurry into the inn, seeking shelter from the gathering chill. They warm themselves with hot tea, sweet strains of music and hearty ale, unaware that strange and nefarious eyes stalk them from the ruins of this once great city.\n\nTonight, even the livestock know there is a... Bad Moon Rising.",
|
"description": "The sun is swallowed by a jagged horizon as another winter's day surrenders to the night. Flecks of orange and red decay into deeper browns, the forest transforming in silent anticipation of the coming snow.\n\nRavenous wolves howl from the bowels of a rocky crevasse beyond the town borders, sending birds scattering from their cozy rooks. Travellers hurry into the inn, seeking shelter from the gathering chill. They warm themselves with hot tea, sweet strains of music and hearty ale, unaware that strange and nefarious eyes stalk them from the ruins of this once great city.\n\nTonight, even the livestock know there is a... Bad Moon Rising.",
|
||||||
"level": "Intermediate",
|
"level": "Intermediate",
|
||||||
"roles": [],
|
"roles": [],
|
||||||
"isOfficial": true
|
"isOfficial": true
|
||||||
|
|
|
@ -293,7 +293,7 @@
|
||||||
"id": "bureaucrat",
|
"id": "bureaucrat",
|
||||||
"name": "Bureaucrat",
|
"name": "Bureaucrat",
|
||||||
"edition": "tb",
|
"edition": "tb",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 1,
|
"firstNight": 1,
|
||||||
"firstNightReminder": "The Bureaucrat points to a player. Put the Bureaucrat's '3 votes' reminder by the chosen player's character token.",
|
"firstNightReminder": "The Bureaucrat points to a player. Put the Bureaucrat's '3 votes' reminder by the chosen player's character token.",
|
||||||
"otherNight": 1,
|
"otherNight": 1,
|
||||||
|
@ -306,7 +306,7 @@
|
||||||
"id": "thief",
|
"id": "thief",
|
||||||
"name": "Thief",
|
"name": "Thief",
|
||||||
"edition": "tb",
|
"edition": "tb",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 1,
|
"firstNight": 1,
|
||||||
"firstNightReminder": "The Thief points to a player. Put the Thief's 'Negative vote' reminder by the chosen player's character token.",
|
"firstNightReminder": "The Thief points to a player. Put the Thief's 'Negative vote' reminder by the chosen player's character token.",
|
||||||
"otherNight": 1,
|
"otherNight": 1,
|
||||||
|
@ -319,7 +319,7 @@
|
||||||
"id": "gunslinger",
|
"id": "gunslinger",
|
||||||
"name": "Gunslinger",
|
"name": "Gunslinger",
|
||||||
"edition": "tb",
|
"edition": "tb",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -332,7 +332,7 @@
|
||||||
"id": "scapegoat",
|
"id": "scapegoat",
|
||||||
"name": "Scapegoat",
|
"name": "Scapegoat",
|
||||||
"edition": "tb",
|
"edition": "tb",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -345,7 +345,7 @@
|
||||||
"id": "beggar",
|
"id": "beggar",
|
||||||
"name": "Beggar",
|
"name": "Beggar",
|
||||||
"edition": "tb",
|
"edition": "tb",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -696,7 +696,7 @@
|
||||||
"id": "apprentice",
|
"id": "apprentice",
|
||||||
"name": "Apprentice",
|
"name": "Apprentice",
|
||||||
"edition": "bmr",
|
"edition": "bmr",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 1,
|
"firstNight": 1,
|
||||||
"firstNightReminder": "Show the Apprentice the 'You are' card, then a Townsfolk or Minion token. In the Grimoire, replace the Apprentice token with that character token, and put the Apprentice's 'Is the Apprentice' reminder by that character token.",
|
"firstNightReminder": "Show the Apprentice the 'You are' card, then a Townsfolk or Minion token. In the Grimoire, replace the Apprentice token with that character token, and put the Apprentice's 'Is the Apprentice' reminder by that character token.",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -709,7 +709,7 @@
|
||||||
"id": "matron",
|
"id": "matron",
|
||||||
"name": "Matron",
|
"name": "Matron",
|
||||||
"edition": "bmr",
|
"edition": "bmr",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -722,7 +722,7 @@
|
||||||
"id": "judge",
|
"id": "judge",
|
||||||
"name": "Judge",
|
"name": "Judge",
|
||||||
"edition": "bmr",
|
"edition": "bmr",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -735,7 +735,7 @@
|
||||||
"id": "bishop",
|
"id": "bishop",
|
||||||
"name": "Bishop",
|
"name": "Bishop",
|
||||||
"edition": "bmr",
|
"edition": "bmr",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -749,7 +749,7 @@
|
||||||
"id": "voudon",
|
"id": "voudon",
|
||||||
"name": "Voudon",
|
"name": "Voudon",
|
||||||
"edition": "bmr",
|
"edition": "bmr",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -1094,7 +1094,7 @@
|
||||||
"id": "barista",
|
"id": "barista",
|
||||||
"name": "Barista",
|
"name": "Barista",
|
||||||
"edition": "snv",
|
"edition": "snv",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 1,
|
"firstNight": 1,
|
||||||
"firstNightReminder": "Choose a player, wake them and tell them which Barista power is affecting them. Treat them accordingly (sober/healthy/true info or activate their ability twice).",
|
"firstNightReminder": "Choose a player, wake them and tell them which Barista power is affecting them. Treat them accordingly (sober/healthy/true info or activate their ability twice).",
|
||||||
"otherNight": 1,
|
"otherNight": 1,
|
||||||
|
@ -1108,7 +1108,7 @@
|
||||||
"id": "harlot",
|
"id": "harlot",
|
||||||
"name": "Harlot",
|
"name": "Harlot",
|
||||||
"edition": "snv",
|
"edition": "snv",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 1,
|
"otherNight": 1,
|
||||||
|
@ -1121,7 +1121,7 @@
|
||||||
"id": "butcher",
|
"id": "butcher",
|
||||||
"name": "Butcher",
|
"name": "Butcher",
|
||||||
"edition": "snv",
|
"edition": "snv",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -1134,7 +1134,7 @@
|
||||||
"id": "bonecollector",
|
"id": "bonecollector",
|
||||||
"name": "Bone Collector",
|
"name": "Bone Collector",
|
||||||
"edition": "snv",
|
"edition": "snv",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 1,
|
"otherNight": 1,
|
||||||
|
@ -1148,7 +1148,7 @@
|
||||||
"id": "deviant",
|
"id": "deviant",
|
||||||
"name": "Deviant",
|
"name": "Deviant",
|
||||||
"edition": "snv",
|
"edition": "snv",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
@ -1730,7 +1730,7 @@
|
||||||
"id": "gangster",
|
"id": "gangster",
|
||||||
"name": "Gangster",
|
"name": "Gangster",
|
||||||
"edition": "",
|
"edition": "",
|
||||||
"team": "traveler",
|
"team": "traveller",
|
||||||
"firstNight": 0,
|
"firstNight": 0,
|
||||||
"firstNightReminder": "",
|
"firstNightReminder": "",
|
||||||
"otherNight": 0,
|
"otherNight": 0,
|
||||||
|
|
|
@ -21,12 +21,12 @@ const getRolesByEdition = (edition = editionJSON[0]) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTravelersNotInEdition = (edition = editionJSON[0]) => {
|
const getTravellersNotInEdition = (edition = editionJSON[0]) => {
|
||||||
return new Map(
|
return new Map(
|
||||||
rolesJSON
|
rolesJSON
|
||||||
.filter(
|
.filter(
|
||||||
r =>
|
r =>
|
||||||
r.team === "traveler" &&
|
r.team === "traveller" &&
|
||||||
r.edition !== edition.id &&
|
r.edition !== edition.id &&
|
||||||
!edition.roles.includes(r.id)
|
!edition.roles.includes(r.id)
|
||||||
)
|
)
|
||||||
|
@ -121,7 +121,7 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
edition: editionJSONbyId.get("tb"),
|
edition: editionJSONbyId.get("tb"),
|
||||||
roles: getRolesByEdition(),
|
roles: getRolesByEdition(),
|
||||||
otherTravelers: getTravelersNotInEdition(),
|
otherTravellers: getTravellersNotInEdition(),
|
||||||
fabled,
|
fabled,
|
||||||
jinxes
|
jinxes
|
||||||
},
|
},
|
||||||
|
@ -244,10 +244,10 @@ export default new Vuex.Store({
|
||||||
...processedRoles.filter(r => r.team === "fabled").map(r => [r.id, r]),
|
...processedRoles.filter(r => r.team === "fabled").map(r => [r.id, r]),
|
||||||
...fabledJSON.map(role => [role.id, role])
|
...fabledJSON.map(role => [role.id, role])
|
||||||
]);
|
]);
|
||||||
// update extraTravelers map to only show travelers not in this script
|
// update extraTravellers map to only show travellers not in this script
|
||||||
state.otherTravelers = new Map(
|
state.otherTravellers = new Map(
|
||||||
rolesJSON
|
rolesJSON
|
||||||
.filter(r => r.team === "traveler" && !roles.some(i => i.id === r.id))
|
.filter(r => r.team === "traveller" && !roles.some(i => i.id === r.id))
|
||||||
.map(role => [role.id, role])
|
.map(role => [role.id, role])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -255,7 +255,7 @@ export default new Vuex.Store({
|
||||||
if (editionJSONbyId.has(edition.id)) {
|
if (editionJSONbyId.has(edition.id)) {
|
||||||
state.edition = editionJSONbyId.get(edition.id);
|
state.edition = editionJSONbyId.get(edition.id);
|
||||||
state.roles = getRolesByEdition(state.edition);
|
state.roles = getRolesByEdition(state.edition);
|
||||||
state.otherTravelers = getTravelersNotInEdition(state.edition);
|
state.otherTravellers = getTravellersNotInEdition(state.edition);
|
||||||
} else {
|
} else {
|
||||||
state.edition = edition;
|
state.edition = edition;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@ const getters = {
|
||||||
alive({ players }) {
|
alive({ players }) {
|
||||||
return players.filter(player => !player.isDead).length;
|
return players.filter(player => !player.isDead).length;
|
||||||
},
|
},
|
||||||
nonTravelers({ players }) {
|
nonTravellers({ players }) {
|
||||||
const nonTravelers = players.filter(
|
const nonTravellers = players.filter(
|
||||||
player => player.role.team !== "traveler"
|
player => player.role.team !== "traveller"
|
||||||
);
|
);
|
||||||
return Math.min(nonTravelers.length, 15);
|
return Math.min(nonTravellers.length, 15);
|
||||||
},
|
},
|
||||||
// calculate a Map of player => night order
|
// calculate a Map of player => night order
|
||||||
nightOrder({ players, fabled }) {
|
nightOrder({ players, fabled }) {
|
||||||
|
@ -73,7 +73,7 @@ const actions = {
|
||||||
let players;
|
let players;
|
||||||
if (rootState.session.isSpectator) {
|
if (rootState.session.isSpectator) {
|
||||||
players = state.players.map(player => {
|
players = state.players.map(player => {
|
||||||
if (player.role.team !== "traveler") {
|
if (player.role.team !== "traveller") {
|
||||||
player.role = {};
|
player.role = {};
|
||||||
}
|
}
|
||||||
player.reminders = [];
|
player.reminders = [];
|
||||||
|
|
|
@ -77,7 +77,7 @@ const mutations = {
|
||||||
addHistory(state, players) {
|
addHistory(state, players) {
|
||||||
if (!state.isVoteHistoryAllowed && state.isSpectator) return;
|
if (!state.isVoteHistoryAllowed && state.isSpectator) return;
|
||||||
if (!state.nomination || state.lockedVote <= players.length) return;
|
if (!state.nomination || state.lockedVote <= players.length) return;
|
||||||
const isExile = players[state.nomination[1]].role.team === "traveler";
|
const isExile = players[state.nomination[1]].role.team === "traveller";
|
||||||
state.voteHistory.push({
|
state.voteHistory.push({
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
nominator: players[state.nomination[0]].name,
|
nominator: players[state.nomination[0]].name,
|
||||||
|
|
|
@ -261,7 +261,7 @@ class LiveSession {
|
||||||
isDead: player.isDead,
|
isDead: player.isDead,
|
||||||
isVoteless: player.isVoteless,
|
isVoteless: player.isVoteless,
|
||||||
pronouns: player.pronouns,
|
pronouns: player.pronouns,
|
||||||
...(player.role && player.role.team === "traveler"
|
...(player.role && player.role.team === "traveller"
|
||||||
? { roleId: player.role.id }
|
? { roleId: player.role.id }
|
||||||
: {})
|
: {})
|
||||||
}));
|
}));
|
||||||
|
@ -331,7 +331,7 @@ class LiveSession {
|
||||||
this._store.commit("players/update", { player, property, value });
|
this._store.commit("players/update", { player, property, value });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// roles are special, because of travelers
|
// roles are special, because of travellers
|
||||||
if (roleId && player.role.id !== roleId) {
|
if (roleId && player.role.id !== roleId) {
|
||||||
const role =
|
const role =
|
||||||
this._store.state.roles.get(roleId) ||
|
this._store.state.roles.get(roleId) ||
|
||||||
|
@ -343,7 +343,7 @@ class LiveSession {
|
||||||
value: role
|
value: role
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (!roleId && player.role.team === "traveler") {
|
} else if (!roleId && player.role.team === "traveller") {
|
||||||
this._store.commit("players/update", {
|
this._store.commit("players/update", {
|
||||||
player,
|
player,
|
||||||
property: "role",
|
property: "role",
|
||||||
|
@ -448,8 +448,8 @@ class LiveSession {
|
||||||
if (this._isSpectator || property === "reminders") return;
|
if (this._isSpectator || property === "reminders") return;
|
||||||
const index = this._store.state.players.players.indexOf(player);
|
const index = this._store.state.players.players.indexOf(player);
|
||||||
if (property === "role") {
|
if (property === "role") {
|
||||||
if (value.team && value.team === "traveler") {
|
if (value.team && value.team === "traveller") {
|
||||||
// update local gamestate to remember this player as a traveler
|
// update local gamestate to remember this player as a traveller
|
||||||
this._gamestate[index].roleId = value.id;
|
this._gamestate[index].roleId = value.id;
|
||||||
this._send("player", {
|
this._send("player", {
|
||||||
index,
|
index,
|
||||||
|
@ -457,7 +457,7 @@ class LiveSession {
|
||||||
value: value.id
|
value: value.id
|
||||||
});
|
});
|
||||||
} else if (this._gamestate[index].roleId) {
|
} else if (this._gamestate[index].roleId) {
|
||||||
// player was previously a traveler
|
// player was previously a traveller
|
||||||
delete this._gamestate[index].roleId;
|
delete this._gamestate[index].roleId;
|
||||||
this._send("player", { index, property, value: "" });
|
this._send("player", { index, property, value: "" });
|
||||||
}
|
}
|
||||||
|
@ -477,9 +477,9 @@ class LiveSession {
|
||||||
if (!this._isSpectator) return;
|
if (!this._isSpectator) return;
|
||||||
const player = this._store.state.players.players[index];
|
const player = this._store.state.players.players[index];
|
||||||
if (!player) return;
|
if (!player) return;
|
||||||
// special case where a player stops being a traveler
|
// special case where a player stops being a traveller
|
||||||
if (property === "role") {
|
if (property === "role") {
|
||||||
if (!value && player.role.team === "traveler") {
|
if (!value && player.role.team === "traveller") {
|
||||||
// reset to an unknown role
|
// reset to an unknown role
|
||||||
this._store.commit("players/update", {
|
this._store.commit("players/update", {
|
||||||
player,
|
player,
|
||||||
|
|
Loading…
Reference in New Issue