237 lines
4.9 KiB
Vue

<script lang="ts">
import { defineComponent, PropType } from 'vue';
interface Player {
id: number,
name: string
}
interface Model {
yourId: number,
players: Map<number, Player>,
board: string[4][4]
}
function getPlayer(model: Model, id: number): Player | undefined {
return model.players.get(id)
}
/*
Handles all game business. Sends (local) player input to the server for distribution.
Receives player input from the server and manages state accordingly.
*/
export default defineComponent({
props: {
ws: { type: WebSocket, required: true },
model: { type: Object as PropType<Model>, required: true }
},
data() {
return {
chatMessages: [] as { from: number, message: string }[],
playerName: '',
chatMessage: ''
}
},
created() {
console.log(this.model.board)
console.log(this.model)
console.log(this.model.players)
this.ws.addEventListener('message', (ev) => {
const message = JSON.parse(ev.data)
const payload = message.payload
console.log(`Message from player ${message.from}:`)
console.log(payload)
switch (payload.type) {
case 'join':
this.model.players.set(message.from, {
id: message.from,
name: ''
})
break
case 'leave':
this.model.players.delete(message.from)
break
case 'chat':
this.chatMessages.push({
from: message.from,
message: payload.message
})
break
case 'set-name':
getPlayer(this.model, message.from)!.name = payload.name
break
case 'roll':
this.model.board = payload.board
break
}
console.log(this.model)
})
},
methods: {
setName() {
this.ws.send(JSON.stringify({
type: 'set-name',
name: this.playerName
}))
},
sendChat() {
if (this.chatMessage === '') {
return
}
this.ws.send(JSON.stringify({
type: 'chat',
message: this.chatMessage
}))
this.chatMessage = ''
},
roll() {
this.ws.send(JSON.stringify({
type: 'roll'
}))
}
}
})
</script>
<template>
<div class="glebby-game">
<div class="topbar">
<h1>Glebby</h1>
</div>
<div class="game">
<h2>board</h2>
<button @click="roll">roll</button>
<div v-if="model.board" class="game-board">
<div v-for="row in model.board" class="game-row">
<div v-for="die in row" class="game-die">{{ die.toUpperCase() }}</div>
</div>
</div>
</div>
<div class="playerlist">
<h2>players</h2>
<form @submit.prevent="setName">
<input v-model="playerName" placeholder="enter your name...">
<button>set name</button>
</form>
<ul v-if="model">
<li v-for="[id, player] in model.players" :key="id">
<code>{{ player.name }}#{{ player.id }}</code>
</li>
</ul>
</div>
<div class="chatbox">
<h2>chat</h2>
<div class="chatbox-scroller">
<div class="chatbox-scroller-content">
<div v-for="message in chatMessages" class="chatbox-message">
<code v-if="model.players.get(message.from)">{{ model.players.get(message.from)!.name }}#{{ message.from }}:</code> {{ message.message }}
</div>
</div>
</div>
<form @submit.prevent="sendChat">
<input v-model="chatMessage">
<button>Post</button>
</form>
</div>
</div>
</template>
<style>
body {
margin: 0;
}
.glebby-game {
width: 100vw;
height: 100vh;
display: grid;
grid-template-columns: 1fr 20em;
grid-template-rows: min-content 1fr 12em;
grid-template-areas:
"topbar topbar"
"game playerlist"
"chatbox playerlist";
gap: .5em;
padding: .5em;
box-sizing: border-box;
}
.topbar,
.game,
.playerlist,
.chatbox {
background-color: #f0f0f0;
padding: .5em;
}
.topbar {
grid-area: topbar;
}
.game {
grid-area: game;
}
.playerlist {
grid-area: playerlist;
}
.chatbox {
grid-area: chatbox;
}
h1, h2 {
margin: 0;
font-family: monospace;
}
.game-board {
display: flex;
gap: 1em;
flex-direction: column;
align-items: center;
}
.game-row {
display: flex;
gap: 1em;
}
.game-die {
width: 2em;
height: 2em;
display: flex;
justify-content: center;
align-items: center;
background-color: white;
font-family: monospace;
font-size: 1.5em;
font-weight: bold;
}
.chatbox {
display: grid;
grid-template-rows: min-content 1fr min-content;
}
.chatbox-scroller {
overflow-y: scroll;
display: flex;
flex-direction: column-reverse;
}
.chatbox-scroller {
border: 1px dashed #888;
margin: .25em 0;
padding: .25em;
}
.chatbox-message code {
color: #888;
}
</style>