npm run format

This commit is contained in:
Dennis Postma 2024-06-02 02:35:42 +02:00
parent 8329afe897
commit 86b80f8244
28 changed files with 572 additions and 562 deletions

View File

@ -3,6 +3,6 @@
"semi": false, "semi": false,
"tabWidth": 2, "tabWidth": 2,
"singleQuote": true, "singleQuote": true,
"printWidth": 100, "printWidth": 300,
"trailingComma": "none" "trailingComma": "none"
} }

View File

@ -15,20 +15,23 @@ import Register from '@/components/screens/Register.vue'
import Characters from '@/components/screens/Characters.vue' import Characters from '@/components/screens/Characters.vue'
import Game from '@/components/Game.vue' import Game from '@/components/Game.vue'
const screen:Ref<string> = ref('login'); const screen: Ref<string> = ref('login')
const socket = useSocketStore(); const socket = useSocketStore()
socket.$subscribe((mutation, state) => { socket.$subscribe(
if (!state.connection) { (mutation, state) => {
screen.value = 'login'; if (!state.connection) {
} screen.value = 'login'
if (state.connection){
screen.value = 'characters';
if (state.character) {
screen.value = 'game';
} }
}
}, { detached: true }) if (state.connection) {
screen.value = 'characters'
if (state.character) {
screen.value = 'game'
}
}
},
{ detached: true }
)
</script> </script>

View File

@ -2,7 +2,7 @@
$white: #fff; $white: #fff;
$black: #000; $black: #000;
$purple: #4741e6; $purple: #4741e6;
$lilac: #7B76FF; $lilac: #7b76ff;
$light-blue: #00c2ff; $light-blue: #00c2ff;
$red: #ff0000; $red: #ff0000;
$gray: #7f7f7f; $gray: #7f7f7f;

View File

@ -96,10 +96,17 @@
text-align: center; text-align: center;
font-size: 3rem; font-size: 3rem;
color: $white; color: $white;
text-shadow: -1px -1px 0 $gray, 1px -1px 0 $gray, -1px 1px 0 $gray, 1px 1px 0 $gray; text-shadow:
-1px -1px 0 $gray,
1px -1px 0 $gray,
-1px 1px 0 $gray,
1px 1px 0 $gray;
} }
p, a, li, label { p,
a,
li,
label {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
color: $white; color: $white;
} }

View File

@ -1,27 +1,32 @@
@import "@/assets/scss/_variables"; @import '@/assets/scss/_variables';
// Fonts // Fonts
@font-face { @font-face {
font-family: GentiumPlus; font-family: GentiumPlus;
src: url('@/assets/fonts/Gentium_plus.ttf'); src: url('@/assets/fonts/Gentium_plus.ttf');
} }
body { body {
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none; scrollbar-width: none;
background: $black; background: $black;
margin: 0; margin: 0;
// Disable selection, might wanna comment when debugging // Disable selection, might wanna comment when debugging
-webkit-user-select: none; /* Safari */ -webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */ -ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */ user-select: none; /* Standard syntax */
} }
h1, h2, h3, h4, h5, h6 { h1,
font-family: GentiumPlus, serif; h2,
h3,
h4,
h5,
h6 {
font-family: GentiumPlus, serif;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
display: none; display: none;
} }

View File

@ -7,55 +7,55 @@ import { ref } from 'vue'
import config from '@/config' import config from '@/config'
const scene = useScene() const scene = useScene()
const pointer_tile = ref(undefined); const pointer_tile = ref(undefined)
const props = defineProps({ const props = defineProps({
layer: Phaser.Tilemaps.TilemapLayer layer: Phaser.Tilemaps.TilemapLayer
}) })
const waypoint = ref({ const waypoint = ref({
visible: false, visible: false,
x: 0, x: 0,
y: 0, y: 0
}) })
function onPointerMove(pointer: Phaser.Input.Pointer) { function onPointerMove(pointer: Phaser.Input.Pointer) {
const px = scene.cameras.main.worldView.x + pointer.x; const px = scene.cameras.main.worldView.x + pointer.x
const py = scene.cameras.main.worldView.y + pointer.y; const py = scene.cameras.main.worldView.y + pointer.y
pointer_tile.value = getTile(px, py, props.layer); pointer_tile.value = getTile(px, py, props.layer)
if (pointer_tile.value) { if (pointer_tile.value) {
waypoint.value.visible = true; waypoint.value.visible = true
// Convert tile coordinates to world coordinates // Convert tile coordinates to world coordinates
const worldPoint = props.layer.tileToWorldXY(pointer_tile.value.x, pointer_tile.value.y); const worldPoint = props.layer.tileToWorldXY(pointer_tile.value.x, pointer_tile.value.y)
waypoint.value.x = worldPoint.x + config.tile_size.y; waypoint.value.x = worldPoint.x + config.tile_size.y
waypoint.value.y = worldPoint.y + config.tile_size.y; waypoint.value.y = worldPoint.y + config.tile_size.y
} else { } else {
waypoint.value.visible = false; waypoint.value.visible = false
} }
} }
scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove); scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
function getTile (x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined { function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y); const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
// console.log(x,y); // console.log(x,y);
// console.log('tile', tile); // console.log('tile', tile);
if (!tile) { if (!tile) {
return undefined; return undefined
} }
return tile; return tile
} }
function getTileAt (iX: number, iY: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined { function getTileAt(iX: number, iY: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
const tile: Phaser.Tilemaps.Tile = layer.getTileAt(iX, iY); const tile: Phaser.Tilemaps.Tile = layer.getTileAt(iX, iY)
if (tile) return tile; if (tile) return tile
else return undefined; else return undefined
} }
function getDepth(tile: Phaser.Tilemaps.Tile): number { function getDepth(tile: Phaser.Tilemaps.Tile): number {
return 32; return 32
} }
</script> </script>

View File

@ -18,7 +18,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import 'phaser'; import 'phaser'
import { Game, Scene } from 'phavuer' import { Game, Scene } from 'phavuer'
import World from '@/components/World.vue' import World from '@/components/World.vue'
import Pointer = Phaser.Input.Pointer import Pointer = Phaser.Input.Pointer
@ -28,10 +28,10 @@ import Chat from '@/components/game/Chat.vue'
import Menubar from '@/components/game/Menu.vue' import Menubar from '@/components/game/Menu.vue'
import { onUnmounted } from 'vue' import { onUnmounted } from 'vue'
const socket = useSocketStore(); const socket = useSocketStore()
onUnmounted(() => { onUnmounted(() => {
socket.disconnectSocket(); socket.disconnectSocket()
}) })
const gameConfig = { const gameConfig = {
@ -39,13 +39,13 @@ const gameConfig = {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight, height: window.innerHeight,
type: Phaser.WEBGL, type: Phaser.WEBGL,
pixelArt: true, pixelArt: true
} }
const bootGame = (game: Phaser.Game) => { const bootGame = (game: Phaser.Game) => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
game.scale.resize(window.innerWidth, window.innerHeight); game.scale.resize(window.innerWidth, window.innerHeight)
}); })
} }
const preloadScene = (scene: Phaser.Scene) => { const preloadScene = (scene: Phaser.Scene) => {
@ -54,19 +54,22 @@ const preloadScene = (scene: Phaser.Scene) => {
* Write logic that downloads all assets from out websocket or http server in base64 format * Write logic that downloads all assets from out websocket or http server in base64 format
* Don't forget to check how intensive that operation is with sockets for performance * Don't forget to check how intensive that operation is with sockets for performance
*/ */
scene.load.image('tiles', '/assets/tiles/default.png'); scene.load.image('tiles', '/assets/tiles/default.png')
scene.load.image('waypoint', '/assets/waypoint.png'); scene.load.image('waypoint', '/assets/waypoint.png')
scene.textures.addBase64('player', 'data:image/png;base64,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') scene.textures.addBase64(
'player',
'data:image/png;base64,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'
)
} }
const bootScene = (scene: Phaser.Scene) => { const bootScene = (scene: Phaser.Scene) => {
// Camera drag system // Camera drag system
let cam = scene.cameras.main; let cam = scene.cameras.main
scene.input.on("pointermove", function (pointer: Pointer) { scene.input.on('pointermove', function (pointer: Pointer) {
if (!pointer.isDown) return; if (!pointer.isDown) return
cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom; cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom
cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom; cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom
}); })
// const grid = scene.add.grid(0, 0, window.innerWidth, window.innerHeight, 64, 32, 0, 0, 0xff0000, 0.5).setOrigin(0, 0); // const grid = scene.add.grid(0, 0, window.innerWidth, window.innerHeight, 64, 32, 0, 0, 0xff0000, 0.5).setOrigin(0, 0);
// //
@ -82,10 +85,11 @@ const bootScene = (scene: Phaser.Scene) => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; height: 100vh;
padding:30px; padding: 30px;
position: relative; position: relative;
} }
.top-ui, .bottom-ui { .top-ui,
.bottom-ui {
display: flex; display: flex;
position: absolute; position: absolute;
left: 0; left: 0;

View File

@ -19,18 +19,18 @@ import { useZoneStore } from '@/stores/zone'
// Phavuer logic // Phavuer logic
let scene = useScene() let scene = useScene()
let tilemapLayer = ref(); let tilemapLayer = ref()
let zoneData = new Phaser.Tilemaps.MapData({ let zoneData = new Phaser.Tilemaps.MapData({
width: 10, // @TODO : get this from the server width: 10, // @TODO : get this from the server
height: 10, // @TODO : get this from the server height: 10, // @TODO : get this from the server
tileWidth: config.tile_size.x, tileWidth: config.tile_size.x,
tileHeight: config.tile_size.y, tileHeight: config.tile_size.y,
orientation: Phaser.Tilemaps.Orientation.ISOMETRIC, orientation: Phaser.Tilemaps.Orientation.ISOMETRIC,
format: Phaser.Tilemaps.Formats.ARRAY_2D, format: Phaser.Tilemaps.Formats.ARRAY_2D
}); })
let tileMap = new Phaser.Tilemaps.Tilemap(scene, zoneData); let tileMap = new Phaser.Tilemaps.Tilemap(scene, zoneData)
let tileset: any = tileMap.addTilesetImage('default', 'tiles'); let tileset: any = tileMap.addTilesetImage('default', 'tiles')
let layer: (typeof TilemapLayer|null) = tileMap.createBlankLayer('layer', tileset, 0, config.tile_size.y); let layer: typeof TilemapLayer | null = tileMap.createBlankLayer('layer', tileset, 0, config.tile_size.y)
// center camera // center camera
const centerY = (tileMap.height * tileMap.tileHeight) / 2 const centerY = (tileMap.height * tileMap.tileHeight) / 2
@ -38,24 +38,28 @@ const centerX = (tileMap.width * tileMap.tileWidth) / 2
scene.cameras.main.centerOn(centerX, centerY) scene.cameras.main.centerOn(centerX, centerY)
// Multiplayer / server logics // Multiplayer / server logics
const zoneStore = useZoneStore(); const zoneStore = useZoneStore()
const socket = useSocketStore(); const socket = useSocketStore()
// Watch for changes in the zoneStore and update the layer // Watch for changes in the zoneStore and update the layer
watch (() => zoneStore.tiles, () => { // @TODO : change to tiles for when loading other maps watch(
zoneStore.getTiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y))); () => zoneStore.tiles,
}, { deep: true }) () => {
// @TODO : change to tiles for when loading other maps
zoneStore.getTiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y)))
},
{ deep: true }
)
// Load the zone from the server // Load the zone from the server
onBeforeMount(() => { onBeforeMount(() => {
socket.getConnection.emit('character:connect'); socket.getConnection.emit('character:connect')
socket.getConnection.emit('character:zone:load'); socket.getConnection.emit('character:zone:load')
}) })
// Listen for the zone event from the server and load the zone // Listen for the zone event from the server and load the zone
socket.getConnection.on('character:zone:load', (data) => { socket.getConnection.on('character:zone:load', (data) => {
console.log('character:zone:load', data); console.log('character:zone:load', data)
zoneStore.loadTiles(data.zone.tiles) zoneStore.loadTiles(data.zone.tiles)
/** /**
* @TODO * @TODO
@ -75,10 +79,10 @@ socket.getConnection.on('character:zone:load', (data) => {
socket.getConnection.on('player_join', (data) => { socket.getConnection.on('player_join', (data) => {
console.log('player_join', data) console.log('player_join', data)
if (data.id === socket.getConnection.id) { if (data.id === socket.getConnection.id) {
console.log('self'); console.log('self')
return; return
} }
zoneStore.addPlayer(data); zoneStore.addPlayer(data)
}) })
socket.getConnection.on('ping', (data) => { socket.getConnection.on('ping', (data) => {

View File

@ -1,13 +1,11 @@
<template> <template>
<div class="chat-wrapper"> <div class="chat-wrapper">
<input placeholder="Type something..." /> <input placeholder="Type something..." />
<img src="/assets/icons/submit-icon.svg"> <img src="/assets/icons/submit-icon.svg" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts"></script>
</script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/assets/scss/main'; @import '@/assets/scss/main';
@ -29,7 +27,6 @@
background-color: rgba($white, 0.85); background-color: rgba($white, 0.85);
border: 2px solid $white; border: 2px solid $white;
color: black; color: black;
} }
img { img {
@ -39,5 +36,4 @@
height: 1.875rem; height: 1.875rem;
} }
} }
</style> </style>

View File

@ -1,54 +1,52 @@
<template> <template>
<div class="hud-wrapper"> <div class="hud-wrapper">
<div class="profile"> <div class="profile">
<img src="/assets/avatar/default/head.png"> <img src="/assets/avatar/default/head.png" />
</div>
<div class="hud">
<div class="stats"></div>
</div>
</div> </div>
<div class="hud">
<div class="stats"></div>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts"></script>
</script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/assets/scss/main'; @import '@/assets/scss/main';
.hud-wrapper { .hud-wrapper {
position: relative; position: relative;
.profile { .profile {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 4rem; width: 4rem;
height: 4rem; height: 4rem;
background-color: rgba($white, 0.8); background-color: rgba($white, 0.8);
border-radius: 100%; border-radius: 100%;
border: 2px solid $white; border: 2px solid $white;
z-index: 1; z-index: 1;
img { img {
width: 2rem; width: 2rem;
position: absolute; position: absolute;
left: calc(50% - 1rem); left: calc(50% - 1rem);
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
}
}
.hud {
position: absolute;
left: 2rem;
top: 2rem;
width: 15rem;
height: 5rem;
background-image: url('/assets/clouds.png');
background-position: center;
background-size: cover;
// background-color: rgba(127, 127, 127, 0.7);
clip-path: ellipse(3rem 3rem at 0% 0%) invert;
border-radius: 1rem;
border: 2px solid $white;
}
} }
}
.hud {
position: absolute;
left: 2rem;
top: 2rem;
width: 15rem;
height: 5rem;
background-image: url('/assets/clouds.png');
background-position: center;
background-size: cover;
// background-color: rgba(127, 127, 127, 0.7);
clip-path: ellipse(3rem 3rem at 0% 0%) invert;
border-radius: 1rem;
border: 2px solid $white;
}
}
</style> </style>

View File

@ -1,106 +1,104 @@
<template> <template>
<div class="menu-wrapper"> <div class="menu-wrapper">
<ul class="menu"> <ul class="menu">
<li class="menu-item"> <li class="menu-item">
<p>Chat</p> <p>Chat</p>
<a> <a>
<img src="/assets/icons/chat.png"> <img src="/assets/icons/chat.png" />
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<p>World</p> <p>World</p>
<a> <a>
<img src="/assets/icons/world.png"> <img src="/assets/icons/world.png" />
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<p>Users</p> <p>Users</p>
<a> <a>
<img src="/assets/icons/users.png"> <img src="/assets/icons/users.png" />
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<p>Inventory</p> <p>Inventory</p>
<a> <a>
<img src="/assets/icons/treasure-chest.png"> <img src="/assets/icons/treasure-chest.png" />
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts"></script>
</script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/assets/scss/main'; @import '@/assets/scss/main';
.menu-wrapper { .menu-wrapper {
.menu { .menu {
list-style: none; list-style: none;
display: flex; display: flex;
gap: 1.25rem; gap: 1.25rem;
.menu-item { .menu-item {
position: relative; position: relative;
p { p {
position: absolute; position: absolute;
bottom: 3.125rem; bottom: 3.125rem;
width: 4rem; width: 4rem;
text-align: center; text-align: center;
background-color: #B1B2B5; background-color: #b1b2b5;
border: 2px solid $white; border: 2px solid $white;
border-radius: 1.5rem; border-radius: 1.5rem;
height: 1.5rem; height: 1.5rem;
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.5rem; line-height: 1.5rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
display: none; display: none;
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
top: 100%; top: 100%;
background-color: $white; background-color: $white;
height: 0.5rem; height: 0.5rem;
width: 0.875rem; width: 0.875rem;
clip-path: polygon(100% 0, 0 0, 50% 100%); clip-path: polygon(100% 0, 0 0, 50% 100%);
left: calc(50% - 0.4375rem); left: calc(50% - 0.4375rem);
} }
}
a {
padding: 0.5rem;
background-color: rgba(127, 127, 127, 0.7);
border: 2px solid $white;
border-radius: 100%;
display: block;
width: 1.875rem;
height: 1.875rem;
img {
width: inherit;
height: inherit;
}
}
&:hover {
p {
display: block;
}
a {
background-image: url('/assets/galaxy.png');
background-position: center;
background-size: cover;
img {
-webkit-filter: drop-shadow(0px 3px 6px $black);
filter: drop-shadow(0px 3px 6px $black);
}
}
}
}
} }
a {
padding: 0.5rem;
background-color: rgba(127, 127, 127, 0.7);
border: 2px solid $white;
border-radius: 100%;
display: block;
width: 1.875rem;
height: 1.875rem;
img {
width: inherit;
height: inherit;
}
}
&:hover {
p {
display: block;
}
a {
background-image: url('/assets/galaxy.png');
background-position: center;
background-size: cover;
img {
-webkit-filter: drop-shadow(0px 3px 6px $black);
filter: drop-shadow(0px 3px 6px $black);
}
}
}
}
} }
}
</style> </style>

View File

@ -1,50 +1,50 @@
<template> <template>
<div class="character-select-screen"> <div class="character-select-screen">
<div class="ui-wrapper"> <div class="ui-wrapper">
<div class="characters-wrapper"> <div class="characters-wrapper">
<div v-for="character in characters" :key="character.id" class="character" :class="{active: selected_character == character.id}"> <div v-for="character in characters" :key="character.id" class="character" :class="{ active: selected_character == character.id }">
<input type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" /> <input type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" />
<label :for="character.id">{{ character.name }}</label> <label :for="character.id">{{ character.name }}</label>
<div class="sprite-container"> <div class="sprite-container">
<img src="/assets/avatar/default/base_right_down.png" /> <img src="/assets/avatar/default/base_right_down.png" />
</div>
<span>Lvl. </span>
</div>
<div class="character new-character">
<button @click="isModalOpen = true">
<img src="/assets/icons/plus-icon.svg" />
<span>Create new</span>
</button>
</div> </div>
<span>Lvl. </span>
</div> </div>
<div class="character new-character"> <div class="buttons-wrapper">
<button @click="isModalOpen = true"> <button v-if="selected_character" @click="select_character()">Play</button>
<img src="/assets/icons/plus-icon.svg" /> <!-- @TODO : Add a confirmation dialog -->
<span>Create new</span> <button v-if="selected_character" @click="delete_character()">Delete</button>
</button>
</div> </div>
</div> </div>
<div class="buttons-wrapper">
<button v-if="selected_character" @click="select_character()">Play</button>
<!-- @TODO : Add a confirmation dialog -->
<button v-if="selected_character" @click="delete_character()">Delete</button>
</div>
</div> </div>
</div>
<Modal :isModalOpen="isModalOpen" @modal:close="isModalOpen = false"> <Modal :isModalOpen="isModalOpen" @modal:close="isModalOpen = false">
<template #modal-header> <template #modal-header>
<h2 class="modal-title">Create your character</h2> <h2 class="modal-title">Create your character</h2>
</template> </template>
<template #modal-body> <template #modal-body>
<form method="post" @submit.prevent="create" class="modal-form"> <form method="post" @submit.prevent="create" class="modal-form">
<div class="form-fields"> <div class="form-fields">
<label for="name">Name</label> <label for="name">Name</label>
<input v-model="name" name="name" id="name"> <input v-model="name" name="name" id="name" />
</div> </div>
<div class="submit"> <div class="submit">
<button @click="create">Create</button> <button @click="create">Create</button>
</div> </div>
</form> </form>
<button @click="isModalOpen = false">Cancel</button> <button @click="isModalOpen = false">Cancel</button>
</template> </template>
</Modal> </Modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -53,38 +53,38 @@ import { ref } from 'vue'
import type { Character } from '../../../env' import type { Character } from '../../../env'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
const socket = useSocketStore(); const socket = useSocketStore()
// Fetch characters // Fetch characters
socket.getConnection.emit('character:list'); socket.getConnection.emit('character:list')
socket.getConnection.on('character:list', (data: any) => { socket.getConnection.on('character:list', (data: any) => {
console.log(data); console.log(data)
characters.value = data; characters.value = data
}); })
// Select character logics // Select character logics
const characters = ref([]); const characters = ref([])
const selected_character = ref(null); const selected_character = ref(null)
function select_character() { function select_character() {
console.log(selected_character.value); console.log(selected_character.value)
if (!selected_character.value) return; if (!selected_character.value) return
socket.getConnection.emit('character:connect', {character_id: selected_character.value}); socket.getConnection.emit('character:connect', { character_id: selected_character.value })
socket.getConnection.on('character:connect', (data: Character) => socket.setCharacter(data)); socket.getConnection.on('character:connect', (data: Character) => socket.setCharacter(data))
} }
// Delete character logics // Delete character logics
function delete_character() { function delete_character() {
if (!selected_character.value) return; if (!selected_character.value) return
socket.getConnection.emit('character:delete', {character_id: selected_character.value}); socket.getConnection.emit('character:delete', { character_id: selected_character.value })
} }
// Create character logics // Create character logics
const isModalOpen = ref(false); const isModalOpen = ref(false)
let name: any = ref(''); let name: any = ref('')
function create() { function create() {
socket.getConnection.emit('character:create', { name: name.value }); socket.getConnection.emit('character:create', { name: name.value })
name.value = ''; name.value = ''
isModalOpen.value = false; isModalOpen.value = false
} }
</script> </script>
@ -92,7 +92,7 @@ function create() {
@import '@/assets/scss/main'; @import '@/assets/scss/main';
.character-select-screen { .character-select-screen {
background-image: url("/assets/bglogin.png"); background-image: url('/assets/bglogin.png');
.ui-wrapper { .ui-wrapper {
// vertical and vertical center // vertical and vertical center
height: 100vh; height: 100vh;
@ -104,7 +104,6 @@ function create() {
padding: 0 5rem; padding: 0 5rem;
&::before { &::before {
content: ''; content: '';
} }
.characters-wrapper { .characters-wrapper {
@ -139,7 +138,6 @@ function create() {
text-align: center; text-align: center;
&::before { &::before {
content: ''; content: '';
} }
img { img {
width: 100px; width: 100px;
@ -155,7 +153,8 @@ function create() {
cursor: pointer; cursor: pointer;
} }
} }
&::before, &::after { &::before,
&::after {
display: none; display: none;
} }
} }
@ -181,12 +180,11 @@ function create() {
} }
// hide the radio buttons // hide the radio buttons
input[type="radio"] { input[type='radio'] {
opacity: 0; opacity: 0;
height: 100%; height: 100%;
width: 100%; width: 100%;
position: absolute; position: absolute;
} }
label { label {
@ -251,7 +249,4 @@ function create() {
margin: 0; margin: 0;
} }
} }
</style> </style>

View File

@ -6,11 +6,11 @@
<form method="post"> <form method="post">
<div class="form-field"> <div class="form-field">
<label for="username">Username</label> <label for="username">Username</label>
<input id="username" v-model="username" type="text" name="username" required autofocus> <input id="username" v-model="username" type="text" name="username" required autofocus />
</div> </div>
<div class="form-field"> <div class="form-field">
<label for="password">Password</label> <label for="password">Password</label>
<input id="password" v-model="password" type="password" name="password" required> <input id="password" v-model="password" type="password" name="password" required />
</div> </div>
</form> </form>
</div> </div>
@ -28,10 +28,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { useSocketStore } from '@/stores/socket.ts' import { useSocketStore } from '@/stores/socket.ts'
import {login, register} from '@/services/authentication.ts' import { login, register } from '@/services/authentication.ts'
import { useNotificationStore } from '@/stores/notifications' import { useNotificationStore } from '@/stores/notifications'
const bgm = ref('bgm'); const bgm = ref('bgm')
if (bgm.value.paused) { if (bgm.value.paused) {
window.addEventListener('click', () => bgm.value.play()) window.addEventListener('click', () => bgm.value.play())
window.addEventListener('keydown', () => bgm.value.play()) window.addEventListener('keydown', () => bgm.value.play())
@ -39,22 +39,22 @@ if (bgm.value.paused) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const notifications = useNotificationStore() const notifications = useNotificationStore()
const socket = useSocketStore(); const socket = useSocketStore()
const username = ref(''); const username = ref('')
const password = ref(''); const password = ref('')
async function loginFunc() { async function loginFunc() {
// check if username and password are valid // check if username and password are valid
if (username.value === '' || password.value === '') { if (username.value === '' || password.value === '') {
notifications.addNotification({ message: 'Please enter a valid username and password' }); notifications.addNotification({ message: 'Please enter a valid username and password' })
return; return
} }
// send login event to server // send login event to server
const success = await login(username.value, password.value); const success = await login(username.value, password.value)
if (!success) { if (!success) {
notifications.addNotification({ message: 'Invalid username or password' }); notifications.addNotification({ message: 'Invalid username or password' })
} }
// if (success) {} // if (success) {}
@ -63,15 +63,15 @@ async function loginFunc() {
async function registerFunc() { async function registerFunc() {
// check if username and password are valid // check if username and password are valid
if (username.value === '' || password.value === '') { if (username.value === '' || password.value === '') {
notifications.addNotification({ message: 'Please enter a valid username and password' }); notifications.addNotification({ message: 'Please enter a valid username and password' })
return; return
} }
// send register event to server // send register event to server
const success = await register(username.value, password.value); const success = await register(username.value, password.value)
if (!success) { if (!success) {
notifications.addNotification({ message: 'Username already exists' }); notifications.addNotification({ message: 'Username already exists' })
} }
// if (success) {} // if (success) {}
@ -79,5 +79,5 @@ async function registerFunc() {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/assets/scss/login"; @import '@/assets/scss/login';
</style> </style>

View File

@ -1,3 +1 @@
<template> <template></template>
</template>

View File

@ -11,7 +11,8 @@
fontStyle: 'bold', fontStyle: 'bold',
strokeThickness: 8, strokeThickness: 8,
stroke: '#213547' stroke: '#213547'
}" /> }"
/>
<Sprite ref="sprite" texture="player" :x="position.x" :y="position.y" /> <Sprite ref="sprite" texture="player" :x="position.x" :y="position.y" />
</Container> </Container>
</template> </template>
@ -22,14 +23,14 @@ import { reactive, type Ref, ref } from 'vue'
import config from '@/config' import config from '@/config'
import { useSocketStore } from '@/stores/socket' import { useSocketStore } from '@/stores/socket'
const socket = useSocketStore(); const socket = useSocketStore()
const props = defineProps({ const props = defineProps({
layer: Phaser.Tilemaps.TilemapLayer, layer: Phaser.Tilemaps.TilemapLayer,
player: { player: {
type: Object, type: Object,
default: undefined default: undefined
}, }
}) })
const scene = useScene() const scene = useScene()
@ -38,64 +39,64 @@ const scene = useScene()
// console.log(time, delta); // console.log(time, delta);
// }) // })
const position = reactive({ x: 0, y: 0 }); const position = reactive({ x: 0, y: 0 })
if (props.player !== undefined) { if (props.player !== undefined) {
console.log('player', props.player); console.log('player', props.player)
position.x = props.player?.coords.x; position.x = props.player?.coords.x
position.y = props.player?.coords.y; position.y = props.player?.coords.y
} }
const pointer_tile = ref(undefined); const pointer_tile = ref(undefined)
function onPointerClick(pointer: Phaser.Input.Pointer) { function onPointerClick(pointer: Phaser.Input.Pointer) {
const px = scene.cameras.main.worldView.x + pointer.x; const px = scene.cameras.main.worldView.x + pointer.x
const py = scene.cameras.main.worldView.y + pointer.y; const py = scene.cameras.main.worldView.y + pointer.y
pointer_tile.value = getTile(px, py, props.layer); pointer_tile.value = getTile(px, py, props.layer)
if (pointer_tile.value) { if (pointer_tile.value) {
const worldPoint = props.layer.tileToWorldXY(pointer_tile.value.x, pointer_tile.value.y); const worldPoint = props.layer.tileToWorldXY(pointer_tile.value.x, pointer_tile.value.y)
position.x = worldPoint.x + config.tile_size.y; position.x = worldPoint.x + config.tile_size.y
position.y = worldPoint.y; position.y = worldPoint.y
socket.getConnection.emit('move', { x: position.x, y: position.y }); socket.getConnection.emit('move', { x: position.x, y: position.y })
} }
//Directions for player sprites + animations //Directions for player sprites + animations
if (px < 0 && py > 0) { if (px < 0 && py > 0) {
console.log('down left'); console.log('down left')
} else if (px < 0 && py < 0) { } else if (px < 0 && py < 0) {
console.log('top left'); console.log('top left')
} else if (px > 0 && py > 0) { } else if (px > 0 && py > 0) {
console.log('down right'); console.log('down right')
} else if (px > 0 && py < 0) { } else if (px > 0 && py < 0) {
console.log('top right'); console.log('top right')
} }
} }
if (!props.player) { if (!props.player) {
scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerClick); scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerClick)
} }
socket.getConnection.on('player_moved', (data: any) => { socket.getConnection.on('player_moved', (data: any) => {
console.log('player_moved', data); console.log('player_moved', data)
if (data.id !== props.player?.id) { if (data.id !== props.player?.id) {
console.log('not you'); console.log('not you')
return; return
} }
position.x = data.coords.x; position.x = data.coords.x
position.y = data.coords.y; position.y = data.coords.y
}) })
function getTile (x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined { function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y); const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
if (!tile) { if (!tile) {
return undefined; return undefined
} }
return tile; return tile
} }
</script> </script>

View File

@ -1,14 +1,14 @@
<template> <template>
<Teleport to="body"> <Teleport to="body">
<div class="modal-container" :style="{ top: y + 'px', left: x + 'px' }" v-if="isModalOpenRef"> <div class="modal-container" :style="{ top: y + 'px', left: x + 'px' }" v-if="isModalOpenRef">
<div class="modal-header" @mousedown="startDrag"> <div class="modal-header" @mousedown="startDrag">
<slot name="modal-header"/> <slot name="modal-header" />
<button @click="close"><img src="/assets/icons/close-button-white.svg"></button> <button @click="close"><img src="/assets/icons/close-button-white.svg" /></button>
</div>
<div class="modal-body">
<slot name="modal-body" />
</div>
</div> </div>
<div class="modal-body">
<slot name="modal-body"/>
</div>
</div>
</Teleport> </Teleport>
</template> </template>
@ -20,64 +20,67 @@ const properties = defineProps({
type: Boolean, type: Boolean,
default: false default: false
} }
}); })
watch(() => properties.isModalOpen, (value) => { watch(
isModalOpenRef.value = value; () => properties.isModalOpen,
}); (value) => {
isModalOpenRef.value = value
}
)
const isModalOpenRef = ref(properties.isModalOpen); const isModalOpenRef = ref(properties.isModalOpen)
const emit = defineEmits(["modal:close", "character:create"]); const emit = defineEmits(['modal:close', 'character:create'])
function close () { function close() {
emit('modal:close'); emit('modal:close')
} }
// make modal draggable // make modal draggable
let startX = 0; let startX = 0
let startY = 0; let startY = 0
let initialX = 0; let initialX = 0
let initialY = 0; let initialY = 0
const x = ref(0); const x = ref(0)
const y = ref(0); const y = ref(0)
const isDragging = ref(false); const isDragging = ref(false)
// set modal position center of the screen // set modal position center of the screen
onMounted(() => { onMounted(() => {
x.value = (window.innerWidth / 2) - 150; x.value = window.innerWidth / 2 - 150
y.value = (window.innerHeight / 2) - 100; y.value = window.innerHeight / 2 - 100
}); })
const startDrag = (event: MouseEvent) => { const startDrag = (event: MouseEvent) => {
isDragging.value = true; isDragging.value = true
startX = event.clientX; startX = event.clientX
startY = event.clientY; startY = event.clientY
initialX = x.value; initialX = x.value
initialY = y.value; initialY = y.value
event.preventDefault(); event.preventDefault()
}; }
const drag = (event: MouseEvent) => { const drag = (event: MouseEvent) => {
if (!isDragging.value) return; if (!isDragging.value) return
const dx = event.clientX - startX; const dx = event.clientX - startX
const dy = event.clientY - startY; const dy = event.clientY - startY
x.value = initialX + dx; x.value = initialX + dx
y.value = initialY + dy; y.value = initialY + dy
}; }
const stopDrag = () => { const stopDrag = () => {
isDragging.value = false; isDragging.value = false
}; }
onMounted(() => { onMounted(() => {
addEventListener('mousemove', drag); addEventListener('mousemove', drag)
addEventListener('mouseup', stopDrag); addEventListener('mouseup', stopDrag)
}); })
onUnmounted(() => { onUnmounted(() => {
removeEventListener('mousemove', drag); removeEventListener('mousemove', drag)
removeEventListener('mouseup', stopDrag); removeEventListener('mouseup', stopDrag)
}); })
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -1,10 +1,6 @@
<template> <template>
<div class="notifications"> <div class="notifications">
<Modal v-for="notification in notifications.getNotifications" <Modal v-for="notification in notifications.getNotifications" :key="notification.id" :isModalOpen="true" @modal:close="closeNotification(notification.id)">
:key="notification.id"
:isModalOpen="true"
@modal:close="closeNotification(notification.id)"
>
<template #modal-body> <template #modal-body>
<p>{{ notification.message }}</p> <p>{{ notification.message }}</p>
</template> </template>
@ -30,7 +26,7 @@ function setupNotificationListener(connection: any) {
notifications.addNotification({ notifications.addNotification({
id: Math.random().toString(16), id: Math.random().toString(16),
message: data.message message: data.message
}); })
}) })
} }
@ -40,7 +36,9 @@ onMounted(() => {
setupNotificationListener(connection) setupNotificationListener(connection)
} else { } else {
// Watch for changes in the socket connection // Watch for changes in the socket connection
watch(() => socket.getConnection, (newConnection) => { watch(
() => socket.getConnection,
(newConnection) => {
if (newConnection) setupNotificationListener(newConnection) if (newConnection) setupNotificationListener(newConnection)
} }
) )

View File

@ -1,10 +1,10 @@
import Player from '../Player/Player'; import Player from '../Player/Player'
export default interface IMap { export default interface IMap {
readonly id: number; readonly id: number
name: string; name: string
width: number; width: number
height: number; height: number
data: any; data: any
players: Array<Player>|[]; players: Array<Player> | []
} }

View File

@ -1,37 +1,37 @@
import type IMap from '@/engine/Map/IMap'; import type IMap from '@/engine/Map/IMap'
import Player from '@/engine/Player/Player'; import Player from '@/engine/Player/Player'
export default class Map implements IMap { export default class Map implements IMap {
id: number; id: number
name: string; name: string
width: number; width: number
height: number; height: number
data: any; data: any
players: Array<Player>|[]; players: Array<Player> | []
constructor(id: number, name: string, width: number, height: number, data: any, players: Array<Player>|[]) { constructor(id: number, name: string, width: number, height: number, data: any, players: Array<Player> | []) {
this.id = id; this.id = id
this.name = name; this.name = name
this.width = width; this.width = width
this.height = height; this.height = height
this.data = data; this.data = data
this.players = players; this.players = players
} }
public addPlayer(player: Player) { public addPlayer(player: Player) {
// @ts-ignore // @ts-ignore
this.players.push(player); this.players.push(player)
} }
public removePlayer(player: Player) { public removePlayer(player: Player) {
this.players = this.players.filter(p => p.id !== player.id); this.players = this.players.filter((p) => p.id !== player.id)
} }
public movePlayer(player: Player, x: number, y: number) { public movePlayer(player: Player, x: number, y: number) {
const playerIndex = this.players.findIndex(p => p.id === player.id); const playerIndex = this.players.findIndex((p) => p.id === player.id)
if (playerIndex !== -1) { if (playerIndex !== -1) {
this.players[playerIndex].coords.x = x; this.players[playerIndex].coords.x = x
this.players[playerIndex].coords.y = y; this.players[playerIndex].coords.y = y
}
} }
}
} }

View File

@ -1,8 +1,8 @@
export default interface IPlayer { export default interface IPlayer {
readonly id: number; readonly id: number
name: string; name: string
coords: { coords: {
x: number; x: number
y: number; y: number
}; }
} }

View File

@ -1,13 +1,13 @@
import type IPlayer from '@/engine/Player/IPlayer'; import type IPlayer from '@/engine/Player/IPlayer'
export default class Player implements IPlayer { export default class Player implements IPlayer {
id: number; id: number
name: string; name: string
coords: { x: number; y: number; }; coords: { x: number; y: number }
constructor(id: number, name: string, coords: { x: number; y: number; }) { constructor(id: number, name: string, coords: { x: number; y: number }) {
this.id = id; this.id = id
this.name = name; this.name = name
this.coords = coords; this.coords = coords
} }
} }

View File

@ -1,32 +1,32 @@
import axios from 'axios'; import axios from 'axios'
import config from '@/config'; import config from '@/config'
import { useSocketStore } from '@/stores/socket'; import { useSocketStore } from '@/stores/socket'
import { useCookies } from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
export async function register(username: string, password: string, socketStore = useSocketStore()) { export async function register(username: string, password: string, socketStore = useSocketStore()) {
try { try {
const response = await axios.post(`${config.server_endpoint}/register`, { username, password }); const response = await axios.post(`${config.server_endpoint}/register`, { username, password })
if (response.status === 200) { if (response.status === 200) {
useCookies().set('token', response.data.token as string) useCookies().set('token', response.data.token as string)
await socketStore.setupSocketConnection(); await socketStore.setupSocketConnection()
return true; return true
} }
} catch (error) { } catch (error) {
console.error('Error registering user:', error); console.error('Error registering user:', error)
return false; return false
} }
} }
export async function login(username: string, password: string, socketStore = useSocketStore()) { export async function login(username: string, password: string, socketStore = useSocketStore()) {
try { try {
const response = await axios.post(`${config.server_endpoint}/login`, { username, password }); const response = await axios.post(`${config.server_endpoint}/login`, { username, password })
if (response.status === 200) { if (response.status === 200) {
useCookies().set('token', response.data.token as string) useCookies().set('token', response.data.token as string)
await socketStore.setupSocketConnection(); await socketStore.setupSocketConnection()
return true; return true
} }
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error)
return false; return false
} }
} }

View File

@ -10,10 +10,10 @@ export const useNotificationStore: StoreDefinition = defineStore('notifications'
}, },
actions: { actions: {
addNotification(notification: Notification) { addNotification(notification: Notification) {
this.notifications.push(notification); this.notifications.push(notification)
}, },
removeNotification(id: string) { removeNotification(id: string) {
this.notifications = this.notifications.filter((notification: Notification) => notification.id !== id); this.notifications = this.notifications.filter((notification: Notification) => notification.id !== id)
} }
} }
}); })

View File

@ -1,7 +1,7 @@
import { defineStore, type StoreDefinition } from 'pinia' import { defineStore, type StoreDefinition } from 'pinia'
import { io, Socket } from 'socket.io-client'; import { io, Socket } from 'socket.io-client'
import {useCookies} from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
import config from '@/config'; import config from '@/config'
import type { Character, User } from '@/types' import type { Character, User } from '@/types'
import { useNotificationStore } from '@/stores/notifications' import { useNotificationStore } from '@/stores/notifications'
@ -9,54 +9,54 @@ export const useSocketStore: StoreDefinition = defineStore('socket', {
state: () => ({ state: () => ({
connection: null as Socket | null, connection: null as Socket | null,
user: null as User | null, user: null as User | null,
character: null as Character | null, character: null as Character | null
}), }),
getters: { getters: {
getConnection: (state: any) => state.connection as Socket, getConnection: (state: any) => state.connection as Socket,
getUser: (state: any) => state.user as User, getUser: (state: any) => state.user as User,
getCharacter: (state: any) => state.character as Character, getCharacter: (state: any) => state.character as Character
}, },
actions: { actions: {
setupSocketConnection() { setupSocketConnection() {
this.connection = io(config.server_endpoint, { this.connection = io(config.server_endpoint, {
withCredentials: true, withCredentials: true,
transports: ['websocket'], transports: ['websocket'],
reconnectionAttempts: 5, reconnectionAttempts: 5
}); })
// Let the server know the user is logged in // Let the server know the user is logged in
this.connection.emit('login'); this.connection.emit('login')
// set user // set user
this.connection.on('login', (user: User) => { this.connection.on('login', (user: User) => {
this.setUser(user); this.setUser(user)
}); })
// When we can't reconnect, disconnect // When we can't reconnect, disconnect
this.connection.on('reconnect_failed', () => { this.connection.on('reconnect_failed', () => {
console.log("Reconnect failed") console.log('Reconnect failed')
this.disconnectSocket(); this.disconnectSocket()
}) })
}, },
disconnectSocket() { disconnectSocket() {
if (!this.connection) return; if (!this.connection) return
this.connection.disconnect(); this.connection.disconnect()
this.connection = null; this.connection = null
this.user = null; this.user = null
this.character = null; this.character = null
useCookies().remove('token'); useCookies().remove('token')
}, },
setUser(user: User|null) { setUser(user: User | null) {
this.user = user; this.user = user
}, },
setCharacter(character: Character|null) { setCharacter(character: Character | null) {
this.character = character; this.character = character
} }
} }
}); })
/** /**
* Resources: * Resources:

View File

@ -1,4 +1,4 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia'
export const useZoneStore = defineStore('zone', { export const useZoneStore = defineStore('zone', {
state: () => ({ state: () => ({
@ -13,18 +13,18 @@ export const useZoneStore = defineStore('zone', {
}, },
actions: { actions: {
loadTiles(tiles: any) { loadTiles(tiles: any) {
this.tiles = tiles; this.tiles = tiles
this.loaded = true; this.loaded = true
}, },
addPlayers(player: any) { addPlayers(player: any) {
this.players = player; this.players = player
}, },
addPlayer(player: any) { addPlayer(player: any) {
this.players[player.id] = player; this.players[player.id] = player
console.log('Player added:', player); console.log('Player added:', player)
}, },
removePlayer(playerId: any) { removePlayer(playerId: any) {
delete this.players[playerId]; delete this.players[playerId]
} }
} }
}); })

View File

@ -1,49 +1,49 @@
export type Notification = { export type Notification = {
id: string; id: string
message: string; message: string
}; }
export type User = { export type User = {
id: number; id: number
username: string; username: string
password: string; password: string
characters: Character[]; characters: Character[]
}; }
export type Character = { export type Character = {
id: number; id: number
userId: number; userId: number
user: User; user: User
name: string; name: string
hitpoints: number; hitpoints: number
mana: number; mana: number
level: number; level: number
experience: number; experience: number
role: string; role: string
position_x: number; position_x: number
position_y: number; position_y: number
rotation: number; rotation: number
zoneId: number; zoneId: number
zone: Zone; zone: Zone
chats: Chat[]; chats: Chat[]
}; }
export type Zone = { export type Zone = {
id: number; id: number
name: string; name: string
width: number; width: number
height: number; height: number
tiles: Record<string, any>; tiles: Record<string, any>
characters: Character[]; characters: Character[]
chats: Chat[]; chats: Chat[]
}; }
export type Chat = { export type Chat = {
id: number; id: number
characterId: number; characterId: number
character: Character; character: Character
zoneId: number; zoneId: number
zone: Zone; zone: Zone
message: string; message: string
createdAt: Date; createdAt: Date
}; }