375 lines
7.7 KiB
Vue
375 lines
7.7 KiB
Vue
<template>
|
|
<section class="lobby">
|
|
<TopNav />
|
|
<section v-if="!inRoom" class="body">
|
|
<section id="info">
|
|
<b-field label="Your name">
|
|
<b-input :disabled="busy" v-model="playerName"></b-input>
|
|
</b-field>
|
|
</section>
|
|
<section id="join">
|
|
<header>
|
|
<h1>Join someone's session</h1>
|
|
</header>
|
|
<b-field label="Session ID" class="only-full">
|
|
<b-input :disabled="busy" v-model="joinSessionID"></b-input>
|
|
</b-field>
|
|
<div class="center submit only-full">
|
|
<b-button
|
|
type="is-primary"
|
|
@click="join"
|
|
class="wide"
|
|
:disabled="busy || !canJoin"
|
|
>
|
|
Join
|
|
</b-button>
|
|
</div>
|
|
<div class="only-mobile">
|
|
<b-field class="full">
|
|
<b-input
|
|
:disabled="busy"
|
|
placeholder="Session ID"
|
|
v-model="joinSessionID"
|
|
></b-input>
|
|
<p class="control">
|
|
<b-button
|
|
type="is-primary"
|
|
@click="join"
|
|
:disabled="busy || !canJoin"
|
|
>
|
|
Join
|
|
</b-button>
|
|
</p>
|
|
</b-field>
|
|
</div>
|
|
</section>
|
|
<section id="host">
|
|
<header>
|
|
<h1>Host a new session</h1>
|
|
</header>
|
|
<div class="columns">
|
|
<div class="column">
|
|
<b-field label="Max players">
|
|
<b-numberinput
|
|
:disabled="busy"
|
|
controls-position="compact"
|
|
v-model="hostMaxPlayers"
|
|
min="2"
|
|
max="8"
|
|
></b-numberinput>
|
|
</b-field>
|
|
</div>
|
|
<div class="column">
|
|
<b-field label="Password">
|
|
<b-input :disabled="busy" v-model="hostPassword"></b-input>
|
|
</b-field>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="center">
|
|
<b-button
|
|
:disabled="busy"
|
|
type="is-primary"
|
|
@click="create"
|
|
class="wide"
|
|
>
|
|
Create
|
|
</b-button>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
<section class="room" v-else>
|
|
<section class="info">
|
|
Invite your friends:
|
|
<span class="selectable"
|
|
><a :href="inviteLink">{{ inviteLink }}</a></span
|
|
>
|
|
</section>
|
|
<section class="chat"></section>
|
|
<section class="players">
|
|
<header>Players</header>
|
|
<ul>
|
|
<li class="selectable" v-for="player in players" :key="player">
|
|
{{ player }}
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section class="player-options">
|
|
<b-field>
|
|
<b-input :disabled="busy" v-model="wantedName"></b-input>
|
|
<p class="control">
|
|
<b-button
|
|
@click="changeName"
|
|
type="is-primary"
|
|
:disabled="!nameAvailable"
|
|
>Change name</b-button
|
|
>
|
|
</p>
|
|
</b-field>
|
|
<b-button type="is-danger">Leave room</b-button>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "@/assets/scss/_variables.scss";
|
|
|
|
.lobby {
|
|
background: url("../assets/images/backgrounds/menubg.webp") center;
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-flow: column;
|
|
}
|
|
|
|
.body {
|
|
flex: 1;
|
|
display: grid;
|
|
padding: 10px;
|
|
padding-top: 0;
|
|
grid-template: 150px 1fr / 1fr 1fr;
|
|
|
|
#info {
|
|
grid-row: 1;
|
|
grid-column: 1 / end;
|
|
}
|
|
|
|
#join,
|
|
#host {
|
|
grid-row: 2;
|
|
}
|
|
|
|
#join {
|
|
grid-column: 2;
|
|
}
|
|
#host {
|
|
grid-column: 1;
|
|
}
|
|
|
|
section {
|
|
margin: 10px;
|
|
border: 1px solid rgba($white, 20%);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
header {
|
|
font-family: $fantasy;
|
|
h1 {
|
|
text-align: center;
|
|
font-size: 18pt;
|
|
}
|
|
margin-bottom: 20px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.wide {
|
|
min-width: 30%;
|
|
margin: 10px auto;
|
|
}
|
|
|
|
.full {
|
|
width: 100%;
|
|
margin: 10px;
|
|
}
|
|
|
|
.center {
|
|
width: 100%;
|
|
text-align: center;
|
|
}
|
|
|
|
.full-btn {
|
|
flex: 1;
|
|
:global(.button) {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.room {
|
|
padding: 10px 20px;
|
|
margin: 10px;
|
|
border: 1px solid rgba($white, 20%);
|
|
border-radius: 10px;
|
|
display: grid;
|
|
grid-template: 50px 1fr / 200px 1fr 300px;
|
|
|
|
.info {
|
|
grid-column: 1 / max;
|
|
grid-row: 1;
|
|
}
|
|
|
|
.players {
|
|
header {
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
grid-row: 2 / max;
|
|
grid-column: 1;
|
|
}
|
|
|
|
.chat {
|
|
grid-row: 2 / max;
|
|
grid-column: 2;
|
|
}
|
|
|
|
.player-options {
|
|
grid-row: 2 / max;
|
|
grid-column: 3;
|
|
}
|
|
}
|
|
|
|
.only-mobile {
|
|
display: none;
|
|
}
|
|
|
|
.selectable {
|
|
user-select: all;
|
|
}
|
|
|
|
@media (max-width: 500px) {
|
|
.only-full {
|
|
display: none;
|
|
}
|
|
.only-mobile {
|
|
display: inherit;
|
|
}
|
|
.body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
section {
|
|
padding: 10px;
|
|
header h1 {
|
|
font-size: 14pt;
|
|
}
|
|
}
|
|
}
|
|
|
|
.room {
|
|
display: flex;
|
|
flex-flow: column;
|
|
& > * {
|
|
padding: 10px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script lang="ts">
|
|
import { Component, Vue } from "vue-property-decorator";
|
|
import TopNav from "@/components/Navigation/TopNav.vue";
|
|
import { StartServerOptions, ConnectOptions } from "@/store/network/types";
|
|
import { Action, Getter } from "vuex-class";
|
|
import { Client, NetworkMessage } from "@/network";
|
|
|
|
const networkNS = { namespace: "network" };
|
|
|
|
@Component({
|
|
components: {
|
|
TopNav
|
|
}
|
|
})
|
|
export default class Lobby extends Vue {
|
|
private playerName!: string;
|
|
private hostMaxPlayers!: number;
|
|
private hostPassword!: string;
|
|
private joinSessionID!: string;
|
|
private wantedName!: string;
|
|
|
|
@Action("startServer", networkNS)
|
|
private startServer!: (options: StartServerOptions) => void;
|
|
|
|
@Action("sendMessage", networkNS)
|
|
private sendMessage!: (message: NetworkMessage) => void;
|
|
|
|
@Action("connect", networkNS)
|
|
private connect!: (options: ConnectOptions) => void;
|
|
|
|
@Getter("inRoom", networkNS)
|
|
private inRoom!: boolean;
|
|
|
|
@Getter("busy", networkNS)
|
|
private busy!: boolean;
|
|
|
|
@Getter("sessionID", networkNS)
|
|
private sessionID!: string | null;
|
|
|
|
@Getter("players", networkNS)
|
|
private players!: string[];
|
|
|
|
private data() {
|
|
const playerName =
|
|
"Guest-" +
|
|
Math.random()
|
|
.toString()
|
|
.slice(2, 8);
|
|
return {
|
|
playerName,
|
|
hostMaxPlayers: 8,
|
|
hostPassword: "",
|
|
joinSessionID: "",
|
|
wantedName: playerName
|
|
};
|
|
}
|
|
|
|
private mounted() {
|
|
if ("id" in this.$route.params) {
|
|
this.joinSessionID = this.$route.params.id;
|
|
this.join();
|
|
}
|
|
}
|
|
|
|
private async create() {
|
|
this.wantedName = this.playerName;
|
|
this.startServer({
|
|
playerInfo: {
|
|
name: this.playerName
|
|
},
|
|
roomInfo: {
|
|
max_players: this.hostMaxPlayers,
|
|
password: this.hostPassword
|
|
}
|
|
});
|
|
}
|
|
|
|
private async join() {
|
|
this.wantedName = this.playerName;
|
|
this.connect({
|
|
serverID: this.joinSessionID,
|
|
playerInfo: {
|
|
name: this.playerName
|
|
}
|
|
});
|
|
}
|
|
|
|
private async changeName() {
|
|
this.sendMessage({
|
|
kind: "rename",
|
|
oldname: this.playerName,
|
|
newname: this.wantedName
|
|
});
|
|
}
|
|
|
|
private get canJoin(): boolean {
|
|
return this.joinSessionID != "";
|
|
}
|
|
|
|
private get inviteLink(): string {
|
|
let subpath = "";
|
|
const joinIndex = location.pathname.indexOf("/join");
|
|
if (joinIndex > 0) {
|
|
subpath = location.pathname.substring(0, joinIndex);
|
|
}
|
|
const lobbyIndex = location.pathname.indexOf("/lobby");
|
|
if (lobbyIndex > 0) {
|
|
subpath = location.pathname.substring(0, lobbyIndex);
|
|
}
|
|
return `${location.origin}${subpath}/join/${this.sessionID}`;
|
|
}
|
|
|
|
private get nameAvailable(): boolean {
|
|
return this.wantedName != "" && !this.players.includes(this.wantedName);
|
|
}
|
|
}
|
|
</script>
|