Merge pull request #1 from bra1n/main

merge
This commit is contained in:
nicfreeman1209 2021-01-29 21:23:10 +00:00 committed by GitHub
commit ef4241308e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1028 additions and 988 deletions

View File

@ -18,6 +18,9 @@ on:
push: push:
branches-ignore: branches-ignore:
- 'gh-pages' - 'gh-pages'
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
############### ###############
# Set the Job # # Set the Job #

View File

@ -1,10 +1,57 @@
# Release Notes # Release Notes
## Version 2.6.0
- night mode can be toggeled with [S] now (thanks @davotronic5000)
- night order shows which players are dead
---
## Version 2.5.0
- all travelers from the base editions are now optionally available (thanks @davotronic5000)
- night order shows player names near roles now
---
## Version 2.4.0
- 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)
- better Lycanthrope icon (thanks @AWConant)
---
## Version 2.3.1
- better vote history design and added timestamps
- adjusted player menu styling on smaller screens
- improved CONTRIBUTING.md description of hosting your own copy
---
## Version 2.3.0
- added spoiler role (Lycanthrope!)
- fixed copy to clipboard in Firefox
- fixed non-countdown votes still playing countdown sound for a split second
---
## Version 2.2.1
- clearing players / roles now also clears Fabled
- fix list of locked votes showing unlocked votes sometimes
---
## Version 2.2.0
- added [V] hotkey to open nomination history (thanks @lilserf)
- updated roles according to official Wiki changes
- adjusted roles night order
---
## Version 2.1.1 ## Version 2.1.1
- show vote results at the end of a vote - show vote results at the end of a vote
- fixed global reminders not showing up anymore when the associated role is assigned to a player - fixed global reminders not showing up anymore when the associated role is assigned to a player
- adjusted backend metrics - adjusted backend metrics
--- ---
## Version 2.1.0 ## Version 2.1.0

View File

@ -43,6 +43,20 @@ $ npm install
The development server can be started with `npm run serve`. The development server can be started with `npm run serve`.
### Deploying to GitHub pages or with a non-root path
Deploying a forked version to GitHub Pages or running your local
development copy in a sub-path (instead of the web root) will require you to modify
the `vue.config.js` and configure the path at which the website will be served.
For example, deploying your forked `townsquare` project to GitHub pages would need the following
`vue.config.js` changes:
```js
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/"
};
```
### Committing Changes ### Committing Changes
Commit messages should be verbose enough to allow someone else to follow your changes and should include references to issues that are being worked on. Commit messages should be verbose enough to allow someone else to follow your changes and should include references to issues that are being worked on.
@ -64,6 +78,10 @@ $ npm run lint
- **`dist`**: contains built files for distribution. - **`dist`**: contains built files for distribution.
- **`public`**: static assets and templates that don't need to be built dynamically.
- **`server`**: NodeJS code for the live session backend server.
- **`src`**: contains the source code. The codebase is written in ES2015. - **`src`**: contains the source code. The codebase is written in ES2015.
- **`assets`**: contains all graphical assets like images, fonts, icons, etc. - **`assets`**: contains all graphical assets like images, fonts, icons, etc.
@ -73,9 +91,13 @@ $ npm run lint
- **`fonts`**: webfonts used on the page - **`fonts`**: webfonts used on the page
- **`icons`**: character token icons - **`icons`**: character token icons
- **`sounds`**: sound effects used on the page
- **`components`**: the internal components used in the project - **`components`**: the internal components used in the project
- **`modals`**: the modals have a separate subfolder - **`modals`**: the modals have a separate subfolder
- **`store`**: the VueX data store and modules - **`store`**: the VueX data store and modules
- **`modules`**: VueX modules that live in their own namespace

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "townsquare", "name": "townsquare",
"version": "2.1.1", "version": "2.6.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

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

View File

@ -102,6 +102,15 @@ export default {
if (this.session.isSpectator) return; if (this.session.isSpectator) return;
this.$store.commit("toggleModal", "roles"); this.$store.commit("toggleModal", "roles");
break; break;
case "v":
if (this.session.voteHistory.length) {
this.$store.commit("toggleModal", "voteHistory");
}
break;
case "s":
if (this.session.isSpectator) return;
this.$store.commit("toggleNight");
break;
case "escape": case "escape":
this.$store.commit("toggleModal"); this.$store.commit("toggleModal");
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@ -43,10 +43,7 @@
<li @click="toggleNight" v-if="!session.isSpectator"> <li @click="toggleNight" v-if="!session.isSpectator">
<template v-if="!grimoire.isNight">Switch to Night</template> <template v-if="!grimoire.isNight">Switch to Night</template>
<template v-if="grimoire.isNight">Switch to Day</template> <template v-if="grimoire.isNight">Switch to Day</template>
<em <em>[S]</em>
><font-awesome-icon
:icon="['fas', grimoire.isNight ? 'sun' : 'cloud-moon']"
/></em>
</li> </li>
<li @click="toggleNightOrder" v-if="players.length"> <li @click="toggleNightOrder" v-if="players.length">
Night order Night order
@ -114,8 +111,7 @@
v-if="session.voteHistory.length" v-if="session.voteHistory.length"
@click="toggleModal('voteHistory')" @click="toggleModal('voteHistory')"
> >
Nomination history Nomination history<em>[V]</em>
<em><font-awesome-icon icon="hand-paper"/></em>
</li> </li>
<li @click="leaveSession" v-if="session.sessionId"> <li @click="leaveSession" v-if="session.sessionId">
Leave Session Leave Session
@ -239,16 +235,9 @@ export default {
} }
}, },
copySessionUrl() { copySessionUrl() {
// check for clipboard permissions const url = window.location.href.split("#")[0];
navigator.permissions const link = url + "#" + this.session.sessionId;
.query({ name: "clipboard-write" }) navigator.clipboard.writeText(link);
.then(({ state }) => {
if (state === "granted" || state === "prompt") {
const url = window.location.href.split("#")[0];
const link = url + "#" + this.session.sessionId;
navigator.clipboard.writeText(link);
}
});
}, },
distributeRoles() { distributeRoles() {
if (this.session.isSpectator) return; if (this.session.isSpectator) return;

View File

@ -675,7 +675,7 @@ li.move:not(.from) .player .overlay svg.move {
border: 10px solid transparent; border: 10px solid transparent;
border-right-color: black; border-right-color: black;
right: 100%; right: 100%;
bottom: 7px; bottom: 5px;
margin-right: 2px; margin-right: 2px;
} }

View File

@ -161,11 +161,13 @@ export default {
}, },
voters: function() { voters: function() {
const nomination = this.session.nomination[1]; const nomination = this.session.nomination[1];
const voters = this.session.votes.map((vote, index) => const voters = Array(this.players.length)
vote ? this.players[index].name : "" .fill("")
); .map((x, index) =>
this.session.votes[index] ? this.players[index].name : ""
);
const reorder = [ const reorder = [
...voters.slice(nomination + 1, this.players.length), ...voters.slice(nomination + 1),
...voters.slice(0, nomination + 1) ...voters.slice(0, nomination + 1)
]; ];
return reorder.slice(0, this.session.lockedVote - 1).filter(n => !!n); return reorder.slice(0, this.session.lockedVote - 1).filter(n => !!n);
@ -178,15 +180,15 @@ export default {
}, },
methods: { methods: {
countdown() { countdown() {
this.$store.commit("session/setVoteInProgress", true);
this.$store.commit("session/lockVote", 0); this.$store.commit("session/lockVote", 0);
this.$store.commit("session/setVoteInProgress", true);
this.voteTimer = setInterval(() => { this.voteTimer = setInterval(() => {
this.start(); this.start();
}, 4000); }, 4000);
}, },
start() { start() {
this.$store.commit("session/setVoteInProgress", true);
this.$store.commit("session/lockVote", 1); this.$store.commit("session/lockVote", 1);
this.$store.commit("session/setVoteInProgress", true);
clearInterval(this.voteTimer); clearInterval(this.voteTimer);
this.voteTimer = setInterval(() => { this.voteTimer = setInterval(() => {
this.$store.commit("session/lockVote"); this.$store.commit("session/lockVote");

View File

@ -54,14 +54,7 @@ export default {
}, },
methods: { methods: {
copy: function() { copy: function() {
// check for clipboard permissions navigator.clipboard.writeText(this.input || this.gamestate);
navigator.permissions
.query({ name: "clipboard-write" })
.then(({ state }) => {
if (state === "granted" || state === "prompt") {
navigator.clipboard.writeText(this.input || this.gamestate);
}
});
}, },
load: function() { load: function() {
if (this.session.isSpectator) return; if (this.session.isSpectator) return;

View File

@ -25,6 +25,17 @@
> >
<span class="name"> <span class="name">
{{ role.name }} {{ role.name }}
<template v-if="role.players.length">
<br />
<small
v-for="(player, index) in role.players"
:class="{ dead: player.isDead }"
:key="index"
>{{
player.name + (role.players.length > index + 1 ? "," : "")
}}</small
>
</template>
</span> </span>
<span <span
class="icon" class="icon"
@ -53,6 +64,17 @@
></span> ></span>
<span class="name"> <span class="name">
{{ role.name }} {{ role.name }}
<template v-if="role.players.length">
<br />
<small
v-for="(player, index) in role.players"
:class="{ dead: player.isDead }"
:key="index"
>{{
player.name + (role.players.length > index + 1 ? "," : "")
}}</small
>
</template>
</span> </span>
</li> </li>
</ul> </ul>
@ -68,11 +90,6 @@ export default {
components: { components: {
Modal Modal
}, },
data: function() {
return {
roleSelection: {}
};
},
computed: { computed: {
rolesFirstNight: function() { rolesFirstNight: function() {
const rolesFirstNight = []; const rolesFirstNight = [];
@ -83,23 +100,22 @@ export default {
id: "evil", id: "evil",
name: "Minion info", name: "Minion info",
firstNight: 2, firstNight: 2,
team: "minion" team: "minion",
players: this.players.filter(p => p.role.team === "minion")
}, },
{ {
id: "evil", id: "evil",
name: "Demon info & bluffs", name: "Demon info & bluffs",
firstNight: 4, firstNight: 4,
team: "demon" team: "demon",
players: this.players.filter(p => p.role.team === "demon")
} }
); );
} }
this.roles.forEach(role => { this.roles.forEach(role => {
if ( const players = this.players.filter(p => p.role.id === role.id);
role.firstNight && if (role.firstNight && (role.team !== "traveler" || players.length)) {
(role.team !== "traveler" || rolesFirstNight.push(Object.assign({ players }, role));
this.players.some(p => p.role.id === role.id))
) {
rolesFirstNight.push(role);
} }
}); });
this.fabled this.fabled
@ -113,12 +129,9 @@ export default {
rolesOtherNight: function() { rolesOtherNight: function() {
const rolesOtherNight = []; const rolesOtherNight = [];
this.roles.forEach(role => { this.roles.forEach(role => {
if ( const players = this.players.filter(p => p.role.id === role.id);
role.otherNight && if (role.otherNight && (role.team !== "traveler" || players.length)) {
(role.team !== "traveler" || rolesOtherNight.push(Object.assign({ players }, role));
this.players.some(p => p.role.id === role.id))
) {
rolesOtherNight.push(role);
} }
}); });
this.fabled this.fabled
@ -179,57 +192,42 @@ h4 {
} }
.fabled { .fabled {
.name, .name {
.player, background: linear-gradient(90deg, $fabled, transparent 35%);
h4 { .night .other & {
color: $fabled; background: linear-gradient(-90deg, $fabled, transparent 35%);
&:before,
&:after {
background-color: $fabled;
} }
} }
} }
.townsfolk { .townsfolk {
.name, .name {
.player, background: linear-gradient(90deg, $townsfolk, transparent 35%);
h4 { .night .other & {
color: $townsfolk; background: linear-gradient(-90deg, $townsfolk, transparent 35%);
&:before,
&:after {
background-color: $townsfolk;
} }
} }
} }
.outsider { .outsider {
.name, .name {
.player, background: linear-gradient(90deg, $outsider, transparent 35%);
h4 { .night .other & {
color: $outsider; background: linear-gradient(-90deg, $outsider, transparent 35%);
&:before,
&:after {
background-color: $outsider;
} }
} }
} }
.minion { .minion {
.name, .name {
.player, background: linear-gradient(90deg, $minion, transparent 35%);
h4 { .night .other & {
color: $minion; background: linear-gradient(-90deg, $minion, transparent 35%);
&:before,
&:after {
background-color: $minion;
} }
} }
} }
.demon { .demon {
.name, .name {
.player, background: linear-gradient(90deg, $demon, transparent 35%);
h4 { .night .other & {
color: $demon; background: linear-gradient(-90deg, $demon, transparent 35%);
&:before,
&:after {
background-color: $demon;
} }
} }
} }
@ -237,20 +235,15 @@ ul {
li { li {
display: flex; display: flex;
width: 100%; width: 100%;
align-items: center; margin-bottom: 3px;
align-content: center;
/*background: linear-gradient(0deg, #ffffff0f, transparent);*/
border-radius: 10px;
.icon { .icon {
width: 6vh; width: 6vh;
background-size: cover; background-size: cover;
background-position: 0 -5px; background-position: 0 0;
flex-grow: 0; flex-grow: 0;
flex-shrink: 0; flex-shrink: 0;
margin: 0 10px;
text-align: center; text-align: center;
border-left: 1px solid #ffffff1f; margin: 0 2px;
border-right: 1px solid #ffffff1f;
&:after { &:after {
content: " "; content: " ";
display: block; display: block;
@ -261,19 +254,18 @@ ul {
flex-grow: 0; flex-grow: 0;
flex-shrink: 0; flex-shrink: 0;
width: 15%; width: 15%;
font-weight: bold;
text-align: right; text-align: right;
font-family: "Papyrus", sans-serif;
font-size: 110%; font-size: 110%;
} padding: 5px;
.player { border-left: 1px solid rgba(255, 255, 255, 0.4);
flex-grow: 0; border-right: 1px solid rgba(255, 255, 255, 0.4);
flex-shrink: 1; small {
text-align: right; color: #888;
margin: 0 10px; margin-right: 5px;
} &.dead {
.ability { text-decoration: line-through;
flex-grow: 1; }
}
} }
} }
&.legend { &.legend {
@ -307,28 +299,23 @@ ul {
.headline { .headline {
display: block; display: block;
font-weight: bold; font-weight: bold;
border-bottom: 1px solid white; border-bottom: 1px solid rgba(255, 255, 255, 0.4);
padding: 5px 10px; padding: 5px 10px;
border-radius: 0; border-radius: 0;
text-align: center; text-align: center;
} }
.icon {
border-color: white;
}
.name { .name {
flex-grow: 1; flex-grow: 1;
} }
.first { .first {
.icon { .name {
border-right: 0; border-left: 0;
} }
} }
.other { .other {
li .name { li .name {
text-align: left; text-align: left;
} border-right: 0;
.icon {
border-left: 0;
} }
} }
} }

View File

@ -56,11 +56,6 @@ export default {
components: { components: {
Modal Modal
}, },
data: function() {
return {
roleSelection: {}
};
},
computed: { computed: {
rolesGrouped: function() { rolesGrouped: function() {
const rolesGrouped = {}; const rolesGrouped = {};
@ -136,7 +131,6 @@ h4 {
.townsfolk { .townsfolk {
.name, .name,
.player,
h4 { h4 {
color: $townsfolk; color: $townsfolk;
&:before, &:before,
@ -147,7 +141,6 @@ h4 {
} }
.outsider { .outsider {
.name, .name,
.player,
h4 { h4 {
color: $outsider; color: $outsider;
&:before, &:before,
@ -158,7 +151,6 @@ h4 {
} }
.minion { .minion {
.name, .name,
.player,
h4 { h4 {
color: $minion; color: $minion;
&:before, &:before,
@ -169,7 +161,6 @@ h4 {
} }
.demon { .demon {
.name, .name,
.player,
h4 { h4 {
color: $demon; color: $demon;
&:before, &:before,
@ -208,7 +199,6 @@ ul {
width: 15%; width: 15%;
font-weight: bold; font-weight: bold;
text-align: right; text-align: right;
font-family: "Papyrus", sans-serif;
font-size: 110%; font-size: 110%;
} }
.player { .player {
@ -216,6 +206,8 @@ ul {
flex-shrink: 1; flex-shrink: 1;
text-align: right; text-align: right;
margin: 0 10px; margin: 0 10px;
color: #888;
font-size: smaller;
} }
.ability { .ability {
flex-grow: 1; flex-grow: 1;
@ -230,6 +222,7 @@ ul {
height: auto; height: auto;
font-family: inherit; font-family: inherit;
font-size: inherit; font-size: inherit;
color: #fff;
} }
.icon:after { .icon:after {
padding-top: 0; padding-top: 0;

View File

@ -82,6 +82,21 @@ export default {
})) }))
]; ];
}); });
// add out of script traveler reminders
this.$store.state.otherTravelers.forEach(role => {
if (players.some(p => p.role.id === role.id)) {
reminders = [
...reminders,
...role.reminders.map(name => ({
role: role.id,
image: role.image,
name
}))
];
}
});
reminders.push({ role: "good", name: "Good" }); reminders.push({ role: "good", name: "Good" });
reminders.push({ role: "evil", name: "Evil" }); reminders.push({ role: "evil", name: "Evil" });
reminders.push({ role: "custom", name: "Custom note" }); reminders.push({ role: "custom", name: "Custom note" });

View File

@ -1,8 +1,5 @@
<template> <template>
<Modal <Modal v-if="modals.role && availableRoles.length" @close="close">
v-if="modals.role && availableRoles.length"
@close="toggleModal('role')"
>
<h3> <h3>
Choose a new character for Choose a new character for
{{ {{
@ -11,7 +8,7 @@
: "bluffing" : "bluffing"
}} }}
</h3> </h3>
<ul class="tokens"> <ul class="tokens" v-if="tab === 'editionRoles' || !otherTravelers.size">
<li <li
v-for="role in availableRoles" v-for="role in availableRoles"
:class="[role.team]" :class="[role.team]"
@ -21,6 +18,33 @@
<Token :role="role" /> <Token :role="role" />
</li> </li>
</ul> </ul>
<ul class="tokens" v-if="tab === 'otherTravelers' && otherTravelers.size">
<li
v-for="role in otherTravelers.values()"
:class="[role.team]"
:key="role.id"
@click="setRole(role)"
>
<Token :role="role" />
</li>
</ul>
<div
class="button-group"
v-if="playerIndex >= 0 && otherTravelers.size && !session.isSpectator"
>
<span
class="button"
:class="{ townsfolk: tab === 'editionRoles' }"
@click="tab = 'editionRoles'"
>Edtition Roles</span
>
<span
class="button"
:class="{ townsfolk: tab === 'otherTravelers' }"
@click="tab = 'otherTravelers'"
>Other Travelers</span
>
</div>
</Modal> </Modal>
</template> </template>
@ -50,7 +74,13 @@ export default {
return availableRoles; return availableRoles;
}, },
...mapState(["modals", "roles", "session"]), ...mapState(["modals", "roles", "session"]),
...mapState("players", ["players"]) ...mapState("players", ["players"]),
...mapState(["otherTravelers"])
},
data() {
return {
tab: "editionRoles"
};
}, },
methods: { methods: {
setRole(role) { setRole(role) {
@ -72,6 +102,10 @@ export default {
} }
this.$store.commit("toggleModal", "role"); this.$store.commit("toggleModal", "role");
}, },
close() {
this.tab = "editionRoles";
this.toggleModal("role");
},
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"])
} }
}; };

View File

@ -15,22 +15,50 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<td>Time</td>
<td>Nominator</td> <td>Nominator</td>
<td>Nominee</td> <td>Nominee</td>
<td>Type</td> <td>Type</td>
<td>Votes</td>
<td>Majority</td> <td>Majority</td>
<td><font-awesome-icon icon="hand-paper" /> Hand up</td> <td>
<font-awesome-icon icon="user-friends" />
Voters
</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(vote, index) in session.voteHistory" :key="index"> <tr v-for="(vote, index) in session.voteHistory" :key="index">
<td>
{{
vote.timestamp
.getHours()
.toString()
.padStart(2, "0")
}}:{{
vote.timestamp
.getMinutes()
.toString()
.padStart(2, "0")
}}
</td>
<td>{{ vote.nominator }}</td> <td>{{ vote.nominator }}</td>
<td>{{ vote.nominee }}</td> <td>{{ vote.nominee }}</td>
<td>{{ vote.type }}</td> <td>{{ vote.type }}</td>
<td>{{ vote.majority }}</td>
<td> <td>
{{ vote.votes.length }} {{ vote.votes.length }}
<font-awesome-icon icon="user-friends" /> <font-awesome-icon icon="hand-paper" />
</td>
<td>
{{ vote.majority }}
<font-awesome-icon
:icon="[
'fas',
vote.votes.length >= vote.majority ? 'check-square' : 'square'
]"
/>
</td>
<td>
{{ vote.votes.join(", ") }} {{ vote.votes.join(", ") }}
</td> </td>
</tr> </tr>
@ -89,13 +117,16 @@ thead td {
} }
tbody { tbody {
td:nth-child(1) { td:nth-child(2) {
color: $townsfolk; color: $townsfolk;
} }
td:nth-child(2) { td:nth-child(3) {
color: $demon; color: $demon;
} }
td:nth-child(4) { td:nth-child(5) {
text-align: center;
}
td:nth-child(6) {
text-align: center; text-align: center;
} }
} }

View File

@ -34,7 +34,6 @@ const faIcons = [
"SearchMinus", "SearchMinus",
"SearchPlus", "SearchPlus",
"Square", "Square",
"Sun",
"TheaterMasks", "TheaterMasks",
"Times", "Times",
"TimesCircle", "TimesCircle",

View File

@ -44,6 +44,12 @@
.player > .name { .player > .name {
top: 0; top: 0;
} }
.player > .menu {
bottom: 0;
&:before {
bottom: 0;
}
}
} }
// Old phones // Old phones

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,19 @@ const getRolesByEdition = (edition = editionJSON[0]) => {
); );
}; };
const getTravelersNotInEdition = (edition = editionJSON[0]) => {
return new Map(
rolesJSON
.filter(
r =>
r.team === "traveler" &&
r.edition !== edition.id &&
!edition.roles.includes(r.id)
)
.map(role => [role.id, role])
);
};
// base definition for custom roles // base definition for custom roles
const imageBase = const imageBase =
"https://raw.githubusercontent.com/bra1n/townsquare/main/src/assets/icons/"; "https://raw.githubusercontent.com/bra1n/townsquare/main/src/assets/icons/";
@ -70,6 +83,7 @@ export default new Vuex.Store({
}, },
edition: editionJSONbyId.get("tb"), edition: editionJSONbyId.get("tb"),
roles: getRolesByEdition(), roles: getRolesByEdition(),
otherTravelers: getTravelersNotInEdition(),
fabled fabled
}, },
getters: { getters: {
@ -180,11 +194,18 @@ export default new Vuex.Store({
// convert to Map // convert to Map
.map(role => [role.id, role]) .map(role => [role.id, role])
); );
// update extraTravelers map to only show travelers not in this script
state.otherTravelers = new Map(
rolesJSON
.filter(r => r.team === "traveler" && !roles.some(i => i.id === r.id))
.map(role => [role.id, role])
);
}, },
setEdition(state, edition) { setEdition(state, edition) {
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);
} else { } else {
state.edition = edition; state.edition = edition;
} }

View File

@ -84,6 +84,7 @@ const actions = {
name, name,
id id
})); }));
commit("setFabled", { fabled: [] });
} }
commit("set", players); commit("set", players);
commit("setBluff"); commit("setBluff");
@ -94,6 +95,7 @@ const mutations = {
clear(state) { clear(state) {
state.players = []; state.players = [];
state.bluffs = []; state.bluffs = [];
state.fabled = [];
}, },
set(state, players = []) { set(state, players = []) {
state.players = players; state.players = players;

View File

@ -72,8 +72,8 @@ const mutations = {
addHistory(state, players) { addHistory(state, players) {
if (!state.nomination || state.lockedVote <= players.length) return; if (!state.nomination || state.lockedVote <= players.length) return;
const isBanishment = players[state.nomination[1]].role.team === "traveler"; const isBanishment = players[state.nomination[1]].role.team === "traveler";
console.log(isBanishment);
state.voteHistory.push({ state.voteHistory.push({
timestamp: new Date(),
nominator: players[state.nomination[0]].name, nominator: players[state.nomination[0]].name,
nominee: players[state.nomination[1]].name, nominee: players[state.nomination[1]].name,
type: isBanishment ? "Banishment" : "Execution", type: isBanishment ? "Banishment" : "Execution",

View File

@ -443,8 +443,11 @@ class LiveSession {
value: {} value: {}
}); });
} else { } else {
// load role // load role, first from session, the global, then fail gracefully
const role = this._store.state.roles.get(value); const role =
this._store.state.roles.get(value) ||
this._store.getters.rolesJSONbyId.get(value) ||
{};
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "role", property: "role",