townsquare/src/components/modals/RolesModal.vue

209 lines
5.3 KiB
Vue
Raw Normal View History

2020-04-11 22:55:37 +02:00
<template>
2020-04-25 19:25:32 +02:00
<Modal
class="roles"
2020-05-02 21:11:20 +02:00
v-show="modals.roles"
@close="toggleModal('roles')"
2020-05-03 23:05:17 +02:00
v-if="nonTravelers >= 5"
2020-04-25 19:25:32 +02:00
>
2020-05-03 23:05:17 +02:00
<h3>Select the characters for {{ nonTravelers }} players:</h3>
2020-04-11 22:55:37 +02:00
<ul
class="tokens"
v-for="(teamRoles, team) in roleSelection"
v-bind:key="team"
>
<li class="count" v-bind:class="[team]">
{{ teamRoles.filter(role => role.selected).length }} /
2020-05-03 23:05:17 +02:00
{{ game[nonTravelers - 5][team] }}
2020-04-11 22:55:37 +02:00
</li>
<li
v-for="role in teamRoles"
v-bind:class="[role.team, role.selected ? 'selected' : '']"
2020-04-11 22:55:37 +02:00
v-bind:key="role.id"
@click="role.selected = !role.selected"
>
<Token :role="role" />
2020-04-11 22:55:37 +02:00
</li>
</ul>
<div class="warning" v-if="hasSelectedSetupRoles">
Warning: there are characters selected that modify the game setup! The
randomizer does not account for these characters.
</div>
<div class="button-group">
<div
class="button"
@click="assignRoles"
v-bind:class="{
2020-05-03 23:05:17 +02:00
disabled: selectedRoles > nonTravelers || !selectedRoles
}"
>
<font-awesome-icon icon="people-arrows" />
Assign {{ selectedRoles }} characters randomly
</div>
<div class="button" @click="selectRandomRoles">
<font-awesome-icon icon="random" />
2020-04-28 22:22:56 +02:00
Shuffle characters
</div>
</div>
2020-04-11 22:55:37 +02:00
</Modal>
</template>
<script>
import Modal from "./Modal";
2020-05-03 23:05:17 +02:00
import gameJSON from "./../../game";
import Token from "./../Token";
import { mapGetters, mapMutations, mapState } from "vuex";
2020-04-11 22:55:37 +02:00
const randomElement = arr => arr[Math.floor(Math.random() * arr.length)];
export default {
components: {
Token,
2020-04-11 22:55:37 +02:00
Modal
},
data: function() {
return {
roleSelection: {},
game: gameJSON
};
},
computed: {
selectedRoles: function() {
return Object.values(this.roleSelection)
.map(roles => roles.filter(role => role.selected).length)
.reduce((a, b) => a + b, 0);
},
hasSelectedSetupRoles: function() {
return Object.values(this.roleSelection).some(roles =>
roles.some(role => role.selected && role.setup)
);
2020-05-02 21:11:20 +02:00
},
2020-05-03 23:05:17 +02:00
...mapState(["roles", "modals"]),
...mapState("players", ["players"]),
...mapGetters({ nonTravelers: "players/nonTravelers" })
2020-04-11 22:55:37 +02:00
},
methods: {
selectRandomRoles() {
2020-04-11 22:55:37 +02:00
this.roleSelection = {};
this.roles.forEach(role => {
if (!this.roleSelection[role.team]) {
this.$set(this.roleSelection, role.team, []);
}
this.roleSelection[role.team].push(role);
this.$set(role, "selected", false);
});
delete this.roleSelection["traveler"];
2020-05-03 23:05:17 +02:00
const playerCount = Math.max(5, this.nonTravelers);
2020-04-11 22:55:37 +02:00
const composition = this.game[playerCount - 5];
Object.keys(composition).forEach(team => {
for (let x = 0; x < composition[team]; x++) {
const available = this.roleSelection[team].filter(
role => role.selected !== true
);
if (available.length) {
randomElement(available).selected = true;
}
}
});
},
assignRoles() {
2020-05-03 23:05:17 +02:00
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))
.reduce((a, b) => [...a, ...b], [])
.map(a => [Math.random(), a])
.sort((a, b) => a[0] - b[0])
.map(a => a[1]);
2020-05-03 23:05:17 +02:00
this.players.forEach((player, index) => {
if (player.role.team !== "traveler" && roles.length) {
player.role = roles.pop();
2020-05-03 23:05:17 +02:00
this.$store.commit("players/update", { index, player });
}
});
2020-05-03 23:05:17 +02:00
this.$store.dispatch("players/updateNightOrder");
this.$store.commit("toggleModal", "roles");
}
2020-05-02 21:11:20 +02:00
},
...mapMutations(["toggleModal"])
2020-04-11 22:55:37 +02:00
},
mounted: function() {
if (!Object.keys(this.roleSelection).length) {
this.selectRandomRoles();
2020-04-11 22:55:37 +02:00
}
2020-04-18 21:03:58 +02:00
},
watch: {
roles() {
this.selectRandomRoles();
}
2020-04-11 22:55:37 +02:00
}
};
</script>
2020-05-03 23:05:17 +02:00
<style lang="scss" scoped>
@import "../../vars.scss";
2020-04-11 22:55:37 +02:00
2020-05-03 23:05:17 +02:00
ul.tokens {
padding-left: 55px;
li {
2020-05-03 23:05:17 +02:00
border-radius: 50%;
height: 120px;
width: 120px;
margin: 5px;
opacity: 0.5;
transition: all 250ms;
&.selected {
opacity: 1;
}
2020-05-03 23:05:17 +02:00
&.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;
}
}
2020-04-11 22:55:37 +02:00
.count {
opacity: 1;
2020-04-11 22:55:37 +02:00
position: absolute;
left: 0;
top: 40px;
2020-04-11 22:55:37 +02:00
font-weight: bold;
line-height: 50px;
text-align: center;
font-size: 100%;
width: 50px;
height: 50px;
2020-04-11 22:55:37 +02:00
&.townsfolk {
color: $townsfolk;
}
&.outsider {
color: $outsider;
}
&.minion {
color: $minion;
}
&.demon {
color: $demon;
}
}
}
.roles .modal .warning {
color: red;
text-align: center;
margin: auto;
}
2020-04-11 22:55:37 +02:00
</style>