eslint added

This commit is contained in:
Steffen 2020-04-05 19:50:33 +02:00
parent 66a68b96af
commit 5bcec74076
No known key found for this signature in database
GPG Key ID: 764D74E98267DFC6
9 changed files with 1638 additions and 593 deletions

14
.eslintrc.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
ecmaVersion: 2020
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
}
};

949
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,22 +2,27 @@
"name": "townsquare", "name": "townsquare",
"version": "1.0.0", "version": "1.0.0",
"description": "Blood on the Clocktower Virtual Town Square", "description": "Blood on the Clocktower Virtual Town Square",
"author": "Steffen Baumgart",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build ./src/main.js",
"lint": "vue-cli-service lint"
},
"main": "App.vue", "main": "App.vue",
"dependencies": { "dependencies": {
"@vue/cli-service": "^4.2.3",
"sass": "^1.26.3", "sass": "^1.26.3",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"@vue/cli-service": "^4.2.3",
"vue": "^2.3.3", "vue": "^2.3.3",
"vue-template-compiler": "^2.6.11" "vue-template-compiler": "^2.6.11"
}, },
"devDependencies": {}, "devDependencies": {
"scripts": { "@vue/cli-plugin-eslint": "^4.3.0",
"serve": "vue-cli-service serve", "@vue/eslint-config-prettier": "^6.0.0",
"build": "vue-cli-service build ./src/main.js" "eslint": "^6.7.2",
}, "eslint-plugin-prettier": "^3.1.1",
"repository": { "eslint-plugin-vue": "^6.2.2",
"type": "git", "prettier": "^1.19.1"
"url": "https://github.com//bra1n/townsquare.git"
}, },
"keywords": [ "keywords": [
"botc", "botc",
@ -25,6 +30,9 @@
"vue", "vue",
"scss" "scss"
], ],
"author": "Steffen Baumgart", "license": "GPL-3.0",
"license": "GPL-3.0" "repository": {
"type": "git",
"url": "https://github.com//bra1n/townsquare.git"
}
} }

View File

@ -8,55 +8,58 @@
</template> </template>
<script> <script>
import TownSquare from './components/TownSquare.vue' import TownSquare from "./components/TownSquare.vue";
export default { export default {
components: { components: {
TownSquare TownSquare
}, },
data: () => ({ data: () => ({
isPublic: false isPublic: false
}), }),
methods: { methods: {
togglePublic () { togglePublic() {
this.isPublic = !this.isPublic; this.isPublic = !this.isPublic;
}
} }
} }
};
</script> </script>
<style lang="scss"> <style lang="scss">
@font-face { font-family: "Papyrus"; @font-face {
src: url("assets/fonts/papyrus.eot"); /* IE9*/ font-family: "Papyrus";
src: url("assets/fonts/papyrus.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */ src: url("assets/fonts/papyrus.eot"); /* IE9*/
url("assets/fonts/papyrus.woff2") format("woff2"), /* chrome firefox */ src: url("assets/fonts/papyrus.eot?#iefix") format("embedded-opentype"),
url("assets/fonts/papyrus.woff") format("woff"), /* chrome firefox */ /* IE6-IE8 */ url("assets/fonts/papyrus.woff2") format("woff2"),
url("assets/fonts/papyrus.ttf") format("truetype"), /* chrome firefox opera Safari, Android, iOS 4.2+*/ /* chrome firefox */ url("assets/fonts/papyrus.woff") format("woff"),
url("assets/fonts/papyrus.svg#PapyrusW01") format("svg"); /* iOS 4.1- */ /* chrome firefox */ url("assets/fonts/papyrus.ttf") format("truetype"),
} /* chrome firefox opera Safari, Android, iOS 4.2+*/
url("assets/fonts/papyrus.svg#PapyrusW01") format("svg"); /* iOS 4.1- */
}
html, body { html,
font-size: 1.2em; body {
line-height: 1.4; font-size: 1.2em;
background: url('assets/background.jpg') center center; line-height: 1.4;
background-size: cover; background: url("assets/background.jpg") center center;
color: white; background-size: cover;
height: 100%; color: white;
font-family: 'Roboto Condensed', sans-serif; height: 100%;
-webkit-font-smoothing: antialiased; font-family: "Roboto Condensed", sans-serif;
-moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased;
padding: 0; -moz-osx-font-smoothing: grayscale;
margin: 0; padding: 0;
} margin: 0;
}
#app { #app {
height: 100%; height: 100%;
} }
// Controls // Controls
.controls { .controls {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
} }
</style> </style>

View File

@ -1,20 +1,21 @@
<script> <script>
export default { export default {
methods: { methods: {
close() { close() {
this.$emit('close'); this.$emit("close");
}, }
}, }
}; };
</script> </script>
<template> <template>
<transition name="modal-fade"> <transition name="modal-fade">
<div class="modal-backdrop" @click="close"> <div class="modal-backdrop" @click="close">
<div class="modal" <div
role="dialog" class="modal"
aria-labelledby="modalTitle" role="dialog"
aria-describedby="modalDescription" aria-labelledby="modalTitle"
@click.stop="" aria-describedby="modalDescription"
@click.stop=""
> >
<slot></slot> <slot></slot>
</div> </div>
@ -22,42 +23,42 @@
</transition> </transition>
</template> </template>
<style lang="scss"> <style lang="scss">
.modal-backdrop { .modal-backdrop {
position: fixed; position: fixed;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 100; z-index: 100;
} }
.modal { .modal {
background: rgba(0,0,0,0.8); background: rgba(0, 0, 0, 0.8);
padding: 10px 20px; padding: 10px 20px;
border-radius: 10px; border-radius: 10px;
box-shadow: 2px 2px 20px 1px #000; box-shadow: 2px 2px 20px 1px #000;
overflow-x: auto; overflow-x: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-width: 60%; max-width: 60%;
h2 { h2 {
margin: 0; margin: 0;
text-align: center; text-align: center;
}
} }
}
.modal-fade-enter, .modal-fade-enter,
.modal-fade-leave-active { .modal-fade-leave-active {
opacity: 0; opacity: 0;
} }
.modal-fade-enter-active, .modal-fade-enter-active,
.modal-fade-leave-active { .modal-fade-leave-active {
transition: opacity .2s ease transition: opacity 0.2s ease;
} }
</style> </style>

View File

@ -1,289 +1,341 @@
<template> <template>
<li> <li>
<div class="player" <div
:class="{ dead: player.hasDied, 'no-vote': player.hasVoted }"> class="player"
:class="{ dead: player.hasDied, 'no-vote': player.hasVoted }"
>
<div class="shroud" @click="toggleStatus()"></div> <div class="shroud" @click="toggleStatus()"></div>
<div class="token" @click="changeRole()" :class="[player.role.id]"> <div class="token" @click="changeRole()" :class="[player.role.id]">
<span class="leaf-left" v-if="player.role.firstNight"></span> <span class="leaf-left" v-if="player.role.firstNight"></span>
<span class="leaf-right" v-if="player.role.otherNight"></span> <span class="leaf-right" v-if="player.role.otherNight"></span>
<span v-if="player.role.reminders.length" v-bind:class="['leaf-top' + player.role.reminders.length]"></span> <span
v-if="player.role.reminders.length"
v-bind:class="['leaf-top' + player.role.reminders.length]"
></span>
<span class="leaf-orange" v-if="player.role.setup"></span> <span class="leaf-orange" v-if="player.role.setup"></span>
<div>{{ player.role.name }}</div> <div>{{ player.role.name }}</div>
</div> </div>
<div class="ability" v-if="player.role.ability">{{ player.role.ability }}</div> <div class="ability" v-if="player.role.ability">
{{ player.role.ability }}
</div>
<div class="name">{{ player.name }}</div> <div class="name">{{ player.name }}</div>
</div> </div>
<div class="reminder" <template v-if="player.reminders">
v-if="player.reminders" <div
v-for="reminder in player.reminders" class="reminder"
v-bind:class="[reminder.role]" v-bind:key="reminder.role + ' ' + reminder.name"
@click="removeReminder(reminder)">{{ reminder.name }}</div> v-for="reminder in player.reminders"
v-bind:class="[reminder.role]"
@click="removeReminder(reminder)"
>
{{ reminder.name }}
</div>
</template>
<div class="reminder add" @click="$emit('add-reminder', player)"></div> <div class="reminder add" @click="$emit('add-reminder', player)"></div>
</li> </li>
</template> </template>
<script> <script>
export default { export default {
props: { props: {
player: { player: {
type: Object, type: Object,
required: true required: true
}, },
roles: { roles: {
type: Map, type: Map,
required: true required: true
}, },
isPublic: { isPublic: {
type: Boolean, type: Boolean,
required: true required: true
}
},
data() {
return {};
},
methods: {
toggleStatus() {
if (!this.isPublic || !this.player.hasDied) {
this.$set(this.player, "hasDied", !this.player.hasDied);
if (!this.player.hasDied) {
this.$set(this.player, "hasVoted", false);
}
} else {
this.$set(this.player, "hasVoted", !this.player.hasVoted);
} }
}, },
data () { changeRole() {
return {} if (!this.isPublic) {
}, this.$emit("set-role", this.player);
methods: { } else {
toggleStatus () { this.toggleStatus();
if (!this.isPublic || !this.player.hasDied) {
this.$set(this.player, 'hasDied', !this.player.hasDied);
if (!this.player.hasDied) {
this.$set(this.player, 'hasVoted', false);
}
} else {
this.$set(this.player, 'hasVoted', !this.player.hasVoted);
}
},
changeRole () {
if (!this.isPublic) {
this.$emit('set-role', this.player);
} else {
this.toggleStatus();
}
},
removeReminder (reminder) {
this.player.reminders.splice(this.player.reminders.indexOf(reminder), 1);
} }
},
removeReminder(reminder) {
this.player.reminders.splice(this.player.reminders.indexOf(reminder), 1);
} }
} }
};
</script> </script>
<style lang="scss"> <style lang="scss">
// token size
$token: 150px;
// token size /***** Player token *****/
$token: 150px; .circle .player {
margin-bottom: 10px;
/***** Player token *****/ .shroud {
.circle .player { content: " ";
margin-bottom: 10px; background: url("../assets/shroud.png") center -10px no-repeat;
background-size: auto 100%;
.shroud { position: absolute;
content: " "; margin-left: -2/6 * $token;
background: url('../assets/shroud.png') center -10px no-repeat; width: 2/3 * $token;
background-size: auto 100%; height: 2/3 * $token;
position: absolute; left: 50%;
margin-left: -2/6 * $token; top: -30px;
width: 2/3 * $token; cursor: pointer;
height: 2/3 * $token; opacity: 0;
left: 50%; transform: scale(1.5);
top: -30px; transition: all 200ms;
cursor: pointer; z-index: 2;
opacity: 0; &:hover {
transform: scale(1.5); opacity: 0.5;
transition: all 200ms; top: -10px;
z-index: 2;
&:hover {
opacity: 0.5;
top: -10px;
transform: scale(1);
}
}
&.dead .shroud {
opacity: 1;
top: 0;
transform: scale(1); transform: scale(1);
} }
&.dead .name { color: #999; }
} }
#townsquare.public .player.dead { &.dead .shroud {
&:after { opacity: 1;
content: " "; top: 0;
position: absolute; transform: scale(1);
left: 0;
top: 0;
width: 100%;
background: url('../assets/vote.png') center center no-repeat;
background-size: 40%;
height: $token + 3px;
pointer-events: none;
}
&.traveller:after { filter: grayscale(100%); }
&.no-vote:after { display: none; }
} }
&.dead .name {
color: #999;
}
}
#townsquare.public .player .shroud { display: none; } #townsquare.public .player.dead {
&:after {
/***** Role token ******/ content: " ";
.circle .token { position: absolute;
border-radius: 50%; left: 0;
top: 0;
width: 100%;
background: url("../assets/vote.png") center center no-repeat;
background-size: 40%;
height: $token + 3px; height: $token + 3px;
width: $token + 3px; pointer-events: none;
background: url('../assets/token.png') center center;
background-size: 100%;
text-align: center;
position: relative;
color: black;
margin: auto;
font-weight: 600;
text-shadow:
-1px -1px 0 #fff,
1px -1px 0 #fff,
-1px 1px 0 #fff,
1px 1px 0 #fff,
0 0 5px rgba(0,0,0,0.75);
padding-top: $token * 0.7;
box-sizing: border-box;
font-family: 'Papyrus', serif;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
cursor: pointer;
&:before {
content: " ";
background-size: 100%;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
span {
position: absolute;
width: 100%;
height: 100%;
background-size: 100%;
left: 0;
top: 0;
pointer-events: none;
&.leaf-left { background-image: url('../assets/leaf-left.png'); }
&.leaf-orange { background-image: url('../assets/leaf-orange.png'); }
&.leaf-right { background-image: url('../assets/leaf-right.png'); }
&.leaf-top1 { background-image: url('../assets/leaf-top1.png'); }
&.leaf-top2 { background-image: url('../assets/leaf-top2.png'); }
&.leaf-top3 { background-image: url('../assets/leaf-top3.png'); }
&.leaf-top4 { background-image: url('../assets/leaf-top4.png'); }
&.leaf-top5 { background-image: url('../assets/leaf-top5.png'); }
}
} }
&.traveller:after {
#townsquare.public .token {
background-image: url('../assets/life.png');
div { display: none; }
&:before, &:after, span { display: none; }
}
#townsquare.public .player.dead .token {
background-image: url('../assets/death.png');
}
#townsquare.public .player.traveller .token {
filter: grayscale(100%); filter: grayscale(100%);
} }
&.no-vote:after {
/***** Player name *****/
.name {
font-size: 120%;
line-height: 120%;
text-shadow:
-2px -2px 0 #000,
2px -2px 0 #000,
-2px 2px 0 #000,
2px 2px 0 #000,
0 0 10px rgba(0,0,0,0.75);
}
/***** Ability text *****/
#townsquare.public .ability { display: none; }
.circle .ability {
position: absolute;
padding: 5px 10px;
top: 20px;
right: 100%;
width: 200px;
z-index: 25;
font-size: 80%;
background: rgba(0,0,0,0.7);
border-radius: 10px;
border: 3px solid black;
text-align: left;
display: none; display: none;
&:after {
content: " ";
border: 10px solid transparent;
position: absolute;
left: 100%;
width: 0;
height: 0;
border-left-color: black;
top: 20px;
margin: 0 2px;
}
} }
}
.player:hover .ability { display: block; } #townsquare.public .player .shroud {
display: none;
}
/***** Reminder token *****/ /***** Role token ******/
.circle .reminder { .circle .token {
background: url('../assets/reminder.png') center center; border-radius: 50%;
height: $token + 3px;
width: $token + 3px;
background: url("../assets/token.png") center center;
background-size: 100%;
text-align: center;
position: relative;
color: black;
margin: auto;
font-weight: 600;
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff,
1px 1px 0 #fff, 0 0 5px rgba(0, 0, 0, 0.75);
padding-top: $token * 0.7;
box-sizing: border-box;
font-family: "Papyrus", serif;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
cursor: pointer;
&:before {
content: " ";
background-size: 100%; background-size: 100%;
width: $token / 2; position: absolute;
height: $token / 2; width: 100%;
color: black; height: 100%;
font-size: 50%; left: 0;
font-weight: bold; top: 0;
display: block;
margin: 5px ($token / -4) 0;
text-align: center;
position: relative;
box-sizing: border-box;
padding-top: $token * 0.3;
border-radius: 50%;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
transition: all 200ms;
cursor: pointer;
&:before, &:after {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-size: 100%;
background-position: center 0;
background-repeat: no-repeat;
background-image: url('../assets/icons/plus.png');
transition: opacity 200ms;
}
&:after {
background-image: url('../assets/icons/x.png');
opacity: 0;
}
&.add {
opacity: 0;
top: 30px;
&:after { display: none; }
}
&:hover:before { opacity: 0; }
&:hover:after { opacity: 1; }
} }
.circle li:hover .reminder.add, { opacity: 1; top: 0; }
.circle li:hover .reminder.add:before { opacity: 1; }
#townsquare.public .reminder { display: none; } span {
position: absolute;
width: 100%;
height: 100%;
background-size: 100%;
left: 0;
top: 0;
pointer-events: none;
&.leaf-left {
background-image: url("../assets/leaf-left.png");
}
&.leaf-orange {
background-image: url("../assets/leaf-orange.png");
}
&.leaf-right {
background-image: url("../assets/leaf-right.png");
}
&.leaf-top1 {
background-image: url("../assets/leaf-top1.png");
}
&.leaf-top2 {
background-image: url("../assets/leaf-top2.png");
}
&.leaf-top3 {
background-image: url("../assets/leaf-top3.png");
}
&.leaf-top4 {
background-image: url("../assets/leaf-top4.png");
}
&.leaf-top5 {
background-image: url("../assets/leaf-top5.png");
}
}
}
#townsquare.public .token {
background-image: url("../assets/life.png");
div {
display: none;
}
&:before,
&:after,
span {
display: none;
}
}
#townsquare.public .player.dead .token {
background-image: url("../assets/death.png");
}
#townsquare.public .player.traveller .token {
filter: grayscale(100%);
}
/***** Player name *****/
.name {
font-size: 120%;
line-height: 120%;
text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000,
2px 2px 0 #000, 0 0 10px rgba(0, 0, 0, 0.75);
}
/***** Ability text *****/
#townsquare.public .ability {
display: none;
}
.circle .ability {
position: absolute;
padding: 5px 10px;
top: 20px;
right: 100%;
width: 200px;
z-index: 25;
font-size: 80%;
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
border: 3px solid black;
text-align: left;
display: none;
&:after {
content: " ";
border: 10px solid transparent;
position: absolute;
left: 100%;
width: 0;
height: 0;
border-left-color: black;
top: 20px;
margin: 0 2px;
}
}
.player:hover .ability {
display: block;
}
/***** Reminder token *****/
.circle .reminder {
background: url("../assets/reminder.png") center center;
background-size: 100%;
width: $token / 2;
height: $token / 2;
color: black;
font-size: 50%;
font-weight: bold;
display: block;
margin: 5px ($token / -4) 0;
text-align: center;
position: relative;
box-sizing: border-box;
padding-top: $token * 0.3;
border-radius: 50%;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
transition: all 200ms;
cursor: pointer;
&:before,
&:after {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-size: 100%;
background-position: center 0;
background-repeat: no-repeat;
background-image: url("../assets/icons/plus.png");
transition: opacity 200ms;
}
&:after {
background-image: url("../assets/icons/x.png");
opacity: 0;
}
&.add {
opacity: 0;
top: 30px;
&:after {
display: none;
}
}
&:hover:before {
opacity: 0;
}
&:hover:after {
opacity: 1;
}
}
.circle li:hover .reminder.add {
opacity: 1;
top: 0;
}
.circle li:hover .reminder.add:before {
opacity: 1;
}
#townsquare.public .reminder {
display: none;
}
</style> </style>

View File

@ -1,22 +1,26 @@
<template> <template>
<div id="townsquare" class="square" v-bind:class="{ public: isPublic }"> <div id="townsquare" class="square" v-bind:class="{ public: isPublic }">
<ul class="circle" v-bind:class="['size-' + players.length]"> <ul class="circle" v-bind:class="['size-' + players.length]">
<Player <Player
v-for="player in players" v-for="player in players"
:key="player.name" :key="player.name"
:player="player" :player="player"
:roles="roles" :roles="roles"
:is-public="isPublic" :is-public="isPublic"
@add-reminder="openReminderModal" @add-reminder="openReminderModal"
@set-role="openRoleModal" @set-role="openRoleModal"
></Player> ></Player>
</ul> </ul>
<Modal v-show="availableReminders.length" @close="closeModal"> <Modal v-show="availableReminders.length" @close="closeModal">
<h2>Choose a reminder token:</h2> <h2>Choose a reminder token:</h2>
<ul class="reminders"> <ul class="reminders">
<li v-for="reminder in availableReminders" class="reminder" <li
v-bind:class="[reminder.role]" v-for="reminder in availableReminders"
@click="addReminder(reminder)"> class="reminder"
v-bind:class="[reminder.role]"
v-bind:key="reminder.role + ' ' + reminder.name"
@click="addReminder(reminder)"
>
{{ reminder.name }} {{ reminder.name }}
</li> </li>
</ul> </ul>
@ -24,9 +28,13 @@
<Modal v-show="availableRoles.length" @close="closeModal"> <Modal v-show="availableRoles.length" @close="closeModal">
<h2>Choose a new role:</h2> <h2>Choose a new role:</h2>
<ul class="tokens"> <ul class="tokens">
<li v-for="role in availableRoles" class="token" <li
v-bind:class="[role.id, role.team]" v-for="role in availableRoles"
@click="setRole(role)"> class="token"
v-bind:class="[role.id, role.team]"
v-bind:key="role.id"
@click="setRole(role)"
>
{{ role.name }} {{ role.name }}
</li> </li>
</ul> </ul>
@ -35,263 +43,275 @@
</template> </template>
<script> <script>
import rolesJSON from '../roles.json' import rolesJSON from "../roles.json";
import Player from './Player' import Player from "./Player";
import Modal from "./Modal"; import Modal from "./Modal";
const roles = new Map(rolesJSON.sort((a, b) => b.team.localeCompare(a.team)).map(role => [role.id, role])); const roles = new Map(
rolesJSON
.sort((a, b) => b.team.localeCompare(a.team))
.map(role => [role.id, role])
);
export default { export default {
components: { components: {
Modal, Modal,
Player Player
},
props: ["isPublic"],
data() {
return {
players: [
{
name: "Steffen",
role: roles.get("baron"),
reminders: [{ role: "imp", name: "Die" }]
},
{ name: "Tino", role: roles.get("imp") },
{ name: "Basti", role: roles.get("chef"), reminders: [] },
{ name: "Bernd", role: roles.get("ravenkeeper"), reminders: [] },
{ name: "Tim", role: roles.get("drunk"), reminders: [] },
{ name: "Yann", role: roles.get("librarian"), reminders: [] },
{ name: "Marie", role: roles.get("empath"), reminders: [] },
{ name: "Bogdan", role: roles.get("scarletwoman"), reminders: [] },
{ name: "Sean", role: roles.get("recluse"), reminders: [] },
{ name: "Petra", role: roles.get("undertaker"), reminders: [] }
],
roles,
selectedPlayer: false,
availableReminders: [],
availableRoles: []
};
},
methods: {
openReminderModal(player) {
this.selectedPlayer = player;
this.availableReminders = [];
this.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 }))
];
}
});
}, },
props: ['isPublic'], openRoleModal(player) {
data () { this.selectedPlayer = player;
return { this.availableRoles = [];
players: [ this.roles.forEach(role => {
{ name: "Steffen", role: roles.get('baron'), reminders: [{ role: 'imp', name: 'Die' }] }, if (role.id !== player.role) {
{ name: "Tino", role: roles.get("imp") }, this.availableRoles.push(role);
{ name: "Basti", role: roles.get("chef"), reminders: [] }, }
{ name: "Bernd", role: roles.get("ravenkeeper"), reminders: [] }, });
{ name: "Tim", role: roles.get("drunk"), reminders: [] },
{ name: "Yann", role: roles.get("librarian"), reminders: [] },
{ name: "Marie", role: roles.get("empath"), reminders: [] },
{ name: "Bogdan", role: roles.get("scarletwoman"), reminders: [] },
{ name: "Sean", role: roles.get("recluse"), reminders: [] },
{ name: "Petra", role: roles.get("undertaker"), reminders: [] },
],
roles,
selectedPlayer: false,
availableReminders: [],
availableRoles: [],
}
}, },
methods: { addReminder(reminder) {
openReminderModal (player) { this.selectedPlayer.reminders.push(reminder);
this.selectedPlayer = player; this.closeModal();
this.availableReminders = []; },
this.roles.forEach(role => { setRole(role) {
if (this.players.some(p => p.role.id === role.id)) { this.selectedPlayer.role = role;
this.availableReminders = [ this.closeModal();
...this.availableReminders, },
...role.reminders.map(name => ({role: role.id, name})) closeModal() {
]; this.selectedPlayer = false;
} this.availableReminders = [];
}); this.availableRoles = [];
},
openRoleModal (player) {
this.selectedPlayer = player;
this.availableRoles = [];
this.roles.forEach(role => {
if(role.id !== player.role) {
this.availableRoles.push(role);
}
});
},
addReminder (reminder) {
this.selectedPlayer.reminders.push(reminder);
this.closeModal();
},
setRole (role) {
this.selectedPlayer.role = role;
this.closeModal();
},
closeModal () {
this.selectedPlayer = false;
this.availableReminders = [];
this.availableRoles = [];
}
} }
} }
};
</script> </script>
<style lang="scss"> <style lang="scss">
@import '../roles.scss'; @import "../roles.scss";
@mixin on-circle($item-count) { @mixin on-circle($item-count) {
$angle: (360 / $item-count); $angle: (360 / $item-count);
$rot: 0; $rot: 0;
@for $i from 1 through $item-count { @for $i from 1 through $item-count {
&:nth-child(#{$i}) { &:nth-child(#{$i}) {
transform: rotate($rot * 1deg); transform: rotate($rot * 1deg);
@if $i - 1 <= $item-count / 2 { @if $i - 1 <= $item-count / 2 {
z-index: $item-count - $i + 1; z-index: $item-count - $i + 1;
.ability { .ability {
left: 100%; right: auto; left: 100%;
&:after { right: auto;
border-left-color: transparent; &:after {
border-right-color: black; border-left-color: transparent;
left: auto; border-right-color: black;
right: 100%; left: auto;
} right: 100%;
} }
} @else {
z-index: $i - 1;
}
> * {
transform: rotate($rot * -1deg);
} }
} @else {
z-index: $i - 1;
} }
$rot: $rot + $angle;
}
}
.circle {
padding: 0;
width: 100%;
height: 100%;
list-style: none;
overflow: hidden;
position: relative;
margin: 0;
box-sizing: border-box;
li {
position: absolute;
top: 0;
left: 50%;
height: 50%;
transform-origin: 0 100%;
text-align: center;
&:hover {
z-index: 25 !important;
}
> * { > * {
margin-left: -100px; transform: rotate($rot * -1deg);
width: 200px;
} }
} }
$rot: $rot + $angle;
} }
}
@for $i from 5 through 20 { .circle {
.circle.size-#{$i} li { padding: 0;
@include on-circle($item-count: $i); width: 100%;
height: 100%;
list-style: none;
overflow: hidden;
position: relative;
margin: 0;
box-sizing: border-box;
li {
position: absolute;
top: 0;
left: 50%;
height: 50%;
transform-origin: 0 100%;
text-align: center;
&:hover {
z-index: 25 !important;
}
> * {
margin-left: -100px;
width: 200px;
} }
} }
}
#townsquare { @for $i from 5 through 20 {
width: 100%; .circle.size-#{$i} li {
height: 100%; @include on-circle($item-count: $i);
}
}
#townsquare {
width: 100%;
height: 100%;
position: relative;
border-radius: 50%;
box-sizing: border-box;
padding: 20px;
}
// player circle
.circle {
background: url("../assets/demon-head2.png") center center no-repeat;
background-size: 10%;
}
#townsquare.public .circle {
background-image: url("../assets/demon-head.png");
}
/***** Role token modal ******/
ul.tokens {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
align-content: center;
align-items: center;
overflow: hidden;
justify-content: center;
font-size: 75%;
line-height: 100%;
.token {
border-radius: 50%;
height: 120px;
width: 120px;
background: url("../assets/token.png") center center;
background-size: 100%;
text-align: center;
position: relative;
color: black;
margin: 5px;
font-weight: 600;
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff,
1px 1px 0 #fff, 0 0 5px rgba(0, 0, 0, 0.75);
padding-top: 85px;
box-sizing: border-box;
font-family: "Papyrus", serif;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
cursor: pointer;
transition: transform 500ms ease;
&.townsfolk {
box-shadow: 0 0 10px #004cff, 0 0 10px #004cff;
}
&.outsider {
box-shadow: 0 0 10px #00d6ff, 0 0 10px #00d6ff;
}
&.minion {
box-shadow: 0 0 10px #ff6900, 0 0 10px #ff6900;
}
&.demon {
box-shadow: 0 0 10px #ff0000, 0 0 10px #ff0000;
}
&:before {
content: " ";
background-size: 100%;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
&:hover {
transform: scale(1.2);
}
}
}
/***** Reminder token modal ******/
ul.reminders {
@extend .tokens;
.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;
position: relative; position: relative;
border-radius: 50%; border-radius: 50%;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
padding: 20px; padding-top: 65px;
} transition: transform 500ms ease;
// player circle &:before {
.circle { content: " ";
background: url('../assets/demon-head2.png') center center no-repeat; position: absolute;
background-size: 10%; left: 0;
} top: 0;
width: 100%;
#townsquare.public .circle { height: 100%;
background-image: url('../assets/demon-head.png');
}
/***** Role token modal ******/
ul.tokens {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
align-content: center;
align-items: center;
overflow: hidden;
justify-content: center;
font-size: 75%;
line-height: 100%;
.token {
border-radius: 50%;
height: 120px;
width: 120px;
background: url('../assets/token.png') center center;
background-size: 100%; background-size: 100%;
text-align: center; background-position: center 0;
position: relative; background-repeat: no-repeat;
color: black; }
margin: 5px;
font-weight: 600;
text-shadow:
-1px -1px 0 #fff,
1px -1px 0 #fff,
-1px 1px 0 #fff,
1px 1px 0 #fff,
0 0 5px rgba(0,0,0,0.75);
padding-top: 85px;
box-sizing: border-box;
font-family: 'Papyrus', serif;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
cursor: pointer;
transition: transform 500ms ease;
&.townsfolk { box-shadow: 0 0 10px #004cff, 0 0 10px #004cff; } &:hover {
&.outsider { box-shadow: 0 0 10px #00D6FF, 0 0 10px #00d6ff; } transform: scale(1.2);
&.minion { box-shadow: 0 0 10px #ff6900, 0 0 10px #ff6900; }
&.demon { box-shadow: 0 0 10px #ff0000, 0 0 10px #ff0000; }
&:before {
content: " ";
background-size: 100%;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
&:hover {
transform: scale(1.2);
}
} }
} }
}
/***** Reminder token modal ******/
ul.reminders {
@extend .tokens;
.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;
position: relative;
border-radius: 50%;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
cursor: pointer;
box-sizing: border-box;
padding-top: 65px;
transition: transform 500ms ease;
&:before {
content: " ";
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> </style>

View File

@ -1,8 +1,8 @@
import Vue from 'vue' import Vue from "vue";
import App from './App' import App from "./App";
Vue.config.productionTip = false; Vue.config.productionTip = false;
new Vue({ new Vue({
render: h => h(App), render: h => h(App)
}).$mount('#app') }).$mount("#app");

View File

@ -1,5 +1,3 @@
module.exports = { module.exports = {
publicPath: process.env.NODE_ENV === 'production' publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/"
? '/townsquare/'
: '/'
}; };