1
0
forked from noxious/client

Merge remote-tracking branch 'origin/main' into feature/#321

This commit is contained in:
Dennis Postma 2025-02-01 01:43:12 +01:00
commit 09b458eeef
11 changed files with 108 additions and 55 deletions

View File

@ -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 }) => { gameStore.connection?.on('map:character:move', (data: { characterId: UUID; positionX: number; positionY: number; rotation: number; isMoving: boolean }) => {
mapStore.updateCharacterPosition(data) 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(() => { onUnmounted(() => {

View File

@ -48,10 +48,7 @@
<input v-model.number="action.frameRate" class="input-field" type="number" step="any" name="frame-speed" placeholder="Frame rate" /> <input v-model.number="action.frameRate" class="input-field" type="number" step="any" name="frame-speed" placeholder="Frame rate" />
</div> </div>
<div class="form-field-full"> <div class="form-field-full">
<SpriteActionsInput <SpriteActionsInput v-model="action.sprites" @tempOffsetChange="(index, offset) => handleTempOffsetChange(action, index, offset)" />
v-model="action.sprites"
@tempOffsetChange="(index, offset) => handleTempOffsetChange(action, index, offset)"
/>
</div> </div>
</form> </form>
</template> </template>

View File

@ -2,9 +2,7 @@
<div class="flex flex-wrap gap-3"> <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)"> <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)" /> <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"> <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>
{{ image.dimensions.width }}x{{ image.dimensions.height }}
</div>
<div class="absolute top-1 left-1 flex-row space-y-1"> <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"> <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"> <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<{ const emit = defineEmits<{
(e: 'update:modelValue', value: SpriteImage[]): void (e: 'update:modelValue', value: SpriteImage[]): void
(e: 'close'): 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) const fileInput = ref<HTMLInputElement | null>(null)

View File

@ -39,15 +39,7 @@
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<label class="block mb-2 text-white">Frame: {{ currentFrame + 1 }} of {{ sprites.length }}</label> <label class="block mb-2 text-white">Frame: {{ currentFrame + 1 }} of {{ sprites.length }}</label>
<input <input type="range" v-model.number="currentFrame" :min="0" :max="sprites.length - 1" step="1" class="w-full accent-cyan-500" @input="stopAnimation" />
type="range"
v-model.number="currentFrame"
:min="0"
:max="sprites.length - 1"
step="1"
class="w-full accent-cyan-500"
@input="stopAnimation"
/>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<label class="block mb-2 text-white">Zoom: {{ zoomLevel }}%</label> <label class="block mb-2 text-white">Zoom: {{ zoomLevel }}%</label>
@ -69,7 +61,7 @@ const props = defineProps<{
frameRate: number frameRate: number
isModalOpen?: boolean isModalOpen?: boolean
tempOffsetIndex?: number tempOffsetIndex?: number
tempOffset?: { x: number, y: number } tempOffset?: { x: number; y: number }
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -32,8 +32,8 @@ export function useBaseControlsComposable(scene: Phaser.Scene, layer: Phaser.Til
if (Math.abs(deltaX) <= dragThreshold && Math.abs(deltaY) <= dragThreshold) return if (Math.abs(deltaX) <= dragThreshold && Math.abs(deltaY) <= dragThreshold) return
const scrollX = camera.scrollX - (deltaX / camera.zoom) const scrollX = camera.scrollX - deltaX / camera.zoom
const scrollY = camera.scrollY - (deltaY / camera.zoom) const scrollY = camera.scrollY - deltaY / camera.zoom
camera.setScroll(scrollX, scrollY) camera.setScroll(scrollX, scrollY)
pointerStartPosition.value = { x: pointer.x, y: pointer.y } pointerStartPosition.value = { x: pointer.x, y: pointer.y }

View File

@ -1,7 +1,7 @@
import { getTile } from '@/composables/mapComposable' import { getTile } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useBaseControlsComposable } from './useBaseControlsComposable'
import type { Ref } from 'vue' 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) { 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() const gameStore = useGameStore()
@ -28,11 +28,70 @@ export function useGameControlsComposable(scene: Phaser.Scene, layer: Phaser.Til
}) })
} }
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
}
}
}
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
}
}
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 (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
})
}
}
const setupControls = () => { const setupControls = () => {
scene.input.on(Phaser.Input.Events.POINTER_DOWN, handlePointerDown) 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_MOVE, handlePointerMove)
scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp)
scene.input.on(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) scene.input.on(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom)
scene.input.keyboard!.on('keydown', handleKeyDown)
scene.input.keyboard!.on('keyup', handleKeyUp)
} }
const cleanupControls = () => { const cleanupControls = () => {
@ -40,6 +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_MOVE, handlePointerMove)
scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp)
scene.input.off(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom) scene.input.off(Phaser.Input.Events.POINTER_WHEEL, baseHandlers.handleZoom)
scene.input.keyboard!.off('keydown', handleKeyDown)
scene.input.keyboard!.off('keyup', handleKeyUp)
} }
return { setupControls, cleanupControls } return { setupControls, cleanupControls }

View File

@ -1,6 +1,6 @@
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { useBaseControlsComposable } from './useBaseControlsComposable'
import { computed, type Ref } from 'vue' 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) { 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() const mapEditor = useMapEditorComposable()

View File

@ -1,15 +1,14 @@
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { computed, watch, type Ref } from 'vue'
import { useGameControlsComposable } from '@/composables/controls/useGameControlsComposable' import { useGameControlsComposable } from '@/composables/controls/useGameControlsComposable'
import { useMapEditorControlsComposable } from '@/composables/controls/useMapEditorControlsComposable' 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 }>) { export function useControlsComposable(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>) {
const camera = scene.cameras.main const camera = scene.cameras.main
const mapEditor = useMapEditorComposable()
const gameHandlers = useGameControlsComposable(scene, layer, waypoint, camera) const gameHandlers = useGameControlsComposable(scene, layer, waypoint, camera)
const mapEditorHandlers = useMapEditorControlsComposable(scene, layer, waypoint, camera) const mapEditorHandlers = useMapEditorControlsComposable(scene, layer, waypoint, camera)
const mapEditor = useMapEditorComposable()
const currentHandlers = computed(() => (mapEditor.active.value ? mapEditorHandlers : gameHandlers)) const currentHandlers = computed(() => (mapEditor.active.value ? mapEditorHandlers : gameHandlers))
const setupControls = () => currentHandlers.value.setupControls() const setupControls = () => currentHandlers.value.setupControls()