moved token into own component

fixed a lot of styling issues
added button styles
updated favicon
This commit is contained in:
Steffen 2020-04-15 22:40:58 +02:00
parent ed283598d7
commit 3d66b67cbf
No known key found for this signature in database
GPG Key ID: 764D74E98267DFC6
32 changed files with 214 additions and 125 deletions

View File

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Blood on the Clocktower Virtual Town Square</title> <title>Blood on the Clocktower Town Square</title>
<link rel="apple-touch-icon" sizes="57x57" href="static/apple-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="static/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="static/apple-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="static/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="static/apple-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="static/apple-icon-72x72.png">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -310,4 +310,41 @@ ul.editions .edition {
1px 1px 0 #000, 0 0 5px rgba(0, 0, 0, 0.75); 1px 1px 0 #000, 0 0 5px rgba(0, 0, 0, 0.75);
cursor: pointer; cursor: pointer;
} }
// Buttons
.button {
padding: 0;
border: solid 0.125em transparent;
border-radius: 15px;
box-shadow: inset 0 1px 1px #9c9c9c, 0 0 10px #000;
background: radial-gradient(
at 0 -15%,
rgba(#fff, 0.07) 70%,
rgba(#fff, 0) 71%
)
0 0/ 80% 90% no-repeat content-box,
linear-gradient(#4e4e4e, #040404) content-box,
linear-gradient(#292929, #010101) border-box;
color: white;
font-weight: bold;
text-shadow: 1px 1px rgba(0, 0, 0, 0.5);
line-height: 40px;
margin: 5px auto;
font-size: 1em;
cursor: pointer;
transition: all 200ms;
&:hover {
color: red;
}
&:disabled {
color: gray;
}
&:before,
&:after {
content: " ";
display: inline-block;
width: 10px;
height: 10px;
}
}
</style> </style>

View File

@ -8,6 +8,7 @@
aria-describedby="modalDescription" aria-describedby="modalDescription"
@click.stop="" @click.stop=""
> >
<font-awesome-icon @click="close" class="close" icon="times-circle" />
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
@ -66,6 +67,16 @@ export default {
font-size: 75%; font-size: 75%;
line-height: 100%; line-height: 100%;
} }
> .close {
position: absolute;
right: 20px;
top: 20px;
cursor: pointer;
z-index: 5;
&:hover {
color: red;
}
}
} }
.modal-fade-enter, .modal-fade-enter,

View File

@ -10,16 +10,7 @@
> >
<div class="shroud" @click="toggleStatus()"></div> <div class="shroud" @click="toggleStatus()"></div>
<div class="life" @click="toggleStatus()"></div> <div class="life" @click="toggleStatus()"></div>
<div class="token" @click="changeRole()" :class="[player.role.id]"> <Token :role="player.role" @set-role="setRole" />
<span class="leaf-left" v-if="player.role.firstNight"></span>
<span class="leaf-right" v-if="player.role.otherNight"></span>
<span
v-if="player.role.reminders && player.role.reminders.length"
v-bind:class="['leaf-top' + player.role.reminders.length]"
></span>
<span class="leaf-orange" v-if="player.role.setup"></span>
<div>{{ player.role.name }}</div>
</div>
<div class="ability" v-if="player.role.ability"> <div class="ability" v-if="player.role.ability">
{{ player.role.ability }} {{ player.role.ability }}
</div> </div>
@ -46,7 +37,12 @@
</template> </template>
<script> <script>
import Token from "./Token";
export default { export default {
components: {
Token
},
props: { props: {
player: { player: {
type: Object, type: Object,
@ -79,7 +75,7 @@ export default {
this.$set(this.player, "hasDied", !this.player.hasDied); this.$set(this.player, "hasDied", !this.player.hasDied);
} }
}, },
changeRole() { setRole() {
this.$emit("set-role", this.player); this.$emit("set-role", this.player);
}, },
changeName() { changeName() {
@ -207,76 +203,19 @@ export default {
} }
/***** Role token ******/ /***** Role token ******/
.circle .token { .player .token {
border-radius: 50%;
height: $token + 6px;
width: $token + 6px;
background: url("../assets/token.png") center center;
background-size: 100%;
text-align: center;
color: black;
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;
font-family: "Papyrus", serif;
border: 3px solid black;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
cursor: pointer;
transition: transform 200ms ease-in-out;
transform: perspective(400px) rotateY(0deg);
backface-visibility: hidden;
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 0; top: 0;
margin-left: ($token + 6) / -2; margin-left: ($token + 6) / -2;
height: $token + 6px;
&:before { width: $token + 6px;
content: " "; transition: transform 200ms ease-in-out;
background-size: 100%; transform: perspective(400px) rotateY(0deg);
position: absolute; backface-visibility: hidden;
width: 100%;
height: 100%;
left: 0;
top: 0;
} }
span { #townsquare.public .circle .token {
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 {
transform: perspective(400px) rotateY(-180deg); transform: perspective(400px) rotateY(-180deg);
} }

View File

@ -12,31 +12,32 @@
</li> </li>
<li <li
v-for="role in teamRoles" v-for="role in teamRoles"
class="token" v-bind:class="[role.team, role.selected ? 'selected' : '']"
v-bind:class="[role.id, role.team, role.selected ? 'selected' : '']"
v-bind:key="role.id" v-bind:key="role.id"
@click="role.selected = !role.selected" @click="role.selected = !role.selected"
> >
{{ role.name }} <Token :role="role" />
</li> </li>
</ul> </ul>
<button <div class="button"
@click="assignRoles()" @click="assignRoles()"
v-bind:disabled="selectedRoles > nontravelerPlayers || !selectedRoles" v-bind:disabled="selectedRoles > nontravelerPlayers || !selectedRoles"
> >
Assign {{ selectedRoles }} roles randomly Assign {{ selectedRoles }} roles randomly
</button> </div>
</Modal> </Modal>
</template> </template>
<script> <script>
import Modal from "./Modal"; import Modal from "./Modal";
import gameJSON from "./../game"; import gameJSON from "./../game";
import Token from "./Token";
const randomElement = arr => arr[Math.floor(Math.random() * arr.length)]; const randomElement = arr => arr[Math.floor(Math.random() * arr.length)];
export default { export default {
components: { components: {
Token,
Modal Modal
}, },
props: { props: {
@ -62,7 +63,8 @@ export default {
computed: { computed: {
nontravelerPlayers: function() { nontravelerPlayers: function() {
return Math.min( return Math.min(
this.players.filter(({ role }) => role && role.team !== "traveler").length, this.players.filter(({ role }) => role && role.team !== "traveler")
.length,
15 15
); );
}, },
@ -129,12 +131,25 @@ export default {
@import "../vars.scss"; @import "../vars.scss";
.roles .modal ul.tokens { .roles .modal ul.tokens {
padding-left: 20px; padding-left: 55px;
li {
opacity: 0.5;
transition: all 250ms;
&.selected {
opacity: 1;
}
}
.count { .count {
opacity: 1;
position: absolute; position: absolute;
left: 0; left: 0;
top: 40px;
font-weight: bold; font-weight: bold;
padding: 5px; line-height: 50px;
text-align: center;
font-size: 100%;
width: 50px;
height: 50px;
&.townsfolk { &.townsfolk {
color: $townsfolk; color: $townsfolk;
} }
@ -148,11 +163,5 @@ export default {
color: $demon; color: $demon;
} }
} }
.token {
opacity: 0.5;
&.selected {
opacity: 1;
}
}
} }
</style> </style>

110
src/components/Token.vue Normal file
View File

@ -0,0 +1,110 @@
<template>
<div class="token" @click="setRole" :class="[role.id]">
<span class="leaf-left" v-if="role.firstNight"></span>
<span class="leaf-right" v-if="role.otherNight"></span>
<span
v-if="role.reminders && role.reminders.length"
v-bind:class="['leaf-top' + role.reminders.length]"
></span>
<span class="leaf-orange" v-if="role.setup"></span>
<div>{{ role.name }}</div>
</div>
</template>
<script>
export default {
name: "Token",
props: {
role: {
type: Object,
required: true
}
},
data() {
return {};
},
methods: {
setRole() {
this.$emit("set-role");
}
}
};
</script>
<style scoped lang="scss">
.token {
border-radius: 50%;
height: 100%;
width: 100%;
background: url("../assets/token.png") center center;
background-size: 100%;
text-align: center;
color: black;
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);
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;
}
div {
position: absolute;
top: 73%;
width: 100%;
line-height: 100%;
}
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");
}
}
}
</style>

View File

@ -1,5 +1,10 @@
<template> <template>
<div id="townsquare" class="square" v-bind:class="{ public: isPublic }" v-bind:style="{ zoom: zoom }"> <div
id="townsquare"
class="square"
v-bind:class="{ public: isPublic }"
v-bind:style="{ zoom: zoom }"
>
<ul class="circle" v-bind:class="['size-' + players.length]"> <ul class="circle" v-bind:class="['size-' + players.length]">
<Player <Player
v-for="(player, index) in players" v-for="(player, index) in players"
@ -12,7 +17,7 @@
@remove-player="removePlayer" @remove-player="removePlayer"
></Player> ></Player>
</ul> </ul>
<Modal v-show="availableReminders.length" @close="closeModal"> <Modal v-show="availableReminders.length && selectedPlayer" @close="closeModal">
<h2>Choose a reminder token:</h2> <h2>Choose a reminder token:</h2>
<ul class="reminders"> <ul class="reminders">
<li <li
@ -26,17 +31,16 @@
</li> </li>
</ul> </ul>
</Modal> </Modal>
<Modal v-show="availableRoles.length" @close="closeModal"> <Modal v-show="availableRoles.length && selectedPlayer" @close="closeModal">
<h2>Choose a new role:</h2> <h2>Choose a new role:</h2>
<ul class="tokens"> <ul class="tokens">
<li <li
v-for="role in availableRoles" v-for="role in availableRoles"
class="token" v-bind:class="[role.team]"
v-bind:class="[role.id, role.team]"
v-bind:key="role.id" v-bind:key="role.id"
@click="setRole(role)" @click="setRole(role)"
> >
{{ role.name }} <Token :role="role" />
</li> </li>
</ul> </ul>
</Modal> </Modal>
@ -46,9 +50,11 @@
<script> <script>
import Player from "./Player"; import Player from "./Player";
import Modal from "./Modal"; import Modal from "./Modal";
import Token from "./Token";
export default { export default {
components: { components: {
Token,
Modal, Modal,
Player Player
}, },
@ -79,8 +85,9 @@ export default {
}, },
methods: { methods: {
openReminderModal(player) { openReminderModal(player) {
this.selectedPlayer = player; this.availableRoles = [];
this.availableReminders = []; this.availableReminders = [];
this.selectedPlayer = player;
this.roles.forEach(role => { this.roles.forEach(role => {
if (this.players.some(p => p.role.id === role.id)) { if (this.players.some(p => p.role.id === role.id)) {
this.availableReminders = [ this.availableReminders = [
@ -93,10 +100,11 @@ export default {
this.availableReminders.push({ role: "evil", name: "Evil" }); this.availableReminders.push({ role: "evil", name: "Evil" });
}, },
openRoleModal(player) { openRoleModal(player) {
this.selectedPlayer = player;
this.availableRoles = []; this.availableRoles = [];
this.availableReminders = [];
this.selectedPlayer = player;
this.roles.forEach(role => { this.roles.forEach(role => {
if (role.id !== player.role) { if (role.id !== player.role.id) {
this.availableRoles.push(role); this.availableRoles.push(role);
} }
}); });
@ -112,8 +120,6 @@ export default {
}, },
closeModal() { closeModal() {
this.selectedPlayer = false; this.selectedPlayer = false;
this.availableReminders = [];
this.availableRoles = [];
}, },
removePlayer(player) { removePlayer(player) {
if (confirm(`Do you really want to remove ${player.name}?`)) { if (confirm(`Do you really want to remove ${player.name}?`)) {
@ -215,23 +221,11 @@ export default {
} }
/***** Role token modal ******/ /***** Role token modal ******/
ul.tokens .token { ul.tokens li {
border-radius: 50%; border-radius: 50%;
height: 120px; height: 120px;
width: 120px; width: 120px;
background: url("../assets/token.png") center center;
background-size: 100%;
text-align: center;
color: black;
margin: 5px; 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;
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; transition: transform 500ms ease;
&.townsfolk { &.townsfolk {
@ -249,17 +243,6 @@ ul.tokens .token {
&.traveler { &.traveler {
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler; box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
} }
&:before {
content: " ";
background-size: 100%;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
&:hover { &:hover {
transform: scale(1.2); transform: scale(1.2);
} }