From 270b14d8e5e1026efbc89ba481970879835a5e01 Mon Sep 17 00:00:00 2001 From: Dennis Postma <dennis@directonline.io> Date: Sat, 29 Jun 2024 22:39:22 +0200 Subject: [PATCH] asset mngr stuff --- package-lock.json | 20 ++-- src/assets/scss/_variables.scss | 2 +- src/assets/scss/main.scss | 15 +-- src/components/World.vue | 4 +- src/components/gui/Chat.vue | 3 +- src/components/utilities/Controls.vue | 22 +++-- src/components/utilities/GmPanel.vue | 51 +++++----- src/components/utilities/Modal.vue | 1 - src/components/utilities/Notifications.vue | 8 +- .../utilities/assetManager/AssetManager.vue | 44 +++++---- .../assetManager/partials/ObjectList.vue | 80 ++++++++++++++++ .../assetManager/partials/TileDetails.vue | 31 ++++-- .../assetManager/partials/TileList.vue | 33 +++---- src/components/utilities/zoneEditor/Tiles.vue | 73 ++------------ .../utilities/zoneEditor/Toolbar.vue | 8 +- .../utilities/zoneEditor/ZoneEditor.vue | 34 ++++--- .../utilities/zoneEditor/ZoneSettings.vue | 2 +- src/screens/Game.vue | 20 ++-- src/services/zone.ts | 4 +- src/stores/assetManager.ts | 20 ++++ src/stores/zoneEditor.ts | 8 +- src/types.ts | 94 +++++++++---------- 22 files changed, 316 insertions(+), 261 deletions(-) create mode 100644 src/components/utilities/assetManager/partials/ObjectList.vue create mode 100644 src/stores/assetManager.ts diff --git a/package-lock.json b/package-lock.json index ce4587e..71cf6a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2070,9 +2070,9 @@ } }, "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.0.tgz", + "integrity": "sha512-cMojmlnwkAgIXqga+2sXshlgrrcI0QEPJ5n58pEvtuFo4PaekfomlCudArDD7hj8Hkswjl0/x4eu4q+Xa0WFgQ==", "dev": true, "license": "MIT", "engines": { @@ -3448,9 +3448,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.814", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.814.tgz", - "integrity": "sha512-GVulpHjFu1Y9ZvikvbArHmAhZXtm3wHlpjTMcXNGKl4IQ4jMQjlnz8yMQYYqdLHKi/jEL2+CBC2akWVCoIGUdw==", + "version": "1.4.815", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz", + "integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==", "dev": true, "license": "ISC" }, @@ -5648,9 +5648,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "funding": [ { "type": "opencollective", @@ -5668,7 +5668,7 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss index 1e2fe8c..ccd1ddb 100644 --- a/src/assets/scss/_variables.scss +++ b/src/assets/scss/_variables.scss @@ -19,4 +19,4 @@ $green: #09ad19; // Fonts $titles: 'Poppins', serif; -$default: 'Inter', serif; \ No newline at end of file +$default: 'Inter', serif; diff --git a/src/assets/scss/main.scss b/src/assets/scss/main.scss index 9af5a41..c5a47f6 100644 --- a/src/assets/scss/main.scss +++ b/src/assets/scss/main.scss @@ -47,13 +47,13 @@ input { border: none; background-color: transparent; - &[type="number"] { + &[type='number'] { -webkit-appearance: textfield; -moz-appearance: textfield; appearance: textfield; } - &[type=number]::-webkit-inner-spin-button, - &[type=number]::-webkit-outer-spin-button { + &[type='number']::-webkit-inner-spin-button, + &[type='number']::-webkit-outer-spin-button { -webkit-appearance: none; } } @@ -64,7 +64,8 @@ input { border: 1px solid $cyan; background-color: rgba($white, 0.8); border-radius: 5px; - &:focus, &:focus-visible { + &:focus, + &:focus-visible { outline: $cyan auto 2px; } } @@ -82,7 +83,8 @@ button { border-radius: 5px; text-shadow: 0 3px 6px rgba($black, 0.2); - &.active, &:hover { + &.active, + &:hover { background-color: $cyan; } } @@ -93,7 +95,8 @@ button { border-radius: 5px; text-shadow: 0 3px 6px rgba($black, 0.2); - &.active, &:hover { + &.active, + &:hover { background-color: $bordeaux; } } diff --git a/src/components/World.vue b/src/components/World.vue index fc4701a..40d74b1 100644 --- a/src/components/World.vue +++ b/src/components/World.vue @@ -23,7 +23,7 @@ import { generateTilemap } from '@/services/zone' let scene = useScene() let tilemapLayer = ref() -const tileMap = generateTilemap(scene, 10, 10); +const tileMap = generateTilemap(scene, 10, 10) let tileset: Tileset = tileMap.addTilesetImage('default', 'tiles') as Tileset let layer: TilemapLayer = tileMap.createBlankLayer('layer', tileset, 0, config.tile_size.y) as TilemapLayer @@ -77,7 +77,7 @@ socket.connection.on('character:moved', (data: CharacterType) => { }) onBeforeUnmount(() => { - zoneStore.reset(); + zoneStore.reset() socket.connection.emit('character:zone:leave') socket.connection.off('character:zone:load') socket.connection.off('zone:character:join') diff --git a/src/components/gui/Chat.vue b/src/components/gui/Chat.vue index 8391ed6..b3d97ee 100644 --- a/src/components/gui/Chat.vue +++ b/src/components/gui/Chat.vue @@ -26,7 +26,7 @@ background-color: rgba($white, 0.85); border: 2px solid $white; color: $gray-2; - background-image: url("/assets/icons/submit-icon.svg"); + background-image: url('/assets/icons/submit-icon.svg'); background-repeat: no-repeat; background-size: 30px; background-position: calc(100% - 25px) center; @@ -37,7 +37,6 @@ left: 500px; width: 30px; height: 30px; - } } } diff --git a/src/components/utilities/Controls.vue b/src/components/utilities/Controls.vue index 4ac6571..449fb3f 100644 --- a/src/components/utilities/Controls.vue +++ b/src/components/utilities/Controls.vue @@ -10,8 +10,8 @@ import { getTile, tileToWorldXY } from '@/services/zone' import { useZoneStore } from '@/stores/zone' import { useZoneEditorStore } from '@/stores/zoneEditor' -const zoneStore = useZoneStore(); -const zoneEditorStore = useZoneEditorStore(); +const zoneStore = useZoneStore() +const zoneEditorStore = useZoneEditorStore() const scene = useScene() const props = defineProps({ layer: Phaser.Tilemaps.TilemapLayer @@ -43,7 +43,7 @@ function onPointerMove(pointer: Phaser.Input.Pointer) { scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove) // Zone camera system -function dragZone (pointer: Phaser.Input.Pointer) { +function dragZone(pointer: Phaser.Input.Pointer) { if (!pointer.isDown) return cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom @@ -52,14 +52,16 @@ let cam = scene.cameras.main scene.input.on(Phaser.Input.Events.POINTER_MOVE, dragZone) watch( - () => zoneEditorStore.tool, () => { + () => zoneEditorStore.tool, + () => { // @TODO : change to zone for when loading other maps - if (zoneEditorStore.tool === 'move') { - scene.input.on(Phaser.Input.Events.POINTER_MOVE, dragZone) - } else { - scene.input.off(Phaser.Input.Events.POINTER_MOVE, dragZone) - } - }, { deep: true } + if (zoneEditorStore.tool === 'move') { + scene.input.on(Phaser.Input.Events.POINTER_MOVE, dragZone) + } else { + scene.input.off(Phaser.Input.Events.POINTER_MOVE, dragZone) + } + }, + { deep: true } ) // Unload funcs diff --git a/src/components/utilities/GmPanel.vue b/src/components/utilities/GmPanel.vue index fc09c53..d299322 100644 --- a/src/components/utilities/GmPanel.vue +++ b/src/components/utilities/GmPanel.vue @@ -1,22 +1,20 @@ <template> - <Modal :isModalOpen="true" :modal-width="1000" :modal-height="650"> - <template #modalHeader> - <h3 class="modal-title">GM Panel</h3> - <div class="gm-selector"> - <button class="btn-cyan">General</button> - <button class="btn-cyan">Users</button> - <button class="btn-cyan">Items</button> - <button class="btn-cyan">NPCs</button> - <button class="btn-cyan">Chats</button> - <button class="btn-cyan active">AM</button> - </div> - </template> - <template #modalBody> - <div class="container gm-panel"> - <AssetManager v-if="toggle == 'asset-manager'" /> - </div> - </template> - </Modal> + <Modal :isModalOpen="true" :modal-width="1000" :modal-height="650"> + <template #modalHeader> + <h3 class="modal-title">GM Panel</h3> + <div class="gm-selector"> + <button class="btn-cyan">General</button> + <button class="btn-cyan">Users</button> + <button class="btn-cyan">Chats</button> + <button class="btn-cyan active">Asset manager</button> + </div> + </template> + <template #modalBody> + <div class="container gm-panel"> + <AssetManager v-if="toggle == 'asset-manager'" /> + </div> + </template> + </Modal> </template> <script setup lang="ts"> @@ -31,14 +29,13 @@ let toggle = ref('asset-manager') @import '@/assets/scss/main'; .gm-selector { - display: flex; - gap: 5px; - flex-wrap: wrap; + display: flex; + gap: 5px; + flex-wrap: wrap; - button { - padding: 6px 15px; - min-width: 100px; - } + button { + padding: 6px 15px; + min-width: 100px; + } } - -</style> \ No newline at end of file +</style> diff --git a/src/components/utilities/Modal.vue b/src/components/utilities/Modal.vue index 8f0bead..e4098cd 100644 --- a/src/components/utilities/Modal.vue +++ b/src/components/utilities/Modal.vue @@ -304,7 +304,6 @@ function handleResize() { } } } - } } } diff --git a/src/components/utilities/Notifications.vue b/src/components/utilities/Notifications.vue index 60f6661..1ca6bd9 100644 --- a/src/components/utilities/Notifications.vue +++ b/src/components/utilities/Notifications.vue @@ -56,7 +56,7 @@ onBeforeUnmount(() => { </script> <style lang="scss"> - .modal-notif { - margin: 15px; - } -</style> \ No newline at end of file +.modal-notif { + margin: 15px; +} +</style> diff --git a/src/components/utilities/assetManager/AssetManager.vue b/src/components/utilities/assetManager/AssetManager.vue index 5440e42..13cbc1b 100644 --- a/src/components/utilities/assetManager/AssetManager.vue +++ b/src/components/utilities/assetManager/AssetManager.vue @@ -1,16 +1,15 @@ <template> <div class="assets-container"> <div class="asset-categories"> - <!-- Asset Categories --> - <a class="category" :class="{ selected: selectedCategory === 'tiles' }" @click="() => selectedCategory = 'tiles'"> + <a class="category" :class="{ selected: selectedCategory === 'tiles' }" @click="() => (selectedCategory = 'tiles')"> <span class="category-name">Tiles</span> </a> - <a class="category" :class="{ selected: selectedCategory === 'objects' }" @click="() => selectedCategory = 'objects'"> + <a class="category" :class="{ selected: selectedCategory === 'objects' }" @click="() => (selectedCategory = 'objects')"> <span class="category-name">Objects</span> </a> <a class="category"> - <span class="category-name">Weapons</span> + <span class="category-name">Loot</span> </a> <a class="category"> <span class="category-name">NPC's</span> @@ -20,7 +19,8 @@ <!-- Assets list --> <div class="assets" ref="elementToScroll" @scroll="onScroll"> - <TileList :name="selectedCategory" /> + <TileList v-if="selectedCategory === 'tiles'" /> + <ObjectList :name="selectedCategory" v-if="selectedCategory === 'objects'" /> </div> <button class="back-to-top" v-show="hasScrolled" @click="toTop"> @@ -30,22 +30,25 @@ <!-- Asset details --> <div class="asset-info"> - <TileDetails :tile="selectedTile" v-if="selectedCategory === 'tiles'" /> + <TileDetails :tile="selectedTile" v-if="selectedCategory === 'tiles' && assetManagerStore.selectedTile" /> </div> </div> </template> <script setup lang="ts"> import { onMounted, ref } from 'vue' -import {useSocketStore} from '@/stores/socket' +import { useSocketStore } from '@/stores/socket' import TileList from '@/components/utilities/assetManager/partials/TileList.vue' import TileDetails from '@/components/utilities/assetManager/partials/TileDetails.vue' +import ObjectList from '@/components/utilities/assetManager/partials/ObjectList.vue' +import { useAssetManagerStore } from '@/stores/assetManager' const socket = useSocketStore() +const assetManagerStore = useAssetManagerStore() const selectedCategory = ref('tiles') const selectedTile = ref('') const hasScrolled = ref(false) -const elementToScroll = ref() +const elementToScroll = ref() const onScroll = (e: Event) => { let scrollTop = (e.target as HTMLBodyElement).scrollTop @@ -61,8 +64,8 @@ function toTop() { elementToScroll.value.scrollTo({ top: 0, left: 0, - behavior: "smooth", - }); + behavior: 'smooth' + }) } </script> @@ -72,7 +75,7 @@ function toTop() { .container { &.gm-panel { height: 100%; - margin: 0!important; + margin: 0 !important; } } .assets-container { @@ -103,7 +106,7 @@ function toTop() { width: 50px; height: 50px; border-radius: 8px; - background-color: rgba($cyan, 0.5); + background-color: rgba($cyan, 0.5); padding: 0; img { position: absolute; @@ -119,14 +122,14 @@ function toTop() { background-color: rgba($cyan, 0.8); } } - + .asset-categories { width: 15%; display: flex; } .assets { - overflow:auto; + overflow: auto; height: 100%; width: 35%; display: flex; @@ -171,7 +174,6 @@ function toTop() { .asset-details { display: flex; - justify-content: center; align-items: center; gap: 10px; img { @@ -185,11 +187,15 @@ function toTop() { } } - .asset-categories, .assets, .asset-info { + .asset-categories, + .assets, + .asset-info { flex-direction: column; position: relative; - .category, .asset, .image-container { + .category, + .asset, + .image-container { position: relative; padding: 10px; &::after { @@ -204,9 +210,7 @@ function toTop() { &.selected { background-color: rgba($cyan, 0.8); } - } } - } -</style> \ No newline at end of file +</style> diff --git a/src/components/utilities/assetManager/partials/ObjectList.vue b/src/components/utilities/assetManager/partials/ObjectList.vue new file mode 100644 index 0000000..ae0d37d --- /dev/null +++ b/src/components/utilities/assetManager/partials/ObjectList.vue @@ -0,0 +1,80 @@ +<template> + <div class="asset"> + <input class="input-cyan search-field" placeholder="Search..." /> + </div> + + <!-- TODO: use the passed :name in props to switch out assets--> + <a class="asset" :class="{ active: name === 'tiles' }" v-for="(tile, index) in tiles" :key="index"> + <div class="asset-details"> + <img :src="`${config.server_endpoint}/assets/tiles/${tile}`" /> + <span class="asset-name">{{ tile }}</span> + </div> + </a> +</template> + +<script setup lang="ts"> +import { useSocketStore } from '@/stores/socket' +import config from '@/config' +import { onMounted, ref, defineProps } from 'vue' + +const props = defineProps<{ name: string }>() + +const socket = useSocketStore() +const tileUploadField = ref(null) +const tiles = ref() + +const handleFileUpload = (e: Event) => { + const files = (e.target as HTMLInputElement).files + if (!files) return + socket.connection.emit('gm:tile:upload', files, (response: boolean) => { + if (!response) { + console.error('Failed to upload tile') + return + } + socket.connection.emit('gm:tile:list', {}, (response: string[]) => { + tiles.value = response + }) + }) +} + +onMounted(() => { + socket.connection.emit('gm:tile:list', {}, (response: string[]) => { + tiles.value = response + }) +}) +</script> + +<style lang="scss" scoped> +@import '@/assets/scss/main'; + +.asset { + &.add-new { + display: flex; + align-items: center; + gap: 10px 20px; + flex-wrap: wrap; + .asset-name { + flex-shrink: 0; + } + } + .search-field { + width: calc(100% - 20px); + } + .file-upload { + background-color: rgba($cyan, 0.5); + border: 1px solid $white; + border-radius: 5px; + text-shadow: 0 3px 6px rgba($black, 0.2); + padding: 6px 15px; + display: inline-flex; + + &:hover { + background-color: $cyan; + cursor: pointer; + } + input[type='file'] { + display: none; + } + } +} +</style> diff --git a/src/components/utilities/assetManager/partials/TileDetails.vue b/src/components/utilities/assetManager/partials/TileDetails.vue index 9fb497c..7f5b2cc 100644 --- a/src/components/utilities/assetManager/partials/TileDetails.vue +++ b/src/components/utilities/assetManager/partials/TileDetails.vue @@ -1,28 +1,41 @@ <template> <div class="image-container"> - <img src="/assets/placeholders/wall-1.png" /> + <img :src="`${config.server_endpoint}/assets/tiles/${assetManagerStore.selectedTile}`" alt="Tile" /> </div> <div class="modal-form asset-manager"> <form class="form-fields"> <div class="form-field name"> <label for="name">Name</label> - <input class="input-cyan" type="text" name="name" placeholder="Wall #1" /> + <input class="input-cyan" type="text" name="name" placeholder="E.g. grass" /> </div> <div class="submit"> <button class="btn-cyan" type="submit">Save</button> - <button class="btn-bordeaux" type="submit">Remove</button> + <button class="btn-bordeaux" type="button" @click="removeTile">Remove</button> </div> </form> </div> </template> <script setup lang="ts"> -const properties = defineProps({ - tile: String -}); +import config from '@/config' +import { useAssetManagerStore } from '@/stores/assetManager' +import { useSocketStore } from '@/stores/socket' + +const socket = useSocketStore() +const assetManagerStore = useAssetManagerStore() + +function removeTile() { + socket.connection.emit('gm:tile:remove', { tile: assetManagerStore.selectedTile }, (response: boolean) => { + if (!response) { + return + } + socket.connection.emit('gm:tile:list', {}, (response: string[]) => { + assetManagerStore.setTileList(response) + assetManagerStore.setSelectedTile('') + }) + }) +} </script> -<style lang="scss"> - -</style> \ No newline at end of file +<style lang="scss"></style> diff --git a/src/components/utilities/assetManager/partials/TileList.vue b/src/components/utilities/assetManager/partials/TileList.vue index 73ebda2..8d0c325 100644 --- a/src/components/utilities/assetManager/partials/TileList.vue +++ b/src/components/utilities/assetManager/partials/TileList.vue @@ -1,34 +1,28 @@ <template> - <div class="asset"> - <input class="input-cyan search-field" placeholder="Search..." /> - </div> <div class="asset add-new"> - <span class="asset-name">Upload new</span> <label for="upload-asset" class="file-upload"> <input id="upload-asset" ref="tileUploadField" type="file" accept="image/png" multiple @change="handleFileUpload" /> - Choose file + Upload tile(s) </label> + <input class="input-cyan search-field" placeholder="Search..." /> </div> - - <!-- TODO: use the passed :name in props to switch out assets--> - <a class="asset" :class="{ active: name === 'tiles' }" v-for="(tile, index) in tiles" :key="index"> + <a class="asset" :class="{ active: assetManagerStore.selectedTile === tile }" v-for="(tile, index) in assetManagerStore.tileList" :key="index" @click="assetManagerStore.setSelectedTile(tile)"> <div class="asset-details"> - <img :src="`${config.server_endpoint}/assets/tiles/${tile}`" /> + <img :src="`${config.server_endpoint}/assets/tiles/${tile}`" alt="Tile" /> <span class="asset-name">{{ tile }}</span> </div> </a> </template> <script setup lang="ts"> -import {useSocketStore} from '@/stores/socket' import config from '@/config' -import { onMounted, ref, defineProps } from 'vue' - -const props = defineProps<{ name: string }>() +import { useSocketStore } from '@/stores/socket' +import { onMounted, ref } from 'vue' +import { useAssetManagerStore } from '@/stores/assetManager' const socket = useSocketStore() const tileUploadField = ref(null) -const tiles = ref() +const assetManagerStore = useAssetManagerStore() const handleFileUpload = (e: Event) => { const files = (e.target as HTMLInputElement).files @@ -39,14 +33,14 @@ const handleFileUpload = (e: Event) => { return } socket.connection.emit('gm:tile:list', {}, (response: string[]) => { - tiles.value = response + assetManagerStore.setTileList(response) }) }) } onMounted(() => { socket.connection.emit('gm:tile:list', {}, (response: string[]) => { - tiles.value = response + assetManagerStore.setTileList(response) }) }) </script> @@ -55,9 +49,10 @@ onMounted(() => { @import '@/assets/scss/main'; .asset { + cursor: pointer; + &.add-new { display: flex; - align-items: center; gap: 10px 20px; flex-wrap: wrap; .asset-name { @@ -79,9 +74,9 @@ onMounted(() => { background-color: $cyan; cursor: pointer; } - input[type="file"] { + input[type='file'] { display: none; } } } -</style> \ No newline at end of file +</style> diff --git a/src/components/utilities/zoneEditor/Tiles.vue b/src/components/utilities/zoneEditor/Tiles.vue index d865b1c..7981312 100644 --- a/src/components/utilities/zoneEditor/Tiles.vue +++ b/src/components/utilities/zoneEditor/Tiles.vue @@ -6,9 +6,8 @@ </template> <template #modalBody> <div class="container tiles"> - <canvas ref="canvas" :width="tileWidth" :height="tileHeight" style="display: none"></canvas> <div class="tiles"> - <img v-for="(tile, index) in tiles" :key="index" :src="tile" alt="Tile" @click="zoneEditorStore.setSelectedTile(index)" :class="{ selected: zoneEditorStore.selectedTile && zoneEditorStore.selectedTile === index }" /> + <img v-for="(tile, index) in tiles" :key="index" :src="tile" alt="Tile" @click="zoneEditorStore.setSelectedTile(tile)" :class="{ selected: zoneEditorStore.selectedTile && zoneEditorStore.selectedTile === tile }" /> </div> </div> </template> @@ -17,73 +16,22 @@ </template> <script setup lang="ts"> -import { ref, onMounted, nextTick } from 'vue' import config from '@/config' -import Modal from '@/components/utilities/Modal.vue' +import { ref, onMounted } from 'vue' import { useZoneEditorStore } from '@/stores/zoneEditor' -import JSZip from 'jszip' +import { useSocketStore } from '@/stores/socket' +import Modal from '@/components/utilities/Modal.vue' -const tileWidth = config.tile_size.x -const tileHeight = config.tile_size.y -const tiles = ref<number[][]>([]) -const selectedTile = ref<number | null>(null) -const canvas = ref<HTMLCanvasElement | null>(null) +const socket = useSocketStore() +const tiles = ref<string[]>([]) const isModalOpen = ref(false) const zoneEditorStore = useZoneEditorStore() -// Hardcoded image path -const imagePath = '/assets/zone/tiles.png' - -const loadImage = (src: string): Promise<HTMLImageElement> => { - return new Promise((resolve) => { - const img = new Image() - img.onload = () => resolve(img) - img.src = src - }) -} - -const splitTiles = (img: HTMLImageElement) => { - if (!canvas.value) { - console.error('Canvas not found') - return - } - const ctx = canvas.value.getContext('2d') - if (!ctx) { - console.error('Failed to get canvas context') - return - } - - const tilesetWidth = img.width - const tilesetHeight = img.height - const columns = Math.floor(tilesetWidth / tileWidth) - const rows = Math.floor(tilesetHeight / tileHeight) - - tiles.value = [] - selectedTile.value = null - - for (let row = 0; row < rows; row++) { - for (let col = 0; col < columns; col++) { - const x = col * tileWidth - const y = row * tileHeight - - ctx.clearRect(0, 0, tileWidth, tileHeight) - ctx.drawImage(img, x, y, tileWidth, tileHeight, 0, 0, tileWidth, tileHeight) - - const tileDataURL = canvas.value.toDataURL() - tiles.value.push(tileDataURL) - } - } -} - -const selectTile = (index: number) => { - selectedTile.value = index -} - onMounted(async () => { isModalOpen.value = true - const img = await loadImage(imagePath) - await nextTick() - splitTiles(img) + socket.connection.emit('gm:tile:list', {}, (response: string[]) => { + tiles.value = response.map((tile) => `${config.server_endpoint}/assets/tiles/${tile}`) + }) }) </script> @@ -107,5 +55,4 @@ onMounted(async () => { .tiles img.selected { border: 2px solid $red; } - -</style> \ No newline at end of file +</style> diff --git a/src/components/utilities/zoneEditor/Toolbar.vue b/src/components/utilities/zoneEditor/Toolbar.vue index ee24ab6..115e6e6 100644 --- a/src/components/utilities/zoneEditor/Toolbar.vue +++ b/src/components/utilities/zoneEditor/Toolbar.vue @@ -13,7 +13,7 @@ <div class="select" v-if="zoneEditorStore.tool === 'pencil'"> <div class="select-trigger" :class="{ open: selectPencilOpen }" @click="selectPencilOpen = !selectPencilOpen"> {{ zoneEditorStore.drawMode }} - <img src="/assets/icons/zoneEditor/chevron.svg"/> + <img src="/assets/icons/zoneEditor/chevron.svg" /> </div> <div class="options" v-show="selectPencilOpen && zoneEditorStore.tool === 'pencil'"> <span class="option" @click="setDrawMode('tile')">Tile</span> @@ -31,7 +31,7 @@ <div class="select" v-if="zoneEditorStore.tool === 'eraser'"> <div class="select-trigger" :class="{ open: selectEraserOpen }" @click="selectEraserOpen = !selectEraserOpen"> {{ zoneEditorStore.drawMode }} - <img src="/assets/icons/zoneEditor/chevron.svg"/> + <img src="/assets/icons/zoneEditor/chevron.svg" /> </div> <div class="options" v-show="selectEraserOpen"> <span class="option" @click="setDrawMode('tile')">Tile</span> @@ -75,8 +75,8 @@ const scene = useScene() const emit = defineEmits(['move', 'eraser', 'pencil', 'save']) // track select state -let selectPencilOpen = ref(false); -let selectEraserOpen = ref(false); +let selectPencilOpen = ref(false) +let selectEraserOpen = ref(false) // drawMode function setDrawMode(value: string) { diff --git a/src/components/utilities/zoneEditor/ZoneEditor.vue b/src/components/utilities/zoneEditor/ZoneEditor.vue index 18e7e11..fcd532f 100644 --- a/src/components/utilities/zoneEditor/ZoneEditor.vue +++ b/src/components/utilities/zoneEditor/ZoneEditor.vue @@ -2,11 +2,11 @@ <TilemapLayerC :tilemap="tileTilemap" :tileset="exampleTilesArray" :layerIndex="0" :cull-padding-x="10" :cull-padding-y="10" /> <Controls :layer="exampleTiles" /> -<!-- @TODO: inside asset manager we need to be able to set the originX and originY per individial asset --> + <!-- @TODO: inside asset manager we need to be able to set the originX and originY per individial asset --> <Container> - <Image :texture="'wall1'" :x="pos.position_x" :y="pos.position_y" :originY="1.13" :originX="1" /> - <Image :texture="'wall1'" :x="pos2.position_x" :y="pos2.position_y" :originY="1.13" :originX="1" /> - <Image :texture="'wall2'" :x="pos3.position_x" :y="pos3.position_y" :originY="1.255" :originX="1" /> + <Image :texture="'wall1'" :x="pos.position_x" :y="pos.position_y" :originY="1.13" :originX="1" /> + <Image :texture="'wall1'" :x="pos2.position_x" :y="pos2.position_y" :originY="1.13" :originX="1" /> + <Image :texture="'wall2'" :x="pos3.position_x" :y="pos3.position_y" :originY="1.255" :originX="1" /> </Container> <Toolbar :layer="exampleTiles" @eraser="eraser" @pencil="pencil" @save="save" /> @@ -38,7 +38,6 @@ let scene = useScene() const socket = useSocketStore() const zoneEditorStore = useZoneEditorStore() - // Tile tilemap const tileMapData = new Phaser.Tilemaps.MapData({ width: zoneEditorStore.width, @@ -48,18 +47,17 @@ const tileMapData = new Phaser.Tilemaps.MapData({ orientation: Phaser.Tilemaps.Orientation.ISOMETRIC, format: Phaser.Tilemaps.Formats.ARRAY_2D }) -let tileTilemap = new Phaser.Tilemaps.Tilemap(scene, tileMapData); +let tileTilemap = new Phaser.Tilemaps.Tilemap(scene, tileMapData) let tilesImg = tileTilemap.addTilesetImage('default', 'tiles') let exampleTiles = tileTilemap.createBlankLayer('exampleTiles', tilesImg as Tileset, 0, config.tile_size.y) as TilemapLayer const exampleTilesArray = Array.from({ length: zoneEditorStore.width }, () => Array.from({ length: zoneEditorStore.height }, () => 1)) -onMounted(() => { -}) - const pos = tileToWorldXY(exampleTiles, 1, 1); - const pos2 = tileToWorldXY(exampleTiles, 1, 2); - const pos3 = tileToWorldXY(exampleTiles, 2, 1); -console.log(pos); +onMounted(() => {}) +const pos = tileToWorldXY(exampleTiles, 1, 1) +const pos2 = tileToWorldXY(exampleTiles, 1, 2) +const pos3 = tileToWorldXY(exampleTiles, 2, 1) +console.log(pos) // center camera const centerY = (tileTilemap.height * tileTilemap.tileHeight) / 2 const centerX = (tileTilemap.width * tileTilemap.tileWidth) / 2 @@ -91,12 +89,12 @@ onBeforeMount(() => { function eraser(tile: Phaser.Tilemaps.Tile) { if (zoneEditorStore.drawMode === 'tile') { tiles.putTileAt(0, tile.x, tile.y) - zoneEditorStore.updateTile(tile.x, tile.y, 0); + zoneEditorStore.updateTile(tile.x, tile.y, 0) } if (zoneEditorStore.drawMode === 'wall') { - walls.putTileAt(0, tile.x, tile.y ) - zoneEditorStore.updateWall(tile.x, tile.y, 0); + walls.putTileAt(0, tile.x, tile.y) + zoneEditorStore.updateWall(tile.x, tile.y, 0) } } @@ -104,14 +102,14 @@ function pencil(tile: Phaser.Tilemaps.Tile) { if (zoneEditorStore.drawMode === 'tile') { if (zoneEditorStore.selectedTile === null) return tiles.putTileAt(zoneEditorStore.selectedTile, tile.x, tile.y) - zoneEditorStore.setTiles(tile.x, tile.y, zoneEditorStore.selectedTile); + zoneEditorStore.setTiles(tile.x, tile.y, zoneEditorStore.selectedTile) } if (zoneEditorStore.drawMode === 'wall') { // @TODO fix position if (zoneEditorStore.selectedWall === null) return walls.putTileAt(zoneEditorStore.selectedWall, tile.x, tile.y) - zoneEditorStore.updateWall(tile.x, tile.y, zoneEditorStore.selectedWall); + zoneEditorStore.updateWall(tile.x, tile.y, zoneEditorStore.selectedWall) } } @@ -127,7 +125,7 @@ function save() { } onBeforeUnmount(() => { - zoneEditorStore.reset(); + zoneEditorStore.reset() }) /** diff --git a/src/components/utilities/zoneEditor/ZoneSettings.vue b/src/components/utilities/zoneEditor/ZoneSettings.vue index 36e75a6..61441c0 100644 --- a/src/components/utilities/zoneEditor/ZoneSettings.vue +++ b/src/components/utilities/zoneEditor/ZoneSettings.vue @@ -55,4 +55,4 @@ watch(width, (value) => { watch(height, (value) => { zoneEditorStore.setHeight(parseInt(value)) }) -</script> \ No newline at end of file +</script> diff --git a/src/screens/Game.vue b/src/screens/Game.vue index 2b03ee4..af0123a 100644 --- a/src/screens/Game.vue +++ b/src/screens/Game.vue @@ -4,16 +4,16 @@ <Game class="game" :config="gameConfig" @create="createGame" v-if="!zoneEditorStore.active"> <Scene name="main" @preload="preloadScene" @create="createScene"> - <div class="top-ui"> - <Hud /> - </div> - <div class="center-ui"> - <World /> - </div> - <div class="bottom-ui"> - <Chat /> - <Menubar /> - </div> + <div class="top-ui"> + <Hud /> + </div> + <div class="center-ui"> + <World /> + </div> + <div class="bottom-ui"> + <Chat /> + <Menubar /> + </div> </Scene> </Game> <Game class="game" :config="gameConfig" @create="createGame" v-else> diff --git a/src/services/zone.ts b/src/services/zone.ts index b62794f..6d015d0 100644 --- a/src/services/zone.ts +++ b/src/services/zone.ts @@ -23,6 +23,4 @@ export function tileToWorldXY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number return { position_x, position_y } } -export function generateTilemap(scene: Phaser.Scene, width: number, height: number) { - -} \ No newline at end of file +export function generateTilemap(scene: Phaser.Scene, width: number, height: number) {} diff --git a/src/stores/assetManager.ts b/src/stores/assetManager.ts new file mode 100644 index 0000000..691ff2d --- /dev/null +++ b/src/stores/assetManager.ts @@ -0,0 +1,20 @@ +import { defineStore } from 'pinia' + +export const useAssetManagerStore = defineStore('assetManager', { + state: () => ({ + tileList: [] as string[], + selectedTile: '' + }), + actions: { + setTileList(tiles: string[]) { + this.tileList = tiles + }, + setSelectedTile(tile: string) { + this.selectedTile = tile + }, + reset() { + this.tileList = [] + this.selectedTile = '' + } + } +}) diff --git a/src/stores/zoneEditor.ts b/src/stores/zoneEditor.ts index 8d2ade2..39fe5f7 100644 --- a/src/stores/zoneEditor.ts +++ b/src/stores/zoneEditor.ts @@ -10,7 +10,7 @@ export const useZoneEditorStore = defineStore('zoneEditor', { decorations: [] as number[][], tool: 'move', drawMode: 'tile', - selectedTile: null, + selectedTile: '', selectedWall: null, selectedDecoration: null, isSettingsModalShown: false @@ -46,7 +46,7 @@ export const useZoneEditorStore = defineStore('zoneEditor', { setDrawMode(mode: string) { this.drawMode = mode }, - setSelectedTile(tile: any) { + setSelectedTile(tile: string) { this.selectedTile = tile }, setSelectedWall(wall: any) { @@ -65,7 +65,7 @@ export const useZoneEditorStore = defineStore('zoneEditor', { this.tiles = [] this.tool = 'move' this.drawMode = 'tile' - this.selectedTile = null + this.selectedTile = '' this.selectedWall = null this.selectedDecoration = null this.isSettingsModalShown = false @@ -77,4 +77,4 @@ export const useZoneEditorStore = defineStore('zoneEditor', { * Resources: * https://www.html5gamedevs.com/topic/21908-phaser-is-there-any-tutorial-on-how-to-do-an-isometric-game/ * http://murdochcarpenter.com/isometric-starling-part-i/ - */ \ No newline at end of file + */ diff --git a/src/types.ts b/src/types.ts index 8748651..93e8fef 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,62 +5,62 @@ export type Notification = { // User model export type User = { - id: number; - username: string; - password: string; - characters: Character[]; -}; + id: number + username: string + password: string + characters: Character[] +} // Character model export type Character = { - id: number; - userId: number; - user: User; - name: string; - hitpoints: number; - mana: number; - level: number; - experience: number; - role: string; - position_x: number; - position_y: number; - rotation: number; - zoneId: number; - zone: Zone; - chats: Chat[]; -}; + id: number + userId: number + user: User + name: string + hitpoints: number + mana: number + level: number + experience: number + role: string + position_x: number + position_y: number + rotation: number + zoneId: number + zone: Zone + chats: Chat[] +} // Zone model export type Zone = { - id: number; - name: string; - width: number; - height: number; - tiles: number[][]; - walls: number[][]; - decorations: ZoneDecoration[]; - characters: Character[]; - chats: Chat[]; - createdAt: Date; - updatedAt: Date; -}; + id: number + name: string + width: number + height: number + tiles: number[][] + walls: number[][] + decorations: ZoneDecoration[] + characters: Character[] + chats: Chat[] + createdAt: Date + updatedAt: Date +} export type ZoneDecoration = { - id: number; - zoneId: number; - zone: Zone; - type: number; - position_x: number; - position_y: number; + id: number + zoneId: number + zone: Zone + type: number + position_x: number + position_y: number } // Chat model export type Chat = { - id: number; - characterId: number; - character: Character; - zoneId: number; - zone: Zone; - message: string; - createdAt: Date; -}; + id: number + characterId: number + character: Character + zoneId: number + zone: Zone + message: string + createdAt: Date +}