mirror of https://github.com/bra1n/townsquare.git
further adjustments to store
This commit is contained in:
parent
84e533640b
commit
ca91112b26
25
src/App.vue
25
src/App.vue
|
@ -11,11 +11,11 @@
|
|||
}"
|
||||
>
|
||||
<Intro v-if="!players.length"></Intro>
|
||||
<TownInfo :players="players" v-if="players.length"></TownInfo>
|
||||
<TownSquare :players="players" @screenshot="takeScreenshot"></TownSquare>
|
||||
<Menu ref="menu" :players="players"></Menu>
|
||||
<EditionSelectionModal :players="players"></EditionSelectionModal>
|
||||
<RoleSelectionModal :players="players"></RoleSelectionModal>
|
||||
<TownInfo v-if="players.length"></TownInfo>
|
||||
<TownSquare @screenshot="takeScreenshot"></TownSquare>
|
||||
<Menu ref="menu"></Menu>
|
||||
<EditionModal></EditionModal>
|
||||
<RolesModal></RolesModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -24,26 +24,23 @@ import { mapState } from "vuex";
|
|||
import TownSquare from "./components/TownSquare";
|
||||
import TownInfo from "./components/TownInfo";
|
||||
import Menu from "./components/Menu";
|
||||
import RoleSelectionModal from "./components/RoleSelectionModal";
|
||||
import EditionSelectionModal from "./components/EditionSelectionModal";
|
||||
import RolesModal from "./components/modals/RolesModal";
|
||||
import EditionModal from "./components/modals/EditionModal";
|
||||
import Intro from "./components/Intro";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Intro,
|
||||
EditionSelectionModal,
|
||||
Menu,
|
||||
TownSquare,
|
||||
TownInfo,
|
||||
RoleSelectionModal
|
||||
TownSquare,
|
||||
Menu,
|
||||
EditionModal,
|
||||
RolesModal
|
||||
},
|
||||
computed: mapState({
|
||||
grimoire: state => state.grimoire,
|
||||
players: state => state.players.players
|
||||
}),
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
takeScreenshot(dimensions) {
|
||||
this.$refs.menu.takeScreenshot(dimensions);
|
||||
|
|
|
@ -80,11 +80,10 @@ export default {
|
|||
components: {
|
||||
Screenshot
|
||||
},
|
||||
props: ["players"],
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
computed: mapState(["grimoire"]),
|
||||
computed: mapState({
|
||||
grimoire: state => state.grimoire,
|
||||
players: state => state.players.players
|
||||
}),
|
||||
methods: {
|
||||
takeScreenshot(dimensions = {}) {
|
||||
this.$store.commit("updateScreenshot");
|
||||
|
@ -99,34 +98,23 @@ export default {
|
|||
addPlayer() {
|
||||
const name = prompt("Player name");
|
||||
if (name) {
|
||||
this.players.push({
|
||||
name,
|
||||
role: {},
|
||||
reminders: []
|
||||
});
|
||||
this.$store.commit("players/add", name);
|
||||
}
|
||||
},
|
||||
randomizeSeatings() {
|
||||
if (confirm("Are you sure you want to randomize seatings?")) {
|
||||
this.players = this.players
|
||||
.map(a => [Math.random(), a])
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(a => a[1]);
|
||||
this.$store.dispatch("players/randomize");
|
||||
}
|
||||
},
|
||||
clearPlayers() {
|
||||
if (confirm("Are you sure you want to remove all players?")) {
|
||||
this.players = [];
|
||||
this.$store.commit("players/clear");
|
||||
}
|
||||
},
|
||||
clearRoles() {
|
||||
this.$store.commit("showGrimoire");
|
||||
if (confirm("Are you sure you want to remove all player roles?")) {
|
||||
this.players.forEach(player => {
|
||||
player.role = {};
|
||||
player.hasDied = false;
|
||||
player.reminders = [];
|
||||
});
|
||||
this.$store.dispatch("players/clearRoles");
|
||||
}
|
||||
},
|
||||
...mapMutations([
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
}}</span>
|
||||
</div>
|
||||
|
||||
<Token :role="player.role" @set-role="setRole" />
|
||||
<Token :role="player.role" @set-role="$emit('set-role')" />
|
||||
|
||||
<div class="name" @click="changeName">
|
||||
<span class="screenshot" @click.stop="takeScreenshot">
|
||||
|
@ -40,7 +40,7 @@
|
|||
<span class="name">
|
||||
{{ player.name }}
|
||||
</span>
|
||||
<span class="remove" @click.stop="$emit('remove-player', player)">
|
||||
<span class="remove" @click.stop="$emit('remove-player')">
|
||||
<font-awesome-icon icon="times-circle" />
|
||||
</span>
|
||||
</div>
|
||||
|
@ -64,7 +64,7 @@
|
|||
{{ reminder.name }}
|
||||
</div>
|
||||
</template>
|
||||
<div class="reminder add" @click="$emit('add-reminder', player)">
|
||||
<div class="reminder add" @click="$emit('add-reminder')">
|
||||
<span class="icon"></span>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -97,7 +97,7 @@ export default {
|
|||
this.$emit("screenshot", { width, height, x, y });
|
||||
},
|
||||
toggleStatus() {
|
||||
if (this.$store.state.grimoire.isPublic) {
|
||||
if (this.grimoire.isPublic) {
|
||||
if (!this.player.hasDied) {
|
||||
this.$set(this.player, "hasDied", true);
|
||||
} else if (this.player.hasVoted) {
|
||||
|
@ -110,9 +110,6 @@ export default {
|
|||
this.$set(this.player, "hasDied", !this.player.hasDied);
|
||||
}
|
||||
},
|
||||
setRole() {
|
||||
this.$emit("set-role", this.player);
|
||||
},
|
||||
changeName() {
|
||||
const name = prompt("Player name", this.player.name);
|
||||
this.player.name = name || this.player.name;
|
||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
|||
props: {
|
||||
role: {
|
||||
type: Object,
|
||||
required: true
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
|
@ -43,32 +43,26 @@ import gameJSON from "./../game";
|
|||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
players: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
teams: function() {
|
||||
const nontravelers = Math.min(
|
||||
this.players.filter(player => player.role.team !== "traveler").length,
|
||||
15
|
||||
);
|
||||
const alive = this.players.filter(player => player.hasDied !== true)
|
||||
.length;
|
||||
const { players } = this.$store.state.players;
|
||||
const nonTravelers = this.$store.getters["players/nonTravelers"];
|
||||
const alive = players.filter(player => player.hasDied !== true).length;
|
||||
return {
|
||||
...gameJSON[nontravelers - 5],
|
||||
traveler: this.players.length - nontravelers,
|
||||
...gameJSON[nonTravelers - 5],
|
||||
traveler: players.length - nonTravelers,
|
||||
alive,
|
||||
votes:
|
||||
alive +
|
||||
this.players.filter(
|
||||
players.filter(
|
||||
player => player.hasDied === true && player.hasVoted !== true
|
||||
).length
|
||||
};
|
||||
},
|
||||
...mapState(["edition"])
|
||||
...mapState({
|
||||
edition: state => state.edition,
|
||||
players: state => state.players.players
|
||||
})
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -10,97 +10,54 @@
|
|||
v-for="(player, index) in players"
|
||||
:key="index"
|
||||
:player="player"
|
||||
@add-reminder="openReminderModal"
|
||||
@set-role="openRoleModal"
|
||||
@remove-player="removePlayer"
|
||||
@add-reminder="openReminderModal(index)"
|
||||
@set-role="openRoleModal(index)"
|
||||
@remove-player="removePlayer(index)"
|
||||
@screenshot="$emit('screenshot', $event)"
|
||||
></Player>
|
||||
</ul>
|
||||
|
||||
<div class="bluffs" v-if="players.length > 6" ref="bluffs">
|
||||
<h3>Demon bluffs</h3>
|
||||
<font-awesome-icon icon="camera" @click.stop="takeScreenshot" />
|
||||
<ul>
|
||||
<li @click="openRoleModal(bluffs[0])">
|
||||
<Token :role="bluffs[0].role"></Token>
|
||||
</li>
|
||||
<li @click="openRoleModal(bluffs[1])">
|
||||
<Token :role="bluffs[1].role"></Token>
|
||||
</li>
|
||||
<li @click="openRoleModal(bluffs[2])">
|
||||
<Token :role="bluffs[2].role"></Token>
|
||||
<li
|
||||
v-for="index in bluffs"
|
||||
:key="index"
|
||||
@click="openRoleModal(index * -1)"
|
||||
>
|
||||
<Token :role="grimoire.bluffs[index - 1]"></Token>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
v-show="availableReminders.length && selectedPlayer"
|
||||
@close="closeModal"
|
||||
>
|
||||
<h3>Choose a reminder token:</h3>
|
||||
<ul class="reminders">
|
||||
<li
|
||||
v-for="reminder in availableReminders"
|
||||
class="reminder"
|
||||
v-bind:class="[reminder.role]"
|
||||
v-bind:key="reminder.role + ' ' + reminder.name"
|
||||
@click="addReminder(reminder)"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
v-bind:style="{
|
||||
backgroundImage: `url(${require('../assets/icons/' +
|
||||
reminder.role +
|
||||
'.png')})`
|
||||
}"
|
||||
></span>
|
||||
{{ reminder.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
|
||||
<Modal v-show="availableRoles.length && selectedPlayer" @close="closeModal">
|
||||
<h3>Choose a new character:</h3>
|
||||
<ul class="tokens">
|
||||
<li
|
||||
v-for="role in availableRoles"
|
||||
v-bind:class="[role.team]"
|
||||
v-bind:key="role.id"
|
||||
@click="setRole(role)"
|
||||
>
|
||||
<Token :role="role" />
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
<ReminderModal :player-index="selectedPlayer"></ReminderModal>
|
||||
<RoleModal :player-index="selectedPlayer"></RoleModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Player from "./Player";
|
||||
import Modal from "./Modal";
|
||||
import Token from "./Token";
|
||||
import { mapState } from "vuex";
|
||||
import Player from "./Player";
|
||||
import Token from "./Token";
|
||||
import ReminderModal from "./modals/ReminderModal";
|
||||
import RoleModal from "./modals/RoleModal";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Player,
|
||||
Token,
|
||||
Modal,
|
||||
Player
|
||||
RoleModal,
|
||||
ReminderModal
|
||||
},
|
||||
props: {
|
||||
players: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
computed: {
|
||||
...mapState(["grimoire", "roles"]),
|
||||
...mapState("players", ["players"])
|
||||
},
|
||||
computed: mapState(["grimoire"]),
|
||||
data() {
|
||||
return {
|
||||
selectedPlayer: false,
|
||||
availableReminders: [],
|
||||
availableRoles: [],
|
||||
bluffs: Array(3)
|
||||
.fill({})
|
||||
.map(() => ({ role: {} }))
|
||||
selectedPlayer: 0,
|
||||
bluffs: 3
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -108,46 +65,22 @@ export default {
|
|||
const { width, height, x, y } = this.$refs.bluffs.getBoundingClientRect();
|
||||
this.$emit("screenshot", { width, height, x, y });
|
||||
},
|
||||
openReminderModal(player) {
|
||||
this.availableRoles = [];
|
||||
this.availableReminders = [];
|
||||
this.selectedPlayer = player;
|
||||
this.$store.state.roles.forEach(role => {
|
||||
if (this.players.some(p => p.role.id === role.id)) {
|
||||
this.availableReminders = [
|
||||
...this.availableReminders,
|
||||
...role.reminders.map(name => ({ role: role.id, name }))
|
||||
];
|
||||
}
|
||||
});
|
||||
this.availableReminders.push({ role: "good", name: "Good" });
|
||||
this.availableReminders.push({ role: "evil", name: "Evil" });
|
||||
openReminderModal(playerIndex) {
|
||||
this.selectedPlayer = playerIndex;
|
||||
this.$store.commit("toggleModal", "reminder");
|
||||
},
|
||||
openRoleModal(player) {
|
||||
this.availableRoles = [];
|
||||
this.availableReminders = [];
|
||||
this.selectedPlayer = player;
|
||||
this.$store.state.roles.forEach(role => {
|
||||
if (player.role && role.id !== player.role.id) {
|
||||
this.availableRoles.push(role);
|
||||
}
|
||||
});
|
||||
this.availableRoles.push({});
|
||||
openRoleModal(playerIndex) {
|
||||
this.selectedPlayer = playerIndex;
|
||||
this.$store.commit("toggleModal", "role");
|
||||
},
|
||||
addReminder(reminder) {
|
||||
this.selectedPlayer.reminders.push(reminder);
|
||||
this.closeModal();
|
||||
},
|
||||
setRole(role) {
|
||||
this.selectedPlayer.role = role;
|
||||
this.closeModal();
|
||||
},
|
||||
closeModal() {
|
||||
this.selectedPlayer = false;
|
||||
},
|
||||
removePlayer(player) {
|
||||
if (confirm(`Do you really want to remove ${player.name}?`)) {
|
||||
this.players.splice(this.players.indexOf(player), 1);
|
||||
removePlayer(playerIndex) {
|
||||
if (
|
||||
confirm(
|
||||
`Do you really want to remove ${this.players[playerIndex].name}?`
|
||||
)
|
||||
) {
|
||||
this.$store.commit("players/remove", playerIndex);
|
||||
this.$store.dispatch("players/updateNightOrder");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,8 +88,6 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../vars.scss";
|
||||
|
||||
.circle {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
@ -286,68 +217,4 @@ export default {
|
|||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
/***** Role token modal ******/
|
||||
ul.tokens li {
|
||||
border-radius: 50%;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 5px;
|
||||
transition: transform 500ms ease;
|
||||
|
||||
&.townsfolk {
|
||||
box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff;
|
||||
}
|
||||
&.outsider {
|
||||
box-shadow: 0 0 10px $outsider, 0 0 10px $outsider;
|
||||
}
|
||||
&.minion {
|
||||
box-shadow: 0 0 10px $minion, 0 0 10px $minion;
|
||||
}
|
||||
&.demon {
|
||||
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
||||
}
|
||||
&.traveler {
|
||||
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
/***** Reminder token modal ******/
|
||||
ul.reminders .reminder {
|
||||
background: url("../assets/reminder.png") center center;
|
||||
background-size: 100%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: black;
|
||||
font-size: 65%;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
border: 3px solid black;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
padding: 65px 9px 0;
|
||||
line-height: 100%;
|
||||
transition: transform 500ms ease;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 100%;
|
||||
background-position: center 0;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import editionJSON from "../editions";
|
||||
import editionJSON from "../../editions";
|
||||
import { mapMutations, mapState } from "vuex";
|
||||
import Modal from "./Modal";
|
||||
|
||||
|
@ -39,25 +39,25 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../vars";
|
||||
@import "../../vars";
|
||||
|
||||
// Editions
|
||||
@each $img, $skipIcons in $editions {
|
||||
.edition-#{$img} {
|
||||
background-image: url("../assets/editions/#{$img}.png");
|
||||
background-image: url("../../assets/editions/#{$img}.png");
|
||||
}
|
||||
@if $skipIcons != true {
|
||||
.edition-#{$img}.townsfolk {
|
||||
background-image: url("../assets/editions/#{$img}-townsfolk.png");
|
||||
background-image: url("../../assets/editions/#{$img}-townsfolk.png");
|
||||
}
|
||||
.edition-#{$img}.outsider {
|
||||
background-image: url("../assets/editions/#{$img}-outsider.png");
|
||||
background-image: url("../../assets/editions/#{$img}-outsider.png");
|
||||
}
|
||||
.edition-#{$img}.minion {
|
||||
background-image: url("../assets/editions/#{$img}-minion.png");
|
||||
background-image: url("../../assets/editions/#{$img}-minion.png");
|
||||
}
|
||||
.edition-#{$img}.demon {
|
||||
background-image: url("../assets/editions/#{$img}-demon.png");
|
||||
background-image: url("../../assets/editions/#{$img}-demon.png");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<Modal
|
||||
v-show="modals.reminder && availableReminders.length"
|
||||
v-if="players[playerIndex]"
|
||||
@close="toggleModal('reminder')"
|
||||
>
|
||||
<h3>Choose a reminder token:</h3>
|
||||
<ul class="reminders">
|
||||
<li
|
||||
v-for="reminder in availableReminders"
|
||||
class="reminder"
|
||||
v-bind:class="[reminder.role]"
|
||||
v-bind:key="reminder.role + ' ' + reminder.name"
|
||||
@click="addReminder(reminder)"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
v-bind:style="{
|
||||
backgroundImage: `url(${require('../../assets/icons/' +
|
||||
reminder.role +
|
||||
'.png')})`
|
||||
}"
|
||||
></span>
|
||||
{{ reminder.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from "./Modal";
|
||||
import { mapMutations, mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
components: { Modal },
|
||||
props: ["playerIndex"],
|
||||
computed: {
|
||||
availableReminders() {
|
||||
let reminders = [];
|
||||
const players = this.$store.state.players.players;
|
||||
this.$store.state.roles.forEach(role => {
|
||||
if (players.some(p => p.role.id === role.id)) {
|
||||
reminders = [
|
||||
...reminders,
|
||||
...role.reminders.map(name => ({ role: role.id, name }))
|
||||
];
|
||||
}
|
||||
});
|
||||
reminders.push({ role: "good", name: "Good" });
|
||||
reminders.push({ role: "evil", name: "Evil" });
|
||||
return reminders;
|
||||
},
|
||||
...mapState(["modals"]),
|
||||
...mapState("players", ["players"])
|
||||
},
|
||||
methods: {
|
||||
addReminder(reminder) {
|
||||
const player = this.$store.state.players.players[this.playerIndex];
|
||||
player.reminders.push(reminder);
|
||||
this.$store.commit("players/update", { index: this.playerIndex, player });
|
||||
this.$store.commit("toggleModal", "reminder");
|
||||
},
|
||||
...mapMutations(["toggleModal"])
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul.reminders .reminder {
|
||||
background: url("../../assets/reminder.png") center center;
|
||||
background-size: 100%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
color: black;
|
||||
font-size: 65%;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
border: 3px solid black;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
padding: 65px 9px 0;
|
||||
line-height: 100%;
|
||||
transition: transform 500ms ease;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 100%;
|
||||
background-position: center 0;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<Modal
|
||||
v-show="modals.role && availableRoles.length"
|
||||
@close="toggleModal('role')"
|
||||
>
|
||||
<h3>Choose a new character: {{playerIndex}}</h3>
|
||||
<ul class="tokens">
|
||||
<li
|
||||
v-for="role in availableRoles"
|
||||
v-bind:class="[role.team]"
|
||||
v-bind:key="role.id"
|
||||
@click="setRole(role)"
|
||||
>
|
||||
<Token :role="role" />
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapState } from "vuex";
|
||||
import Modal from "./Modal";
|
||||
import Token from "../Token";
|
||||
|
||||
export default {
|
||||
components: { Token, Modal },
|
||||
props: ["playerIndex"],
|
||||
computed: {
|
||||
availableRoles() {
|
||||
const availableRoles = [];
|
||||
const players = this.$store.state.players.players;
|
||||
this.$store.state.roles.forEach(role => {
|
||||
// don't show bluff roles that are already assigned to players
|
||||
if (
|
||||
this.playerIndex >= 0 ||
|
||||
(this.playerIndex < 0 &&
|
||||
!players.some(player => player.role.id === role.id))
|
||||
) {
|
||||
availableRoles.push(role);
|
||||
}
|
||||
});
|
||||
availableRoles.push({});
|
||||
return availableRoles;
|
||||
},
|
||||
...mapState(["modals", "roles"]),
|
||||
...mapState("players", ["players"])
|
||||
},
|
||||
methods: {
|
||||
setRole(role) {
|
||||
if (this.playerIndex < 0) {
|
||||
// assign to bluff slot
|
||||
this.$store.commit("setBluff", {
|
||||
index: this.playerIndex * -1 - 1,
|
||||
role
|
||||
});
|
||||
} else {
|
||||
// assign to player
|
||||
const player = this.$store.state.players.players[this.playerIndex];
|
||||
player.role = role;
|
||||
this.$store.commit("players/update", {
|
||||
index: this.playerIndex,
|
||||
player
|
||||
});
|
||||
this.$store.dispatch("players/updateNightOrder");
|
||||
}
|
||||
this.$store.commit("toggleModal", "role");
|
||||
},
|
||||
...mapMutations(["toggleModal"])
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../vars.scss";
|
||||
|
||||
ul.tokens li {
|
||||
border-radius: 50%;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 5px;
|
||||
transition: transform 500ms ease;
|
||||
|
||||
&.townsfolk {
|
||||
box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff;
|
||||
}
|
||||
&.outsider {
|
||||
box-shadow: 0 0 10px $outsider, 0 0 10px $outsider;
|
||||
}
|
||||
&.minion {
|
||||
box-shadow: 0 0 10px $minion, 0 0 10px $minion;
|
||||
}
|
||||
&.demon {
|
||||
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
||||
}
|
||||
&.traveler {
|
||||
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,9 +3,9 @@
|
|||
class="roles"
|
||||
v-show="modals.roles"
|
||||
@close="toggleModal('roles')"
|
||||
v-if="nontravelerPlayers >= 5"
|
||||
v-if="nonTravelers >= 5"
|
||||
>
|
||||
<h3>Select the characters for {{ nontravelerPlayers }} players:</h3>
|
||||
<h3>Select the characters for {{ nonTravelers }} players:</h3>
|
||||
<ul
|
||||
class="tokens"
|
||||
v-for="(teamRoles, team) in roleSelection"
|
||||
|
@ -13,7 +13,7 @@
|
|||
>
|
||||
<li class="count" v-bind:class="[team]">
|
||||
{{ teamRoles.filter(role => role.selected).length }} /
|
||||
{{ game[nontravelerPlayers - 5][team] }}
|
||||
{{ game[nonTravelers - 5][team] }}
|
||||
</li>
|
||||
<li
|
||||
v-for="role in teamRoles"
|
||||
|
@ -33,7 +33,7 @@
|
|||
class="button"
|
||||
@click="assignRoles"
|
||||
v-bind:class="{
|
||||
disabled: selectedRoles > nontravelerPlayers || !selectedRoles
|
||||
disabled: selectedRoles > nonTravelers || !selectedRoles
|
||||
}"
|
||||
>
|
||||
<font-awesome-icon icon="people-arrows" />
|
||||
|
@ -49,9 +49,9 @@
|
|||
|
||||
<script>
|
||||
import Modal from "./Modal";
|
||||
import gameJSON from "./../game";
|
||||
import Token from "./Token";
|
||||
import { mapMutations, mapState } from "vuex";
|
||||
import gameJSON from "./../../game";
|
||||
import Token from "./../Token";
|
||||
import { mapGetters, mapMutations, mapState } from "vuex";
|
||||
|
||||
const randomElement = arr => arr[Math.floor(Math.random() * arr.length)];
|
||||
|
||||
|
@ -60,12 +60,6 @@ export default {
|
|||
Token,
|
||||
Modal
|
||||
},
|
||||
props: {
|
||||
players: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
roleSelection: {},
|
||||
|
@ -73,13 +67,6 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
nontravelerPlayers: function() {
|
||||
return Math.min(
|
||||
this.players.filter(({ role }) => role && role.team !== "traveler")
|
||||
.length,
|
||||
15
|
||||
);
|
||||
},
|
||||
selectedRoles: function() {
|
||||
return Object.values(this.roleSelection)
|
||||
.map(roles => roles.filter(role => role.selected).length)
|
||||
|
@ -90,7 +77,9 @@ export default {
|
|||
roles.some(role => role.selected && role.setup)
|
||||
);
|
||||
},
|
||||
...mapState(["roles", "modals"])
|
||||
...mapState(["roles", "modals"]),
|
||||
...mapState("players", ["players"]),
|
||||
...mapGetters({ nonTravelers: "players/nonTravelers" })
|
||||
},
|
||||
methods: {
|
||||
selectRandomRoles() {
|
||||
|
@ -103,7 +92,7 @@ export default {
|
|||
this.$set(role, "selected", false);
|
||||
});
|
||||
delete this.roleSelection["traveler"];
|
||||
const playerCount = Math.max(5, this.nontravelerPlayers);
|
||||
const playerCount = Math.max(5, this.nonTravelers);
|
||||
const composition = this.game[playerCount - 5];
|
||||
Object.keys(composition).forEach(team => {
|
||||
for (let x = 0; x < composition[team]; x++) {
|
||||
|
@ -117,7 +106,7 @@ export default {
|
|||
});
|
||||
},
|
||||
assignRoles() {
|
||||
if (this.selectedRoles <= this.nontravelerPlayers && this.selectedRoles) {
|
||||
if (this.selectedRoles <= this.nonTravelers && this.selectedRoles) {
|
||||
// generate list of selected roles and randomize it
|
||||
const roles = Object.values(this.roleSelection)
|
||||
.map(roles => roles.filter(role => role.selected))
|
||||
|
@ -125,12 +114,14 @@ export default {
|
|||
.map(a => [Math.random(), a])
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(a => a[1]);
|
||||
this.players.forEach(player => {
|
||||
this.players.forEach((player, index) => {
|
||||
if (player.role.team !== "traveler" && roles.length) {
|
||||
player.role = roles.pop();
|
||||
this.$store.commit("players/update", { index, player });
|
||||
}
|
||||
});
|
||||
this.close();
|
||||
this.$store.dispatch("players/updateNightOrder");
|
||||
this.$store.commit("toggleModal", "roles");
|
||||
}
|
||||
},
|
||||
...mapMutations(["toggleModal"])
|
||||
|
@ -148,17 +139,40 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../vars.scss";
|
||||
<style lang="scss" scoped>
|
||||
@import "../../vars.scss";
|
||||
|
||||
.roles .modal ul.tokens {
|
||||
ul.tokens {
|
||||
padding-left: 55px;
|
||||
li {
|
||||
border-radius: 50%;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
margin: 5px;
|
||||
opacity: 0.5;
|
||||
transition: all 250ms;
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
&.townsfolk {
|
||||
box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff;
|
||||
}
|
||||
&.outsider {
|
||||
box-shadow: 0 0 10px $outsider, 0 0 10px $outsider;
|
||||
}
|
||||
&.minion {
|
||||
box-shadow: 0 0 10px $minion, 0 0 10px $minion;
|
||||
}
|
||||
&.demon {
|
||||
box-shadow: 0 0 10px $demon, 0 0 10px $demon;
|
||||
}
|
||||
&.traveler {
|
||||
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
.count {
|
||||
opacity: 1;
|
|
@ -32,11 +32,14 @@ export default new Vuex.Store({
|
|||
isScreenshot: false,
|
||||
isScreenshotSuccess: false,
|
||||
zoom: 1,
|
||||
background: ""
|
||||
background: "",
|
||||
bluffs: []
|
||||
},
|
||||
modals: {
|
||||
edition: false,
|
||||
roles: false
|
||||
roles: false,
|
||||
role: false,
|
||||
reminder: false
|
||||
},
|
||||
edition: "tb",
|
||||
roles: getRolesByEdition()
|
||||
|
@ -61,6 +64,9 @@ export default new Vuex.Store({
|
|||
setBackground({ grimoire }, background) {
|
||||
grimoire.background = background;
|
||||
},
|
||||
setBluff({ grimoire }, { index, role }) {
|
||||
grimoire.bluffs.splice(index, 1, role);
|
||||
},
|
||||
toggleModal({ modals }, name) {
|
||||
modals[name] = !modals[name];
|
||||
},
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
const NEWPLAYER = {
|
||||
role: {},
|
||||
reminders: [],
|
||||
hasVoted: false,
|
||||
hasDied: false,
|
||||
firstNight: 0,
|
||||
otherNight: 0
|
||||
};
|
||||
|
||||
const state = () => ({
|
||||
players: []
|
||||
});
|
||||
const getters = {};
|
||||
|
||||
const getters = {
|
||||
nonTravelers({ players }) {
|
||||
const nonTravelers = players.filter(
|
||||
player => player.role.team !== "traveler"
|
||||
);
|
||||
return Math.min(nonTravelers.length, 15);
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
// recalculate night order for all players
|
||||
updateNightOrder({ state, commit }) {
|
||||
|
@ -23,26 +41,44 @@ const actions = {
|
|||
if (player.firstNight !== first || player.otherNight !== other) {
|
||||
player.firstNight = first;
|
||||
player.otherNight = other;
|
||||
commit("updatePlayer", index, player);
|
||||
commit("update", { index, player });
|
||||
console.log("updated night order for player", player.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
randomize({ state, commit }) {
|
||||
const players = state.players
|
||||
.map(a => [Math.random(), a])
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(a => a[1]);
|
||||
commit("set", players);
|
||||
},
|
||||
clearRoles({ state, commit }) {
|
||||
const players = state.players.map(({ name }) => ({
|
||||
name,
|
||||
...NEWPLAYER
|
||||
}));
|
||||
commit("set", players);
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setPlayers(state, players = []) {
|
||||
clear(state) {
|
||||
state.players = [];
|
||||
},
|
||||
set(state, players = []) {
|
||||
state.players = players;
|
||||
},
|
||||
updatePlayer(state, index, player) {
|
||||
update(state, { index, player }) {
|
||||
state.players[index] = player;
|
||||
},
|
||||
addPlayer(state, name) {
|
||||
add(state, name) {
|
||||
state.players.push({
|
||||
name,
|
||||
role: {},
|
||||
reminders: []
|
||||
...NEWPLAYER
|
||||
});
|
||||
},
|
||||
removePlayer(state, index) {
|
||||
remove(state, index) {
|
||||
state.players.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,16 +7,27 @@ module.exports = store => {
|
|||
store.commit("showGrimoire", JSON.parse(localStorage.isPublic));
|
||||
}
|
||||
if (localStorage.edition !== undefined) {
|
||||
// this will initialize state.roles!
|
||||
store.commit("setEdition", localStorage.edition);
|
||||
}
|
||||
if (localStorage.bluffs !== undefined) {
|
||||
JSON.parse(localStorage.bluffs).forEach((role, index) => {
|
||||
store.commit("setBluff", {
|
||||
index,
|
||||
role: store.state.roles.get(role) || {}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (localStorage.players) {
|
||||
store.commit(
|
||||
"players/setPlayers",
|
||||
"players/set",
|
||||
JSON.parse(localStorage.players).map(player => ({
|
||||
...player,
|
||||
role: store.state.roles.get(player.role) || {}
|
||||
}))
|
||||
);
|
||||
// recalculate night order
|
||||
store.dispatch("players/updateNightOrder");
|
||||
}
|
||||
|
||||
// listen to mutations
|
||||
|
@ -39,20 +50,35 @@ module.exports = store => {
|
|||
case "setEdition":
|
||||
localStorage.setItem("edition", payload);
|
||||
break;
|
||||
case "addPlayer":
|
||||
case "updatePlayer":
|
||||
case "removePlayer":
|
||||
case "setBluff":
|
||||
localStorage.setItem(
|
||||
"players",
|
||||
JSON.stringify(
|
||||
state.players.players.map(player => ({
|
||||
...player,
|
||||
role: player.role.id || {}
|
||||
}))
|
||||
)
|
||||
"bluffs",
|
||||
JSON.stringify(state.grimoire.bluffs.map(({ id }) => id))
|
||||
);
|
||||
break;
|
||||
case "players/add":
|
||||
case "players/update":
|
||||
case "players/remove":
|
||||
case "players/clear":
|
||||
case "players/set":
|
||||
if (state.players.players.length) {
|
||||
localStorage.setItem(
|
||||
"players",
|
||||
JSON.stringify(
|
||||
state.players.players.map(player => ({
|
||||
...player,
|
||||
// simplify the stored data
|
||||
role: player.role.id || {},
|
||||
firstNight: undefined,
|
||||
otherNight: undefined
|
||||
}))
|
||||
)
|
||||
);
|
||||
} else {
|
||||
localStorage.removeItem("players");
|
||||
}
|
||||
break;
|
||||
}
|
||||
console.log(type, payload);
|
||||
console.log("persistance", type, payload);
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue