mirror of https://github.com/bra1n/townsquare.git
player management, better controls, town info
This commit is contained in:
parent
7974e2f131
commit
ed56801ad6
180
src/App.vue
180
src/App.vue
|
@ -1,51 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<ul class="info">
|
<TownInfo :players="players"></TownInfo>
|
||||||
<li>
|
|
||||||
{{ players.length }} <font-awesome-icon icon="users" />
|
|
||||||
{{ teams.alive }} <font-awesome-icon icon="heartbeat" />
|
|
||||||
{{ teams.votes }} <font-awesome-icon icon="vote-yea" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{ teams.townsfolk }}
|
|
||||||
<span class="townsfolk"><font-awesome-icon icon="user-friends"/></span>
|
|
||||||
{{ teams.outsiders }}
|
|
||||||
<span class="outsider"><font-awesome-icon v-bind:icon="teams.outsiders > 1 ? 'user-friends' : 'user'"/></span>
|
|
||||||
{{ teams.minions }}
|
|
||||||
<span class="minion"><font-awesome-icon v-bind:icon="teams.minions > 1 ? 'user-friends' : 'user'"/></span>
|
|
||||||
{{ teams.demons }}
|
|
||||||
<span class="demon"><font-awesome-icon v-bind:icon="teams.demons > 1 ? 'user-friends' : 'user'"/></span>
|
|
||||||
<template v-if="teams.travellers">
|
|
||||||
{{ teams.travellers }}
|
|
||||||
<span class="traveller"
|
|
||||||
><font-awesome-icon v-bind:icon="teams.travellers > 1 ? 'user-friends' : 'user'"/></span>
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<TownSquare
|
<TownSquare
|
||||||
v-if="players.length >= 5"
|
|
||||||
:is-public="isPublic"
|
:is-public="isPublic"
|
||||||
:players="players"
|
:players="players"
|
||||||
:roles="roles"
|
:roles="roles"
|
||||||
></TownSquare>
|
></TownSquare>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button v-on:click="togglePublic">Toggle</button>
|
<font-awesome-icon icon="cogs" @click="isControlOpen = !isControlOpen"/>
|
||||||
<button v-on:click="addPlayer" v-bind:disabled="players.length >= 20">
|
<ul v-if="isControlOpen">
|
||||||
|
<li v-on:click="togglePublic">
|
||||||
|
Toggle Grimoire
|
||||||
|
</li>
|
||||||
|
<li v-on:click="addPlayer" v-if="players.length < 20">
|
||||||
Add Player
|
Add Player
|
||||||
</button>
|
</li>
|
||||||
<button v-on:click="togglePublic" v-bind:disabled="players.length <= 0">
|
<li v-on:click="togglePublic" v-if="players.length > 4">
|
||||||
Remove Player
|
Select Roles
|
||||||
</button>
|
</li>
|
||||||
<button v-on:click="togglePublic">Select Roles</button>
|
<li v-on:click="randomizeSeatings" v-if="players.length > 2">
|
||||||
<button v-on:click="togglePublic">Randomize Roles</button>
|
Randomize Seatings
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TownSquare from "./components/TownSquare.vue";
|
import TownSquare from "./components/TownSquare";
|
||||||
|
import TownInfo from "./components/TownInfo";
|
||||||
import rolesJSON from "./roles";
|
import rolesJSON from "./roles";
|
||||||
import gameJSON from "./game";
|
|
||||||
|
|
||||||
const roles = new Map(
|
const roles = new Map(
|
||||||
rolesJSON
|
rolesJSON
|
||||||
|
@ -55,44 +39,27 @@ const roles = new Map(
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TownSquare
|
TownSquare,
|
||||||
},
|
TownInfo
|
||||||
computed: {
|
|
||||||
teams: function() {
|
|
||||||
const nontravellers = this.players.filter(
|
|
||||||
player => player.role.team !== "traveller"
|
|
||||||
).length;
|
|
||||||
const alive = this.players.filter(player => player.hasDied !== true)
|
|
||||||
.length;
|
|
||||||
return {
|
|
||||||
...gameJSON[nontravellers - 5],
|
|
||||||
travellers: this.players.length - nontravellers,
|
|
||||||
alive,
|
|
||||||
votes:
|
|
||||||
alive +
|
|
||||||
this.players.filter(
|
|
||||||
player => player.hasDied === true && player.hasVoted !== true
|
|
||||||
).length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
isControlOpen: false,
|
||||||
players: [
|
players: [
|
||||||
{
|
// {
|
||||||
name: "Steffen",
|
// name: "Steffen",
|
||||||
role: roles.get("baron"),
|
// role: roles.get("baron"),
|
||||||
reminders: [{ role: "imp", name: "Die" }]
|
// reminders: [{ role: "imp", name: "Die" }]
|
||||||
},
|
// },
|
||||||
{ name: "Tino", role: roles.get("harlot"), reminders: [] },
|
// { name: "Tino", role: roles.get("harlot"), reminders: [] },
|
||||||
{ name: "Basti", role: roles.get("chef"), reminders: [] },
|
// { name: "Basti", role: roles.get("chef"), reminders: [] },
|
||||||
{ name: "Bernd", role: roles.get("ravenkeeper"), reminders: [] },
|
// { name: "Bernd", role: roles.get("ravenkeeper"), reminders: [] },
|
||||||
{ name: "Tim", role: roles.get("drunk"), reminders: [] },
|
// { name: "Tim", role: roles.get("drunk"), reminders: [] },
|
||||||
{ name: "Yann", role: roles.get("librarian"), reminders: [] },
|
// { name: "Yann", role: roles.get("librarian"), reminders: [] },
|
||||||
{ name: "Marie", role: roles.get("empath"), reminders: [] },
|
// { name: "Marie", role: roles.get("empath"), reminders: [] },
|
||||||
{ name: "Bogdan", role: roles.get("scarletwoman"), reminders: [] },
|
// { name: "Bogdan", role: roles.get("scarletwoman"), reminders: [] },
|
||||||
{ name: "Sean", role: roles.get("recluse"), reminders: [] },
|
// { name: "Sean", role: roles.get("recluse"), reminders: [] },
|
||||||
{ name: "Petra", role: roles.get("undertaker"), reminders: [] }
|
// { name: "Petra", role: roles.get("undertaker"), reminders: [] }
|
||||||
],
|
],
|
||||||
roles,
|
roles,
|
||||||
set: "TB"
|
set: "TB"
|
||||||
|
@ -100,6 +67,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
togglePublic() {
|
togglePublic() {
|
||||||
this.isPublic = !this.isPublic;
|
this.isPublic = !this.isPublic;
|
||||||
|
this.isControlOpen = false;
|
||||||
},
|
},
|
||||||
addPlayer() {
|
addPlayer() {
|
||||||
const name = prompt("Player name");
|
const name = prompt("Player name");
|
||||||
|
@ -110,14 +78,20 @@ export default {
|
||||||
reminders: []
|
reminders: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "vars.scss";
|
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Papyrus";
|
font-family: "Papyrus";
|
||||||
src: url("assets/fonts/papyrus.eot"); /* IE9*/
|
src: url("assets/fonts/papyrus.eot"); /* IE9*/
|
||||||
|
@ -158,56 +132,30 @@ body {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
text-align: right;
|
||||||
|
padding: 10px;
|
||||||
.info {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
width: 20%;
|
|
||||||
height: 20%;
|
|
||||||
margin-left: -10%;
|
|
||||||
margin-top: -5%;
|
|
||||||
padding: 0;
|
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
background: url("assets/demon-head.png") center center no-repeat;
|
|
||||||
background-size: auto 100%;
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: block;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 0 0 2px black;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0 5px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: 10px;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
ul {
|
||||||
.townsfolk {
|
display: flex;
|
||||||
color: $townsfolk;
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 10px black;
|
||||||
|
li {
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
margin-bottom: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: red;
|
||||||
}
|
}
|
||||||
.outsider {
|
|
||||||
color: $outsider;
|
|
||||||
}
|
|
||||||
.minion {
|
|
||||||
color: $minion;
|
|
||||||
}
|
|
||||||
.demon {
|
|
||||||
color: $demon;
|
|
||||||
}
|
|
||||||
.traveller {
|
|
||||||
color: $traveller;
|
|
||||||
}
|
|
||||||
.alive,
|
|
||||||
.votes {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,12 @@
|
||||||
<div class="ability" v-if="player.role.ability">
|
<div class="ability" v-if="player.role.ability">
|
||||||
{{ player.role.ability }}
|
{{ player.role.ability }}
|
||||||
</div>
|
</div>
|
||||||
<div class="name" @click="changeName">{{ player.name }}</div>
|
<div class="name" @click="changeName">
|
||||||
|
{{ player.name }}
|
||||||
|
<span class="remove" @click.stop="$emit('remove-player', player)">
|
||||||
|
<font-awesome-icon icon="times-circle" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="player.reminders">
|
<template v-if="player.reminders">
|
||||||
<div
|
<div
|
||||||
|
@ -238,9 +243,19 @@ export default {
|
||||||
.name {
|
.name {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000,
|
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 1))
|
||||||
2px 2px 0 #000, 0 0 10px rgba(0, 0, 0, 0.75);
|
drop-shadow(0 0 1px rgba(0, 0, 0, 1))
|
||||||
|
drop-shadow(0 0 1px rgba(0, 0, 0, 1));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover span {
|
||||||
|
display: inline-block;
|
||||||
|
&:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***** Ability text *****/
|
/***** Ability text *****/
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<ul class="info">
|
||||||
|
<li v-if="players.length < 5">Please add more players!</li>
|
||||||
|
<li>
|
||||||
|
{{ players.length }} <font-awesome-icon class="players" icon="users" />
|
||||||
|
{{ teams.alive }} <font-awesome-icon class="alive" icon="heartbeat" />
|
||||||
|
{{ teams.votes }} <font-awesome-icon class="votes" icon="vote-yea" />
|
||||||
|
</li>
|
||||||
|
<li v-if="players.length >= 5">
|
||||||
|
{{ teams.townsfolk }}
|
||||||
|
<font-awesome-icon class="townsfolk" icon="user-friends" />
|
||||||
|
{{ teams.outsiders }}
|
||||||
|
<font-awesome-icon
|
||||||
|
class="outsider"
|
||||||
|
v-bind:icon="teams.outsiders > 1 ? 'user-friends' : 'user'"
|
||||||
|
/>
|
||||||
|
{{ teams.minions }}
|
||||||
|
<font-awesome-icon
|
||||||
|
class="minion"
|
||||||
|
v-bind:icon="teams.minions > 1 ? 'user-friends' : 'user'"
|
||||||
|
/>
|
||||||
|
{{ teams.demons }}
|
||||||
|
<font-awesome-icon
|
||||||
|
class="demon"
|
||||||
|
v-bind:icon="teams.demons > 1 ? 'user-friends' : 'user'"
|
||||||
|
/>
|
||||||
|
<template v-if="teams.travellers">
|
||||||
|
{{ teams.travellers }}
|
||||||
|
<font-awesome-icon
|
||||||
|
class="traveller"
|
||||||
|
v-bind:icon="teams.travellers > 1 ? 'user-friends' : 'user'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gameJSON from "./../game";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
players: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
teams: function() {
|
||||||
|
const nontravellers = this.players.filter(
|
||||||
|
player => player.role.team !== "traveller"
|
||||||
|
).length;
|
||||||
|
const alive = this.players.filter(player => player.hasDied !== true)
|
||||||
|
.length;
|
||||||
|
return {
|
||||||
|
...gameJSON[nontravellers - 5],
|
||||||
|
travellers: this.players.length - nontravellers,
|
||||||
|
alive,
|
||||||
|
votes:
|
||||||
|
alive +
|
||||||
|
this.players.filter(
|
||||||
|
player => player.hasDied === true && player.hasVoted !== true
|
||||||
|
).length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "../vars.scss";
|
||||||
|
|
||||||
|
.info {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 20%;
|
||||||
|
height: 20%;
|
||||||
|
margin-left: -10%;
|
||||||
|
margin-top: -5%;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: url("../assets/demon-head.png") center center no-repeat;
|
||||||
|
background-size: auto 100%;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 5px;
|
||||||
|
width: 100%;
|
||||||
|
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.7));
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.players {
|
||||||
|
color: #00f700;
|
||||||
|
}
|
||||||
|
.alive {
|
||||||
|
color: #ff4a50;
|
||||||
|
}
|
||||||
|
.votes {
|
||||||
|
color: #1cfff2;
|
||||||
|
}
|
||||||
|
.townsfolk {
|
||||||
|
color: $townsfolk;
|
||||||
|
}
|
||||||
|
.outsider {
|
||||||
|
color: $outsider;
|
||||||
|
}
|
||||||
|
.minion {
|
||||||
|
color: $minion;
|
||||||
|
}
|
||||||
|
.demon {
|
||||||
|
color: $demon;
|
||||||
|
}
|
||||||
|
.traveller {
|
||||||
|
color: $traveller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -9,6 +9,7 @@
|
||||||
:is-public="isPublic"
|
:is-public="isPublic"
|
||||||
@add-reminder="openReminderModal"
|
@add-reminder="openReminderModal"
|
||||||
@set-role="openRoleModal"
|
@set-role="openRoleModal"
|
||||||
|
@remove-player="removePlayer"
|
||||||
></Player>
|
></Player>
|
||||||
</ul>
|
</ul>
|
||||||
<Modal v-show="availableReminders.length" @close="closeModal">
|
<Modal v-show="availableReminders.length" @close="closeModal">
|
||||||
|
@ -51,7 +52,20 @@ export default {
|
||||||
Modal,
|
Modal,
|
||||||
Player
|
Player
|
||||||
},
|
},
|
||||||
props: ["isPublic", "players", "roles"],
|
props: {
|
||||||
|
isPublic: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
players: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
type: Map,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedPlayer: false,
|
selectedPlayer: false,
|
||||||
|
@ -96,6 +110,11 @@ export default {
|
||||||
this.selectedPlayer = false;
|
this.selectedPlayer = false;
|
||||||
this.availableReminders = [];
|
this.availableReminders = [];
|
||||||
this.availableRoles = [];
|
this.availableRoles = [];
|
||||||
|
},
|
||||||
|
removePlayer(player) {
|
||||||
|
if (confirm(`Do you really want to remove ${player.name}?`)) {
|
||||||
|
this.players.splice(this.players.indexOf(player), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -174,7 +193,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@for $i from 5 through 20 {
|
@for $i from 1 through 20 {
|
||||||
.circle.size-#{$i} li {
|
.circle.size-#{$i} li {
|
||||||
@include on-circle($item-count: $i);
|
@include on-circle($item-count: $i);
|
||||||
}
|
}
|
||||||
|
|
14
src/main.js
14
src/main.js
|
@ -6,11 +6,21 @@ import {
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faVoteYea,
|
faVoteYea,
|
||||||
faUserFriends,
|
faUserFriends,
|
||||||
faUser
|
faUser,
|
||||||
|
faTimesCircle,
|
||||||
|
faCogs
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
|
|
||||||
library.add(faUsers, faHeartbeat, faVoteYea, faUserFriends, faUser);
|
library.add(
|
||||||
|
faUsers,
|
||||||
|
faHeartbeat,
|
||||||
|
faVoteYea,
|
||||||
|
faUserFriends,
|
||||||
|
faUser,
|
||||||
|
faTimesCircle,
|
||||||
|
faCogs
|
||||||
|
);
|
||||||
|
|
||||||
Vue.component("font-awesome-icon", FontAwesomeIcon);
|
Vue.component("font-awesome-icon", FontAwesomeIcon);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ $token: 150px;
|
||||||
$townsfolk: #1f65ff;
|
$townsfolk: #1f65ff;
|
||||||
$outsider: #46d5ff;
|
$outsider: #46d5ff;
|
||||||
$minion: #ff6900;
|
$minion: #ff6900;
|
||||||
$demon: #ff0000;
|
$demon: #ce0100;
|
||||||
$traveller: #cc04ff;
|
$traveller: #cc04ff;
|
||||||
|
|
||||||
$roles:
|
$roles:
|
||||||
|
|
Loading…
Reference in New Issue