237 lines
4.9 KiB
Vue
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>
|