1
0
forked from noxious/client

GM panel UI improvements, added accordion component, worked on sprite logics, updated types, npm update

This commit is contained in:
Dennis Postma 2024-07-24 03:26:08 +02:00
parent a788cdef99
commit 026165cff3
8 changed files with 280 additions and 203 deletions

92
package-lock.json generated
View File

@ -1763,9 +1763,9 @@
}
},
"node_modules/@types/node": {
"version": "20.14.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
"integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
"version": "20.14.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz",
"integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1986,9 +1986,9 @@
"license": "ISC"
},
"node_modules/@vitejs/plugin-vue": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz",
"integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.0.tgz",
"integrity": "sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==",
"dev": true,
"license": "MIT",
"engines": {
@ -2260,14 +2260,14 @@
"license": "MIT"
},
"node_modules/@vue/devtools-core": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.3.6.tgz",
"integrity": "sha512-XqFYVkyS3eySHF4bgLt+KF6yL6nYzVY/JTJHnK6KIJXIE4GIAxmn5Gxfsb4cUG9sl0FGiMqRCnM37Q+P08wr8A==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.3.7.tgz",
"integrity": "sha512-IapWbHUqvO6n+p5JFTCE5JyNjpsZ5IS1GYIRX0P7/SqYPgFCOdH0dG+u8PbBHYdnp+VPxHLO+GGZ/WBZFCZnsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.3.6",
"@vue/devtools-shared": "^7.3.6",
"@vue/devtools-kit": "^7.3.7",
"@vue/devtools-shared": "^7.3.7",
"mitt": "^3.0.1",
"nanoid": "^3.3.4",
"pathe": "^1.1.2",
@ -2278,13 +2278,13 @@
}
},
"node_modules/@vue/devtools-kit": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.6.tgz",
"integrity": "sha512-5Ym9V3fkJenEoptqKoo+cgY5RTVwrSssFdzRsuyIgaeiskCT+rRJeQdwoo81tyrQ1mfS7Er1rYZlSzr3Y3L/ew==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.7.tgz",
"integrity": "sha512-ktHhhjI4CoUrwdSUF5b/MFfjrtAtK8r4vhOkFyRN5Yp9kdXTwsRBYcwarHuP+wFPKf4/KM7DVBj2ELO8SBwdsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.3.6",
"@vue/devtools-shared": "^7.3.7",
"birpc": "^0.2.17",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
@ -2294,9 +2294,9 @@
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.6.tgz",
"integrity": "sha512-R/FOmdJV+hhuwcNoxp6e87RRkEeDMVhWH+nOsnHUrwjjsyeXJ2W1475Ozmw+cbZhejWQzftkHVKO28Fuo1yqCw==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.7.tgz",
"integrity": "sha512-M9EU1/bWi5GNS/+IZrAhwGOVZmUTN4MH22Hvh35nUZZg9AZP2R2OhfCb+MG4EtAsrUEYlu3R43/SIj3G7EZYtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2452,9 +2452,9 @@
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"version": "0.14.9",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.9.tgz",
"integrity": "sha512-dC1TJMODGM8lxhP6wLToncaDPPNB3biVxxRDuNCYpuXwi70ou7NsGd97KVTJ2omepGId429JZt8oaZKeXbqxwg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@ -2544,9 +2544,9 @@
}
},
"node_modules/@vueuse/integrations/node_modules/vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"version": "0.14.9",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.9.tgz",
"integrity": "sha512-dC1TJMODGM8lxhP6wLToncaDPPNB3biVxxRDuNCYpuXwi70ou7NsGd97KVTJ2omepGId429JZt8oaZKeXbqxwg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@ -2591,9 +2591,9 @@
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"version": "0.14.9",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.9.tgz",
"integrity": "sha512-dC1TJMODGM8lxhP6wLToncaDPPNB3biVxxRDuNCYpuXwi70ou7NsGd97KVTJ2omepGId429JZt8oaZKeXbqxwg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@ -3440,9 +3440,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.832",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz",
"integrity": "sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz",
"integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==",
"dev": true,
"license": "ISC"
},
@ -5669,9 +5669,9 @@
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"version": "0.14.9",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.9.tgz",
"integrity": "sha512-dC1TJMODGM8lxhP6wLToncaDPPNB3biVxxRDuNCYpuXwi70ou7NsGd97KVTJ2omepGId429JZt8oaZKeXbqxwg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@ -6839,9 +6839,9 @@
}
},
"node_modules/typescript": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@ -7063,19 +7063,19 @@
}
},
"node_modules/vite-plugin-vue-devtools": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.3.6.tgz",
"integrity": "sha512-j4Cssv6DVBtMZfyVBEm/4MZy7BiL6RedEn+f9jT3zFyGZKG1vNuEpTO86XvPPbHbYdITFyrkWb7VQuWyhfSgqA==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.3.7.tgz",
"integrity": "sha512-pPv6YJYrCIlWP+wwRk9gzDp2rK5M5jQ5oz//Nci3C3FDvORL1btKQqGvgthx3hs6xbx5acToJtkMGgDnZg8smw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/devtools-core": "^7.3.6",
"@vue/devtools-kit": "^7.3.6",
"@vue/devtools-shared": "^7.3.6",
"@vue/devtools-core": "^7.3.7",
"@vue/devtools-kit": "^7.3.7",
"@vue/devtools-shared": "^7.3.7",
"execa": "^8.0.1",
"sirv": "^2.0.4",
"vite-plugin-inspect": "^0.8.4",
"vite-plugin-vue-inspector": "^5.1.2"
"vite-plugin-vue-inspector": "^5.1.3"
},
"engines": {
"node": ">=v14.21.3"
@ -7085,9 +7085,9 @@
}
},
"node_modules/vite-plugin-vue-inspector": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.1.2.tgz",
"integrity": "sha512-M+yH2LlQtVNzJAljQM+61CqDXBvHim8dU5ImGaQuwlo13tMDHue5D7IC20YwDJuWDODiYc/cZBUYspVlyPf2vQ==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.1.3.tgz",
"integrity": "sha512-pMrseXIDP1Gb38mOevY+BvtNGNqiqmqa2pKB99lnLsADQww9w9xMbAfT4GB6RUoaOkSPrtlXqpq2Fq+Dj2AgFg==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@ -0,0 +1,26 @@
<template>
<div class="border border-gray-300 rounded mb-4">
<button @click="toggle" class="w-full p-3 bg-gray-100 rounded hover:bg-gray-200 text-left cursor-pointer transition-colors duration-200 ease-in-out">
{{ props.title }}
</button>
<transition enter-active-class="transition-all duration-300 ease-in-out" leave-active-class="transition-all duration-300 ease-in-out" enter-from-class="opacity-0 max-h-0" enter-to-class="opacity-100 max-h-96" leave-from-class="opacity-100 max-h-96" leave-to-class="opacity-0 max-h-0">
<div v-if="isOpen" class="p-3 overflow-hidden">
<slot></slot>
</div>
</transition>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const isOpen = ref(false)
const toggle = () => {
isOpen.value = !isOpen.value
}
const props = defineProps({
title: String
})
</script>

View File

@ -1,10 +1,12 @@
<template>
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap">
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 py-1.5 px-4 inline-flex hover:bg-cyan hover:cursor-pointer">
<div class="relative p-2.5 flex items-center gap-x-2.5">
<input v-model="searchQuery" class="input-cyan flex-grow" placeholder="Search..." @input="handleSearch" />
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 p-1.5 inline-flex items-center justify-center hover:bg-cyan hover:cursor-pointer w-6 h-6">
<input class="hidden" id="upload-asset" ref="objectUploadField" type="file" accept="image/png" multiple @change="handleFileUpload" />
Upload object(s)
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</label>
<input v-model="searchQuery" class="input-cyan w-full" placeholder="Search..." @input="handleSearch" />
<div class="absolute left-0 bottom-0 w-full h-px bg-cyan-200"></div>
</div>
<div v-bind="containerProps" class="overflow-y-auto relative" @scroll="onScroll">

View File

@ -1,56 +1,64 @@
<template>
<div class="h-full overflow-auto">
<div class="relative p-2.5 flex flex-col items-center justify-between h-72">
<div class="filler"></div>
<img class="max-h-56" :src="`${config.server_endpoint}/assets/sprites/${selectedSprite?.id}.png`" :alt="'Sprite ' + selectedSprite?.id" />
<button class="btn-bordeaux px-4 py-1.5 min-w-24" type="button" @click.prevent="removeSprite">Remove</button>
<div class="absolute left-0 bottom-0 w-full h-px bg-cyan-200"></div>
<div class="relative p-2.5 flex flex-col items-center justify-between">
<div class="w-full flex flex-col sm:flex-row items-center gap-9 mb-5">
<div class="w-full sm:flex-grow">
<input v-model="spriteName" class="input-cyan w-full" type="text" name="name" placeholder="New sprite" />
</div>
<div class="flex gap-2 w-full sm:w-auto">
<button class="btn-cyan px-4 py-1.5 flex-1 sm:flex-none sm:min-w-24" type="button" @click.prevent="saveSprite">Save</button>
<button class="btn-bordeaux px-4 py-1.5 flex-1 sm:flex-none sm:min-w-24" type="button" @click.prevent="removeSprite">Remove</button>
</div>
</div>
<div class="w-full h-px bg-cyan-200"></div>
</div>
<div class="m-2.5 p-2.5 block">
<form class="flex gap-2.5 flex-wrap" @submit.prevent="saveSprite">
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="name">Name</label>
<input v-model="spriteName" class="input-cyan" type="text" name="name" placeholder="Wall #1" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="origin-x">Origin X</label>
<input v-model="spriteOriginX" class="input-cyan" type="number" step="any" name="origin-x" placeholder="Origin X" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="origin-y">Origin Y</label>
<input v-model="spriteOriginY" class="input-cyan" type="number" step="any" name="origin-y" placeholder="Origin Y" />
</div>
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-speed">Frame speed</label>
<input v-model="spriteFrameSpeed" class="input-cyan" type="number" step="any" name="frame-speed" placeholder="Frame speed" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-width">Frame width</label>
<input v-model="spriteFrameWidth" class="input-cyan" type="number" step="any" name="frame-width" placeholder="Frame width" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-height">Frame height</label>
<input v-model="spriteFrameHeight" class="input-cyan" type="number" step="any" name="frame-height" placeholder="Frame height" />
</div>
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="is-looping">Is looping</label>
<select v-model="spriteIsLooping" class="input-cyan" name="is-looping">
<option :value="false">No</option>
<option :value="true">Yes</option>
</select>
</div>
<button class="btn-cyan px-4 py-1.5 min-w-24" type="submit">Save</button>
</form>
<div class="m-2.5 px-2.5 block">
<button class="btn-cyan px-4 py-1.5 flex-1 sm:flex-none sm:min-w-24 mb-5" type="button" @click.prevent="addNewImage">New IMG</button>
<Accordion v-for="image in spriteImages" :key="image.id" :title="image.name">
<form class="flex gap-2.5 flex-wrap" @submit.prevent="saveSprite">
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="name">Name</label>
<input v-model="image.name" class="input-cyan" type="text" name="name" placeholder="Wall #1" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="origin-x">Origin X</label>
<input v-model.number="image.origin_x" class="input-cyan" type="number" step="any" name="origin-x" placeholder="Origin X" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="origin-y">Origin Y</label>
<input v-model.number="image.origin_y" class="input-cyan" type="number" step="any" name="origin-y" placeholder="Origin Y" />
</div>
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-speed">Frame speed</label>
<input v-model.number="image.frameSpeed" class="input-cyan" type="number" step="any" name="frame-speed" placeholder="Frame speed" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-width">Frame width</label>
<input v-model.number="image.frameWidth" class="input-cyan" type="number" step="any" name="frame-width" placeholder="Frame width" />
</div>
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="frame-height">Frame height</label>
<input v-model.number="image.frameHeight" class="input-cyan" type="number" step="any" name="frame-height" placeholder="Frame height" />
</div>
<div class="w-full flex flex-col mb-5">
<label class="mb-1.5 font-titles" for="is-looping">Is looping</label>
<select v-model="image.isLooping" class="input-cyan" name="is-looping">
<option :value="false">No</option>
<option :value="true">Yes</option>
</select>
</div>
</form>
</Accordion>
</div>
</div>
</template>
<script setup lang="ts">
import type { Sprite } from '@/types'
import type { Sprite, SpriteImage } from '@/types'
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useAssetManagerStore } from '@/stores/assetManager'
import { useGameStore } from '@/stores/game'
import config from '@/config'
import Accordion from '@/components/utilities/Accordion.vue'
const gameStore = useGameStore()
const assetManagerStore = useAssetManagerStore()
@ -58,12 +66,7 @@ const assetManagerStore = useAssetManagerStore()
const selectedSprite = computed(() => assetManagerStore.selectedSprite)
const spriteName = ref('')
const spriteOriginX = ref(0)
const spriteOriginY = ref(0)
const spriteFrameSpeed = ref(0)
const spriteFrameWidth = ref(0)
const spriteFrameHeight = ref(0)
const spriteIsLooping = ref(false)
const spriteImages = ref<SpriteImage[]>([])
if (!selectedSprite.value) {
console.error('No sprite selected')
@ -71,12 +74,7 @@ if (!selectedSprite.value) {
if (selectedSprite.value) {
spriteName.value = selectedSprite.value.name
spriteOriginX.value = selectedSprite.value.origin_x
spriteOriginY.value = selectedSprite.value.origin_y
spriteFrameSpeed.value = selectedSprite.value.frameSpeed
spriteFrameWidth.value = selectedSprite.value.frameWidth
spriteFrameHeight.value = selectedSprite.value.frameHeight
spriteIsLooping.value = selectedSprite.value.isLooping
spriteImages.value = selectedSprite.value.spriteImages
}
function removeSprite() {
@ -105,37 +103,51 @@ function saveSprite() {
return
}
gameStore.connection?.emit(
'gm:sprite:update',
{
id: selectedSprite.value.id,
name: spriteName.value,
origin_x: spriteOriginX.value,
origin_y: spriteOriginY.value,
frameSpeed: spriteFrameSpeed.value,
frameWidth: spriteFrameWidth.value,
frameHeight: spriteFrameHeight.value,
isLooping: spriteIsLooping.value
},
(response: boolean) => {
if (!response) {
console.error('Failed to save sprite')
return
}
refreshSpriteList(false)
const updatedSprite = {
id: selectedSprite.value.id,
name: spriteName.value,
spriteImages: selectedSprite.value.spriteImages
}
gameStore.connection?.emit('gm:sprite:update', updatedSprite, (response: boolean) => {
if (!response) {
console.error('Failed to save sprite')
return
}
)
refreshSpriteList(false)
})
}
function addNewImage() {
if (!selectedSprite.value) return
const newImage: SpriteImage = {
id: Date.now().toString(), // Temporary ID, should be replaced by server-generated ID
name: 'New image',
origin_x: 0,
origin_y: 0,
frameSpeed: 0,
frameWidth: 0,
frameHeight: 0,
isAnimated: false,
spriteId: selectedSprite.value.id,
sprite: selectedSprite.value,
action: '',
isLooping: false
}
// spriteimages value can be undefined
if (!spriteImages.value) {
spriteImages.value = []
}
spriteImages.value.push(newImage)
}
watch(selectedSprite, (sprite: Sprite | null) => {
if (!sprite) return
spriteName.value = sprite.name
spriteOriginX.value = sprite.origin_x
spriteOriginY.value = sprite.origin_y
spriteFrameSpeed.value = sprite.frameSpeed
spriteFrameWidth.value = sprite.frameWidth
spriteFrameHeight.value = sprite.frameHeight
spriteIsLooping.value = sprite.isLooping
spriteImages.value = sprite.spriteImages
})
onMounted(() => {

View File

@ -1,19 +1,17 @@
<template>
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap">
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 py-1.5 px-4 inline-flex hover:bg-cyan hover:cursor-pointer">
<input class="hidden" id="upload-asset" ref="spriteUploadField" type="file" accept="image/png" multiple @change="handleFileUpload" />
Upload sprite(s)
</label>
<input v-model="searchQuery" class="input-cyan w-full" placeholder="Search..." @input="handleSearch" />
<div class="relative p-2.5 flex items-center gap-x-2.5">
<input v-model="searchQuery" class="input-cyan flex-grow" placeholder="Search..." @input="handleSearch" />
<button @click.prevent="newButtonClickHandler" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 p-1.5 inline-flex items-center justify-center hover:bg-cyan hover:cursor-pointer w-9 h-9">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</button>
<div class="absolute left-0 bottom-0 w-full h-px bg-cyan-200"></div>
</div>
<div v-bind="containerProps" class="overflow-y-auto relative" @scroll="onScroll">
<div v-bind="wrapperProps" ref="elementToScroll">
<a v-for="{ data: sprite } in list" :key="sprite.id" class="relative p-2.5 cursor-pointer block" :class="{ 'bg-cyan/80': assetManagerStore.selectedSprite?.id === sprite.id }" @click="assetManagerStore.setSelectedSprite(sprite as Sprite)">
<div class="flex items-center gap-2.5">
<div class="h-7 w-16 max-w-16 flex justify-center">
<img class="h-7" :src="`${config.server_endpoint}/assets/sprites/${sprite.id}.png`" alt="Sprite" />
</div>
<span>{{ sprite.name }}</span>
</div>
<div class="absolute left-0 bottom-0 w-full h-px bg-cyan-200"></div>
@ -35,7 +33,6 @@ import { useVirtualList } from '@vueuse/core'
import type { Sprite } from '@/types'
const gameStore = useGameStore()
const spriteUploadField = ref(null)
const assetManagerStore = useAssetManagerStore()
const assetStore = useAssetStore()
@ -44,17 +41,13 @@ const searchQuery = ref('')
const hasScrolled = ref(false)
const elementToScroll = ref()
const handleFileUpload = (e: Event) => {
const files = (e.target as HTMLInputElement).files
if (!files) return
gameStore.connection?.emit('gm:sprite:upload', files, (response: boolean) => {
function newButtonClickHandler() {
gameStore.connection?.emit('gm:sprite:create', {}, (response: boolean) => {
if (!response) {
if (config.development) console.error('Failed to upload sprite')
if (config.development) console.error('Failed to create new sprite')
return
}
assetStore.fetchAssets()
gameStore.connection?.emit('gm:sprite:list', {}, (response: Sprite[]) => {
assetManagerStore.setSpriteList(response)
})

View File

@ -1,10 +1,12 @@
<template>
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap">
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 py-1.5 px-4 inline-flex hover:bg-cyan hover:cursor-pointer">
<div class="relative p-2.5 flex items-center gap-x-2.5">
<input v-model="searchQuery" class="input-cyan flex-grow" placeholder="Search..." @input="handleSearch" />
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 p-1.5 inline-flex items-center justify-center hover:bg-cyan hover:cursor-pointer w-6 h-6">
<input class="hidden" id="upload-asset" ref="tileUploadField" type="file" accept="image/png" multiple @change="handleFileUpload" />
Upload tile(s)
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</label>
<input v-model="searchQuery" class="input-cyan w-full" placeholder="Search..." @input="handleSearch" />
<div class="absolute left-0 bottom-0 w-full h-px bg-cyan-200"></div>
</div>
<div v-bind="containerProps" class="overflow-y-auto relative" @scroll="onScroll">

View File

@ -67,6 +67,7 @@ async function loginFunc() {
gameStore.setToken(response.token)
gameStore.initConnection()
return true // Indicate success
}
async function registerFunc() {
@ -84,6 +85,9 @@ async function registerFunc() {
return
}
await loginFunc()
const loginSuccess = await loginFunc()
if (!loginSuccess) {
notifications.addNotification({ message: 'Login after registration failed. Please try logging in manually.' })
}
}
</script>

View File

@ -10,23 +10,10 @@ export type Asset = {
type: 'base64' | 'link'
}
export type Sprite = {
id: string
name: string
origin_x: number
origin_y: number
frameSpeed: number
frameWidth: number
frameHeight: number
isLooping: boolean
createdAt: Date
updatedAt: Date
}
export type Tile = {
id: string
name: string
tags?: any
tags: any | null
createdAt: Date
updatedAt: Date
}
@ -34,7 +21,7 @@ export type Tile = {
export type Object = {
id: string
name: string
tags?: any
tags: any | null
origin_x: number
origin_y: number
isAnimated: boolean
@ -49,54 +36,19 @@ export type Object = {
export type Item = {
id: string
name: string
description: string
description: string | null
stackable: boolean
createdAt: Date
updatedAt: Date
characters: CharacterItem[]
}
export type User = {
id: number
username: string
password: string
characters: Character[]
}
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[]
items: CharacterItem[]
}
export type CharacterItem = {
id: number
characterId: number
character: Character
itemId: string
item: Item
quantity: number
}
export type Zone = {
id: number
name: string
width: number
height: number
tiles: string[][]
tiles: any | null
pvp: boolean
zoneEventTiles: ZoneEventTile[]
zoneObjects: ZoneObject[]
@ -133,6 +85,92 @@ export type ZoneEventTile = {
position_y: number
}
export type User = {
id: number
username: string
password: string
characters: Character[]
}
export enum CharacterGender {
MALE = 'MALE',
FEMALE = 'FEMALE'
}
export enum CharacterRace {
HUMAN = 'HUMAN',
ELF = 'ELF',
DWARF = 'DWARF',
ORC = 'ORC',
GOBLIN = 'GOBLIN'
}
export type CharacterType = {
id: number
name: string
gender: CharacterGender
race: CharacterRace
characters: Character[]
spriteId: string
sprite: Sprite
createdAt: Date
updatedAt: Date
}
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
characterTypeId: number | null
characterType: CharacterType | null
chats: Chat[]
items: CharacterItem[]
}
export type CharacterItem = {
id: number
characterId: number
character: Character
itemId: string
item: Item
quantity: number
}
export type Sprite = {
id: string
name: string
createdAt: Date
updatedAt: Date
spriteImages: SpriteImage[]
characterTypes: CharacterType[]
}
export type SpriteImage = {
id: string
spriteId: string
sprite: Sprite
name: string
action: string
origin_x: number
origin_y: number
isAnimated: boolean
isLooping: boolean
frameWidth: number
frameHeight: number
frameSpeed: number
}
export type Chat = {
id: number
characterId: number