From e389534e30c923708c28752c3e1b7636a06f8c9f Mon Sep 17 00:00:00 2001 From: Dennis Postma <dennis@directonline.io> Date: Fri, 31 Jan 2025 22:33:45 +0100 Subject: [PATCH 1/3] npm run format --- src/components/game/gui/Minimap.vue | 8 ++--- .../partials/sprite/SpriteDetails.vue | 29 +++++++++---------- .../sprite/partials/SpriteImagesInput.vue | 6 ++-- .../sprite/partials/SpritePreview.vue | 12 ++------ .../controls/useBaseControlsComposable.ts | 6 ++-- .../controls/useGameControlsComposable.ts | 2 +- .../useMapEditorControlsComposable.ts | 2 +- src/composables/useControlsComposable.ts | 7 ++--- 8 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/components/game/gui/Minimap.vue b/src/components/game/gui/Minimap.vue index d3cefb4..35c8ecd 100644 --- a/src/components/game/gui/Minimap.vue +++ b/src/components/game/gui/Minimap.vue @@ -5,12 +5,12 @@ </div> <div class="absolute -bottom-3 left-1/2 -translate-x-1/2 flex gap-1"> <button class="w-6 h-6 relative p-0"> - <img class="w-3 h-3 center-element" src="/assets/icons/plus-icon.svg" alt="Zoom-in button icon"/> - <img class="w-full h-full" src="/assets/ui-elements/button-ui-box-textured.svg" alt=""/> + <img class="w-3 h-3 center-element" src="/assets/icons/plus-icon.svg" alt="Zoom-in button icon" /> + <img class="w-full h-full" src="/assets/ui-elements/button-ui-box-textured.svg" alt="" /> </button> <button class="w-6 h-6 relative p-0"> - <img class="w-3 h-3 center-element" src="/assets/icons/minus-icon.svg" alt="Zoom-out button icon"/> - <img class="w-full h-full" src="/assets/ui-elements/button-ui-box-textured.svg" alt=""/> + <img class="w-3 h-3 center-element" src="/assets/icons/minus-icon.svg" alt="Zoom-out button icon" /> + <img class="w-full h-full" src="/assets/ui-elements/button-ui-box-textured.svg" alt="" /> </button> </div> </div> diff --git a/src/components/gameMaster/assetManager/partials/sprite/SpriteDetails.vue b/src/components/gameMaster/assetManager/partials/sprite/SpriteDetails.vue index 705af93..bffd251 100644 --- a/src/components/gameMaster/assetManager/partials/sprite/SpriteDetails.vue +++ b/src/components/gameMaster/assetManager/partials/sprite/SpriteDetails.vue @@ -48,23 +48,20 @@ <input v-model.number="action.frameRate" class="input-field" type="number" step="any" name="frame-speed" placeholder="Frame rate" /> </div> <div class="form-field-full"> - <SpriteActionsInput - v-model="action.sprites" - @tempOffsetChange="(index, offset) => handleTempOffsetChange(action, index, offset)" - /> + <SpriteActionsInput v-model="action.sprites" @tempOffsetChange="(index, offset) => handleTempOffsetChange(action, index, offset)" /> </div> </form> </template> </Accordion> - <SpritePreview - v-if="selectedAction" - :sprites="selectedAction.sprites" - :frame-rate="selectedAction.frameRate" - :is-modal-open="isModalOpen" - :temp-offset-index="tempOffsetData.index" - :temp-offset="tempOffsetData.offset" - @update:frame-rate="updateFrameRate" - @update:is-modal-open="isModalOpen = $event" + <SpritePreview + v-if="selectedAction" + :sprites="selectedAction.sprites" + :frame-rate="selectedAction.frameRate" + :is-modal-open="isModalOpen" + :temp-offset-index="tempOffsetData.index" + :temp-offset="tempOffsetData.offset" + @update:frame-rate="updateFrameRate" + @update:is-modal-open="isModalOpen = $event" /> </div> </div> @@ -199,9 +196,9 @@ function updateFrameRate(value: number) { } } -const tempOffsetData = ref<{ index: number | undefined; offset: { x: number; y: number } | undefined }>({ - index: undefined, - offset: undefined +const tempOffsetData = ref<{ index: number | undefined; offset: { x: number; y: number } | undefined }>({ + index: undefined, + offset: undefined }) function handleTempOffsetChange(action: SpriteAction, index: number, offset: { x: number; y: number }) { diff --git a/src/components/gameMaster/assetManager/partials/sprite/partials/SpriteImagesInput.vue b/src/components/gameMaster/assetManager/partials/sprite/partials/SpriteImagesInput.vue index 96b6061..00d047c 100644 --- a/src/components/gameMaster/assetManager/partials/sprite/partials/SpriteImagesInput.vue +++ b/src/components/gameMaster/assetManager/partials/sprite/partials/SpriteImagesInput.vue @@ -2,9 +2,7 @@ <div class="flex flex-wrap gap-3"> <div v-for="(image, index) in modelValue" :key="index" class="h-20 w-20 p-4 bg-gray-300 bg-opacity-50 rounded text-center relative group cursor-move" draggable="true" @dragstart="dragStart($event, index)" @dragover.prevent @dragenter.prevent @drop="drop($event, index)"> <img :src="image.url" class="max-w-full max-h-full object-contain pointer-events-none" alt="Uploaded image" @load="updateImageDimensions($event, index)" /> - <div v-if="image.dimensions" class="absolute bottom-1 right-1 bg-black/50 text-white text-xs px-1 py-0.5 rounded transition-opacity font-default"> - {{ image.dimensions.width }}x{{ image.dimensions.height }} - </div> + <div v-if="image.dimensions" class="absolute bottom-1 right-1 bg-black/50 text-white text-xs px-1 py-0.5 rounded transition-opacity font-default">{{ image.dimensions.width }}x{{ image.dimensions.height }}</div> <div class="absolute top-1 left-1 flex-row space-y-1"> <button @click.stop="deleteImage(index)" class="bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" aria-label="Delete image"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> @@ -78,7 +76,7 @@ const props = withDefaults(defineProps<Props>(), { const emit = defineEmits<{ (e: 'update:modelValue', value: SpriteImage[]): void (e: 'close'): void - (e: 'tempOffsetChange', index: number, offset: { x: number, y: number }): void + (e: 'tempOffsetChange', index: number, offset: { x: number; y: number }): void }>() const fileInput = ref<HTMLInputElement | null>(null) diff --git a/src/components/gameMaster/assetManager/partials/sprite/partials/SpritePreview.vue b/src/components/gameMaster/assetManager/partials/sprite/partials/SpritePreview.vue index c44e6ea..61bcc4e 100644 --- a/src/components/gameMaster/assetManager/partials/sprite/partials/SpritePreview.vue +++ b/src/components/gameMaster/assetManager/partials/sprite/partials/SpritePreview.vue @@ -39,15 +39,7 @@ </div> <div class="flex flex-col"> <label class="block mb-2 text-white">Frame: {{ currentFrame + 1 }} of {{ sprites.length }}</label> - <input - type="range" - v-model.number="currentFrame" - :min="0" - :max="sprites.length - 1" - step="1" - class="w-full accent-cyan-500" - @input="stopAnimation" - /> + <input type="range" v-model.number="currentFrame" :min="0" :max="sprites.length - 1" step="1" class="w-full accent-cyan-500" @input="stopAnimation" /> </div> <div class="flex flex-col"> <label class="block mb-2 text-white">Zoom: {{ zoomLevel }}%</label> @@ -69,7 +61,7 @@ const props = defineProps<{ frameRate: number isModalOpen?: boolean tempOffsetIndex?: number - tempOffset?: { x: number, y: number } + tempOffset?: { x: number; y: number } }>() const emit = defineEmits<{ diff --git a/src/composables/controls/useBaseControlsComposable.ts b/src/composables/controls/useBaseControlsComposable.ts index 5651152..dce4312 100644 --- a/src/composables/controls/useBaseControlsComposable.ts +++ b/src/composables/controls/useBaseControlsComposable.ts @@ -32,8 +32,8 @@ export function useBaseControlsComposable(scene: Phaser.Scene, layer: Phaser.Til if (Math.abs(deltaX) <= dragThreshold && Math.abs(deltaY) <= dragThreshold) return - const scrollX = camera.scrollX - (deltaX / camera.zoom) - const scrollY = camera.scrollY - (deltaY / camera.zoom) + const scrollX = camera.scrollX - deltaX / camera.zoom + const scrollY = camera.scrollY - deltaY / camera.zoom camera.setScroll(scrollX, scrollY) pointerStartPosition.value = { x: pointer.x, y: pointer.y } @@ -66,4 +66,4 @@ export function useBaseControlsComposable(scene: Phaser.Scene, layer: Phaser.Til handleZoom, pointerStartPosition } -} \ No newline at end of file +} diff --git a/src/composables/controls/useGameControlsComposable.ts b/src/composables/controls/useGameControlsComposable.ts index 79e26d3..a79297d 100644 --- a/src/composables/controls/useGameControlsComposable.ts +++ b/src/composables/controls/useGameControlsComposable.ts @@ -1,7 +1,7 @@ import { getTile } from '@/composables/mapComposable' import { useGameStore } from '@/stores/gameStore' -import { useBaseControlsComposable } from './useBaseControlsComposable' import type { Ref } from 'vue' +import { useBaseControlsComposable } from './useBaseControlsComposable' export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) { const gameStore = useGameStore() diff --git a/src/composables/controls/useMapEditorControlsComposable.ts b/src/composables/controls/useMapEditorControlsComposable.ts index 8c3afa4..c0db832 100644 --- a/src/composables/controls/useMapEditorControlsComposable.ts +++ b/src/composables/controls/useMapEditorControlsComposable.ts @@ -1,6 +1,6 @@ import { useMapEditorComposable } from '@/composables/useMapEditorComposable' -import { useBaseControlsComposable } from './useBaseControlsComposable' import { computed, type Ref } from 'vue' +import { useBaseControlsComposable } from './useBaseControlsComposable' export function useMapEditorControlsComposable(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) { const mapEditor = useMapEditorComposable() diff --git a/src/composables/useControlsComposable.ts b/src/composables/useControlsComposable.ts index 92cab72..2ce48fb 100644 --- a/src/composables/useControlsComposable.ts +++ b/src/composables/useControlsComposable.ts @@ -1,15 +1,14 @@ -import { useMapEditorComposable } from '@/composables/useMapEditorComposable' -import { computed, watch, type Ref } from 'vue' import { useGameControlsComposable } from '@/composables/controls/useGameControlsComposable' import { useMapEditorControlsComposable } from '@/composables/controls/useMapEditorControlsComposable' -import { useGameStore } from '@/stores/gameStore' +import { useMapEditorComposable } from '@/composables/useMapEditorComposable' +import { computed, type Ref } from 'vue' export function useControlsComposable(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>) { const camera = scene.cameras.main - const mapEditor = useMapEditorComposable() const gameHandlers = useGameControlsComposable(scene, layer, waypoint, camera) const mapEditorHandlers = useMapEditorControlsComposable(scene, layer, waypoint, camera) + const mapEditor = useMapEditorComposable() const currentHandlers = computed(() => (mapEditor.active.value ? mapEditorHandlers : gameHandlers)) const setupControls = () => currentHandlers.value.setupControls() From a9de031673a9c623a8cd2d67d11dc3b82d437873 Mon Sep 17 00:00:00 2001 From: Dennis Postma <dennis@directonline.io> Date: Sat, 1 Feb 2025 01:31:28 +0100 Subject: [PATCH 2/3] poc --- src/components/game/map/Map.vue | 6 +++++ .../controls/useGameControlsComposable.ts | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/components/game/map/Map.vue b/src/components/game/map/Map.vue index a9a6df7..fb95343 100644 --- a/src/components/game/map/Map.vue +++ b/src/components/game/map/Map.vue @@ -34,6 +34,12 @@ gameStore.connection?.on('map:character:leave', (characterId: UUID) => { gameStore.connection?.on('map:character:move', (data: { characterId: UUID; positionX: number; positionY: number; rotation: number; isMoving: boolean }) => { mapStore.updateCharacterPosition(data) + // @TODO: Replace with universal class, composable or store + if (data.characterId === gameStore.character?.id) { + gameStore.character!.positionX = data.positionX + gameStore.character!.positionY = data.positionY + gameStore.character!.rotation = data.rotation + } }) onUnmounted(() => { diff --git a/src/composables/controls/useGameControlsComposable.ts b/src/composables/controls/useGameControlsComposable.ts index a79297d..fa52197 100644 --- a/src/composables/controls/useGameControlsComposable.ts +++ b/src/composables/controls/useGameControlsComposable.ts @@ -28,11 +28,35 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til }) } + function handleArrowKeys(event: KeyboardEvent) { + if (event.key === 'ArrowLeft') { + console.log('ArrowLeft') + gameStore.connection?.emit('map:character:move', { positionX: gameStore.character!.positionX - 1, positionY: 0 }) + return + } + if (event.key === 'ArrowRight') { + console.log('ArrowRight') + gameStore.connection?.emit('map:character:move', { positionX: gameStore.character!.positionX + 1, positionY: 0 }) + return + } + if (event.key === 'ArrowUp') { + console.log('ArrowUp') + gameStore.connection?.emit('map:character:move', { positionX: 0, positionY: gameStore.character!.positionY - 1 }) + return + } + if (event.key === 'ArrowDown') { + console.log('ArrowDown') + gameStore.connection?.emit('map:character:move', { positionX: 0, positionY: gameStore.character!.positionY + 1 }) + return + } + } + const setupControls = () => { scene.input.on(Phaser.Input.Events.POINTER_DOWN, handlePointerDown) scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.on(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) + scene.input.keyboard!.on(Phaser.Input.Keyboard.Events.KEY_DOWN, handleArrowKeys) } const cleanupControls = () => { @@ -40,6 +64,7 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.off(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) + scene.input.keyboard!.off(Phaser.Input.Keyboard.Events.KEY_DOWN, handleArrowKeys) } return { setupControls, cleanupControls } From 9d9556267929ea0cb32e9edab3cd9dda9eb22ecb Mon Sep 17 00:00:00 2001 From: Dennis Postma <dennis@directonline.io> Date: Sat, 1 Feb 2025 01:43:01 +0100 Subject: [PATCH 3/3] Implemented logic to walk with arrow keys --- .../mapEditor/partials/TileList.vue | 22 +++--- src/components/screens/MapEditor.vue | 2 +- .../controls/useGameControlsComposable.ts | 74 ++++++++++++++----- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/components/gameMaster/mapEditor/partials/TileList.vue b/src/components/gameMaster/mapEditor/partials/TileList.vue index 718c163..248a290 100644 --- a/src/components/gameMaster/mapEditor/partials/TileList.vue +++ b/src/components/gameMaster/mapEditor/partials/TileList.vue @@ -31,14 +31,14 @@ @click="openGroup(group)" @load="() => processTile(group.parent)" :class="{ - 'border-cyan shadow-lg': isActiveTile(group.parent), - 'border-transparent hover:border-gray-300': !isActiveTile(group.parent) - }" + 'border-cyan shadow-lg': isActiveTile(group.parent), + 'border-transparent hover:border-gray-300': !isActiveTile(group.parent) + }" /> <span class="text-xs mt-1">{{ getTileCategory(group.parent) }}</span> <span v-if="group.children.length > 0" class="absolute top-0 right-0 bg-cyan text-white rounded-full w-5 h-5 flex items-center justify-center text-xs"> - {{ group.children.length + 1 }} - </span> + {{ group.children.length + 1 }} + </span> </div> </div> </div> @@ -56,9 +56,9 @@ :alt="selectedGroup.parent.name" @click="selectTile(selectedGroup.parent.id)" :class="{ - 'border-cyan shadow-lg': isActiveTile(selectedGroup.parent), - 'border-transparent hover:border-gray-300': !isActiveTile(selectedGroup.parent) - }" + 'border-cyan shadow-lg': isActiveTile(selectedGroup.parent), + 'border-transparent hover:border-gray-300': !isActiveTile(selectedGroup.parent) + }" /> <span class="text-xs mt-1">{{ getTileCategory(selectedGroup.parent) }}</span> </div> @@ -69,9 +69,9 @@ :alt="childTile.name" @click="selectTile(childTile.id)" :class="{ - 'border-cyan shadow-lg': isActiveTile(childTile), - 'border-transparent hover:border-gray-300': !isActiveTile(childTile) - }" + 'border-cyan shadow-lg': isActiveTile(childTile), + 'border-transparent hover:border-gray-300': !isActiveTile(childTile) + }" /> <span class="text-xs mt-1">{{ getTileCategory(childTile) }}</span> </div> diff --git a/src/components/screens/MapEditor.vue b/src/components/screens/MapEditor.vue index 1599663..1c1eaac 100644 --- a/src/components/screens/MapEditor.vue +++ b/src/components/screens/MapEditor.vue @@ -19,7 +19,7 @@ /> <MapList ref="mapModal" @open-create-map="mapSettingsModal?.open" /> <TileList ref="tileList" /> - <ObjectList ref="objectList"/> + <ObjectList ref="objectList" /> <MapSettings ref="mapSettingsModal" /> <TeleportModal ref="teleportModal" /> </div> diff --git a/src/composables/controls/useGameControlsComposable.ts b/src/composables/controls/useGameControlsComposable.ts index fa52197..6fff96f 100644 --- a/src/composables/controls/useGameControlsComposable.ts +++ b/src/composables/controls/useGameControlsComposable.ts @@ -28,26 +28,60 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til }) } - function handleArrowKeys(event: KeyboardEvent) { - if (event.key === 'ArrowLeft') { - console.log('ArrowLeft') - gameStore.connection?.emit('map:character:move', { positionX: gameStore.character!.positionX - 1, positionY: 0 }) - return + const pressedKeys = new Set<string>() + let moveInterval: number | null = null + + function handleKeyDown(event: KeyboardEvent) { + if (!gameStore.character) return + + if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) { + pressedKeys.add(event.key) + + // Start movement loop if not already running + if (!moveInterval) { + moveInterval = window.setInterval(moveCharacter, 250) // Adjust timing as needed + moveCharacter() // Move immediately on first press + } } - if (event.key === 'ArrowRight') { - console.log('ArrowRight') - gameStore.connection?.emit('map:character:move', { positionX: gameStore.character!.positionX + 1, positionY: 0 }) - return + } + + function handleKeyUp(event: KeyboardEvent) { + pressedKeys.delete(event.key) + + // If no movement keys are pressed, clear the interval + if (pressedKeys.size === 0 && moveInterval) { + clearInterval(moveInterval) + moveInterval = null } - if (event.key === 'ArrowUp') { - console.log('ArrowUp') - gameStore.connection?.emit('map:character:move', { positionX: 0, positionY: gameStore.character!.positionY - 1 }) - return + } + + function moveCharacter() { + if (!gameStore.character) return + const { positionX, positionY } = gameStore.character + + if (pressedKeys.has('ArrowLeft')) { + gameStore.connection?.emit('map:character:move', { + positionX: positionX - 1, + positionY: positionY + }) } - if (event.key === 'ArrowDown') { - console.log('ArrowDown') - gameStore.connection?.emit('map:character:move', { positionX: 0, positionY: gameStore.character!.positionY + 1 }) - return + if (pressedKeys.has('ArrowRight')) { + gameStore.connection?.emit('map:character:move', { + positionX: positionX + 1, + positionY: positionY + }) + } + if (pressedKeys.has('ArrowUp')) { + gameStore.connection?.emit('map:character:move', { + positionX: positionX, + positionY: positionY - 1 + }) + } + if (pressedKeys.has('ArrowDown')) { + gameStore.connection?.emit('map:character:move', { + positionX: positionX, + positionY: positionY + 1 + }) } } @@ -56,7 +90,8 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.on(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) - scene.input.keyboard!.on(Phaser.Input.Keyboard.Events.KEY_DOWN, handleArrowKeys) + scene.input.keyboard!.on('keydown', handleKeyDown) + scene.input.keyboard!.on('keyup', handleKeyUp) } const cleanupControls = () => { @@ -64,7 +99,8 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.off(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) - scene.input.keyboard!.off(Phaser.Input.Keyboard.Events.KEY_DOWN, handleArrowKeys) + scene.input.keyboard!.off('keydown', handleKeyDown) + scene.input.keyboard!.off('keyup', handleKeyUp) } return { setupControls, cleanupControls }