forked from noxious/client
Merge remote-tracking branch 'origin/main' into feature/map-refactor
# Conflicts: # src/components/gameMaster/mapEditor/Map.vue # src/components/gameMaster/mapEditor/partials/MapObjectList.vue # src/components/gameMaster/mapEditor/partials/TileList.vue # src/components/screens/MapEditor.vue # src/composables/pointerHandlers/useMapEditorPointerHandlers.ts
This commit is contained in:
69
src/composables/controls/useBaseControlsComposable.ts
Normal file
69
src/composables/controls/useBaseControlsComposable.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import config from '@/application/config'
|
||||
import { getTile, tileToWorldXY } from '@/composables/mapComposable'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { ref, type Ref } from 'vue'
|
||||
|
||||
export function useBaseControlsComposable(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
const gameStore = useGameStore()
|
||||
const pointerStartPosition = ref({ x: 0, y: 0 })
|
||||
const dragThreshold = 5 // pixels
|
||||
|
||||
function updateWaypoint(worldX: number, worldY: number) {
|
||||
const pointerTile = getTile(layer, worldX, worldY)
|
||||
if (!pointerTile) {
|
||||
waypoint.value.visible = false
|
||||
return
|
||||
}
|
||||
const worldPoint = tileToWorldXY(layer, pointerTile.x, pointerTile.y)
|
||||
if (!worldPoint.worldPositionX || !worldPoint.worldPositionX) return
|
||||
|
||||
waypoint.value = {
|
||||
visible: true,
|
||||
x: worldPoint.worldPositionX,
|
||||
y: worldPoint.worldPositionY + config.tile_size.height + 15
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragMap(pointer: Phaser.Input.Pointer) {
|
||||
if (!gameStore.game.isPlayerDraggingCamera) return
|
||||
|
||||
const deltaX = pointer.x - pointerStartPosition.value.x
|
||||
const deltaY = pointer.y - pointerStartPosition.value.y
|
||||
|
||||
if (Math.abs(deltaX) <= dragThreshold && Math.abs(deltaY) <= dragThreshold) return
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
function startDragging(pointer: Phaser.Input.Pointer) {
|
||||
pointerStartPosition.value = { x: pointer.x, y: pointer.y }
|
||||
gameStore.setPlayerDraggingCamera(true)
|
||||
}
|
||||
|
||||
function stopDragging() {
|
||||
gameStore.setPlayerDraggingCamera(false)
|
||||
}
|
||||
|
||||
function handleZoom(pointer: Phaser.Input.Pointer) {
|
||||
if (pointer.event instanceof WheelEvent && pointer.event.shiftKey) {
|
||||
const deltaY = pointer.event.deltaY
|
||||
const zoomLevel = camera.zoom - deltaY * 0.005
|
||||
if (zoomLevel > 0 && zoomLevel < 3) {
|
||||
camera.setZoom(zoomLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
updateWaypoint,
|
||||
handleDragMap,
|
||||
startDragging,
|
||||
stopDragging,
|
||||
handleZoom,
|
||||
pointerStartPosition
|
||||
}
|
||||
}
|
114
src/composables/controls/useGameControlsComposable.ts
Normal file
114
src/composables/controls/useGameControlsComposable.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { getTile } from '@/composables/mapComposable'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
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()
|
||||
const baseHandlers = useBaseControlsComposable(scene, layer, waypoint, camera)
|
||||
|
||||
function handlePointerDown(pointer: Phaser.Input.Pointer) {
|
||||
baseHandlers.startDragging(pointer)
|
||||
}
|
||||
|
||||
function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
baseHandlers.updateWaypoint(pointer.worldX, pointer.worldY)
|
||||
baseHandlers.handleDragMap(pointer)
|
||||
}
|
||||
|
||||
function handlePointerUp(pointer: Phaser.Input.Pointer) {
|
||||
baseHandlers.stopDragging()
|
||||
|
||||
const pointerTile = getTile(layer, pointer.worldX, pointer.worldY)
|
||||
if (!pointerTile) return
|
||||
|
||||
gameStore.connection?.emit('map:character:move', {
|
||||
positionX: pointerTile.x,
|
||||
positionY: pointerTile.y
|
||||
})
|
||||
}
|
||||
|
||||
const pressedKeys = new Set<string>()
|
||||
let moveInterval: number | null = null
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (!gameStore.character) return
|
||||
|
||||
// console.log(event.key)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Attack on CTRL
|
||||
if (event.key === 'Control') {
|
||||
gameStore.connection?.emit('map:character:attack')
|
||||
}
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
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('keydown', handleKeyDown)
|
||||
scene.input.keyboard!.on('keyup', handleKeyUp)
|
||||
}
|
||||
|
||||
const cleanupControls = () => {
|
||||
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
|
||||
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('keydown', handleKeyDown)
|
||||
scene.input.keyboard!.off('keyup', handleKeyUp)
|
||||
}
|
||||
|
||||
return { setupControls, cleanupControls }
|
||||
}
|
42
src/composables/controls/useMapEditorControlsComposable.ts
Normal file
42
src/composables/controls/useMapEditorControlsComposable.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
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()
|
||||
const baseHandlers = useBaseControlsComposable(scene, layer, waypoint, camera)
|
||||
const isMoveTool = computed(() => mapEditor.tool.value === 'move')
|
||||
|
||||
function handlePointerDown(pointer: Phaser.Input.Pointer) {
|
||||
if (isMoveTool.value || pointer.event.shiftKey) {
|
||||
baseHandlers.startDragging(pointer)
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
if (isMoveTool.value || pointer.event.shiftKey) {
|
||||
baseHandlers.handleDragMap(pointer)
|
||||
}
|
||||
baseHandlers.updateWaypoint(pointer.worldX, pointer.worldY)
|
||||
}
|
||||
|
||||
function handlePointerUp(pointer: Phaser.Input.Pointer) {
|
||||
baseHandlers.stopDragging()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
const cleanupControls = () => {
|
||||
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
|
||||
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)
|
||||
}
|
||||
|
||||
return { setupControls, cleanupControls }
|
||||
}
|
@ -59,7 +59,6 @@ export async function loadTexture(scene: Phaser.Scene, textureData: TextureData)
|
||||
export async function loadSpriteTextures(scene: Phaser.Scene, sprite_id: string) {
|
||||
if (!sprite_id) return false
|
||||
|
||||
// @TODO: Fix this
|
||||
const spriteStorage = new SpriteStorage()
|
||||
const sprite = await spriteStorage.get(sprite_id)
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
import config from '@/application/config'
|
||||
import { getTile, tileToWorldXY } from '@/composables/mapComposable'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { ref, type Ref } from 'vue'
|
||||
|
||||
export function useGamePointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
const gameStore = useGameStore()
|
||||
const pointerStartPosition = ref({ x: 0, y: 0 })
|
||||
const dragThreshold = 5 // pixels
|
||||
|
||||
function updateWaypoint(worldX: number, worldY: number) {
|
||||
const pointerTile = getTile(layer, worldX, worldY)
|
||||
if (!pointerTile) {
|
||||
waypoint.value.visible = false
|
||||
return
|
||||
}
|
||||
const worldPoint = tileToWorldXY(layer, pointerTile.x, pointerTile.y)
|
||||
if (!worldPoint.worldPositionX || !worldPoint.worldPositionX) return
|
||||
|
||||
waypoint.value = {
|
||||
visible: true,
|
||||
x: worldPoint.worldPositionX,
|
||||
y: worldPoint.worldPositionY + config.tile_size.height + 15
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerDown(pointer: Phaser.Input.Pointer) {
|
||||
pointerStartPosition.value = pointer.position
|
||||
gameStore.setPlayerDraggingCamera(true)
|
||||
}
|
||||
|
||||
function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
updateWaypoint(pointer.worldX, pointer.worldY)
|
||||
|
||||
if (!gameStore.game.isPlayerDraggingCamera) return
|
||||
|
||||
// If the distance is less than the drag threshold, return
|
||||
// We do this to prevent the camera from scrolling too quickly
|
||||
const distance = Phaser.Math.Distance.Between(pointerStartPosition.value.x, pointerStartPosition.value.y, pointer.x, pointer.y)
|
||||
if (distance <= dragThreshold) return
|
||||
|
||||
camera.setScroll(camera.scrollX - (pointer.x - pointer.prevPosition.x) / camera.zoom, camera.scrollY - (pointer.y - pointer.prevPosition.y) / camera.zoom)
|
||||
}
|
||||
|
||||
function handlePointerUp(pointer: Phaser.Input.Pointer) {
|
||||
gameStore.setPlayerDraggingCamera(false)
|
||||
|
||||
const pointerTile = getTile(layer, pointer.worldX, pointer.worldY)
|
||||
if (!pointerTile) return
|
||||
|
||||
gameStore.connection?.emit('map:character:move', {
|
||||
positionX: pointerTile.x,
|
||||
positionY: pointerTile.y
|
||||
})
|
||||
}
|
||||
|
||||
const setupPointerHandlers = () => {
|
||||
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)
|
||||
}
|
||||
|
||||
const cleanupPointerHandlers = () => {
|
||||
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
|
||||
scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
|
||||
scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp)
|
||||
}
|
||||
|
||||
return { setupPointerHandlers, cleanupPointerHandlers }
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import config from '@/application/config'
|
||||
import { getTile, tileToWorldXY } from '@/composables/mapComposable'
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { computed, ref, type Ref } from 'vue'
|
||||
|
||||
export function useMapEditorPointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
const gameStore = useGameStore()
|
||||
const mapEditor = useMapEditorComposable()
|
||||
const pointerStartPosition = ref({ x: 0, y: 0 })
|
||||
const dragThreshold = 5 // pixels
|
||||
|
||||
function updateWaypoint(worldX: number, worldY: number) {
|
||||
const pointerTile = getTile(layer, worldX, worldY)
|
||||
if (!pointerTile) {
|
||||
waypoint.value.visible = false
|
||||
return
|
||||
}
|
||||
const worldPoint = tileToWorldXY(layer, pointerTile.x, pointerTile.y)
|
||||
if (!worldPoint.worldPositionX || !worldPoint.worldPositionX) return
|
||||
|
||||
waypoint.value = {
|
||||
visible: true,
|
||||
x: worldPoint.worldPositionX,
|
||||
y: worldPoint.worldPositionY + config.tile_size.height + 15
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerDown(pointer: Phaser.Input.Pointer) {
|
||||
pointerStartPosition.value = { x: pointer.x, y: pointer.y }
|
||||
if (mapEditor.tool.value === 'move' || pointer.event.shiftKey) {
|
||||
gameStore.setPlayerDraggingCamera(true)
|
||||
}
|
||||
}
|
||||
|
||||
function dragMap(pointer: Phaser.Input.Pointer) {
|
||||
if (!gameStore.game.isPlayerDraggingCamera) return
|
||||
|
||||
const distance = Phaser.Math.Distance.Between(pointerStartPosition.value.x, pointerStartPosition.value.y, pointer.x, pointer.y)
|
||||
|
||||
// If the distance is less than the drag threshold, return
|
||||
// We do this to prevent the camera from scrolling too quickly
|
||||
|
||||
if (distance <= dragThreshold) return
|
||||
|
||||
camera.setScroll(camera.scrollX - (pointer.x - pointer.prevPosition.x) / camera.zoom, camera.scrollY - (pointer.y - pointer.prevPosition.y) / camera.zoom)
|
||||
}
|
||||
|
||||
function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
if (mapEditor.tool.value === 'move' || pointer.event.shiftKey) {
|
||||
dragMap(pointer)
|
||||
}
|
||||
updateWaypoint(pointer.worldX, pointer.worldY)
|
||||
}
|
||||
|
||||
function handlePointerUp(pointer: Phaser.Input.Pointer) {
|
||||
gameStore.setPlayerDraggingCamera(false)
|
||||
}
|
||||
|
||||
function handleZoom(pointer: Phaser.Input.Pointer) {
|
||||
if (pointer.event instanceof WheelEvent && pointer.event.shiftKey) {
|
||||
const deltaY = pointer.event.deltaY
|
||||
const zoomLevel = camera.zoom - deltaY * 0.005
|
||||
if (zoomLevel > 0 && zoomLevel < 3) {
|
||||
camera.setZoom(zoomLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setupPointerHandlers = () => {
|
||||
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, handleZoom)
|
||||
}
|
||||
|
||||
const cleanupPointerHandlers = () => {
|
||||
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
|
||||
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, handleZoom)
|
||||
}
|
||||
|
||||
return { setupPointerHandlers, cleanupPointerHandlers }
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useMapStore } from '@/stores/mapStore'
|
||||
|
||||
export function useCameraControls(scene: Phaser.Scene) {
|
||||
const gameStore = useGameStore()
|
||||
const camera = scene.cameras.main
|
||||
|
||||
const onPointerDown = () => gameStore.setPlayerDraggingCamera(true)
|
||||
const onPointerUp = () => gameStore.setPlayerDraggingCamera(false)
|
||||
|
||||
scene.input.on(Phaser.Input.Events.POINTER_DOWN, onPointerDown)
|
||||
scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerUp)
|
||||
|
||||
return { camera }
|
||||
}
|
136
src/composables/useCharacterSpriteComposable.ts
Normal file
136
src/composables/useCharacterSpriteComposable.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { Direction } from '@/application/enums'
|
||||
import { type MapCharacter } from '@/application/types'
|
||||
import { loadSpriteTextures } from '@/composables/gameComposable'
|
||||
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/mapComposable'
|
||||
import { CharacterTypeStorage } from '@/storage/storages'
|
||||
import { refObj } from 'phavuer'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export function useCharacterSprite(scene: Phaser.Scene, tilemap: Phaser.Tilemaps.Tilemap, mapCharacter: MapCharacter) {
|
||||
const characterContainer = refObj<Phaser.GameObjects.Container>()
|
||||
const characterSpriteId = ref('')
|
||||
const characterSprite = refObj<Phaser.GameObjects.Sprite>()
|
||||
const currentPositionX = ref(0)
|
||||
const currentPositionY = ref(0)
|
||||
const isometricDepth = ref(1)
|
||||
const isInitialPosition = ref(true)
|
||||
const tween = ref<Phaser.Tweens.Tween | null>(null)
|
||||
|
||||
const updateIsometricDepth = (positionX: number, positionY: number) => {
|
||||
isometricDepth.value = calculateIsometricDepth(positionX, positionY, 28, 94, true)
|
||||
}
|
||||
|
||||
const updatePosition = (positionX: number, positionY: number, direction: Direction) => {
|
||||
const newPositionX = tileToWorldX(tilemap, positionX, positionY)
|
||||
const newPositionY = tileToWorldY(tilemap, positionX, positionY)
|
||||
|
||||
if (isInitialPosition.value) {
|
||||
currentPositionX.value = newPositionX
|
||||
currentPositionY.value = newPositionY
|
||||
isInitialPosition.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (tween.value?.isPlaying()) {
|
||||
tween.value.stop()
|
||||
}
|
||||
|
||||
const distance = Math.sqrt(Math.pow(newPositionX - currentPositionX.value, 2) + Math.pow(newPositionY - currentPositionY.value, 2))
|
||||
const baseSpeed = 150 // pixels per second
|
||||
const duration = (distance / baseSpeed) * 1000 // Convert to milliseconds
|
||||
|
||||
tween.value = tilemap.scene.tweens.add({
|
||||
targets: characterContainer.value,
|
||||
x: newPositionX,
|
||||
y: newPositionY,
|
||||
duration,
|
||||
ease: 'Linear',
|
||||
onStart: () => {
|
||||
if (direction === Direction.POSITIVE) {
|
||||
updateIsometricDepth(positionX, positionY)
|
||||
}
|
||||
},
|
||||
onUpdate: () => {
|
||||
currentPositionX.value = characterContainer.value?.x ?? currentPositionX.value
|
||||
currentPositionY.value = characterContainer.value?.y ?? currentPositionY.value
|
||||
},
|
||||
onComplete: () => {
|
||||
if (direction === Direction.NEGATIVE) {
|
||||
updateIsometricDepth(positionX, positionY)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const calcDirection = (oldPositionX: number, oldPositionY: number, newPositionX: number, newPositionY: number): Direction => {
|
||||
if (newPositionY < oldPositionY || newPositionX < oldPositionX) return Direction.NEGATIVE
|
||||
if (newPositionX > oldPositionX || newPositionY > oldPositionY) return Direction.POSITIVE
|
||||
return Direction.UNCHANGED
|
||||
}
|
||||
|
||||
const isFlippedX = computed(() => [6, 4].includes(mapCharacter.character.rotation ?? 0))
|
||||
|
||||
const currentDirection = computed(() => {
|
||||
return [0, 6].includes(mapCharacter.character.rotation ?? 0) ? 'left_up' : 'right_down'
|
||||
})
|
||||
|
||||
const currentAction = computed(() => {
|
||||
return mapCharacter.isMoving ? 'walk' : 'idle'
|
||||
})
|
||||
|
||||
const charTexture = computed(() => {
|
||||
const spriteId = characterSpriteId.value ?? 'idle_right_down'
|
||||
return `${spriteId}-${currentAction.value}_${currentDirection.value}`
|
||||
})
|
||||
|
||||
const updateSprite = () => {
|
||||
if (!characterSprite.value) return
|
||||
|
||||
if (mapCharacter.isMoving) {
|
||||
characterSprite.value.anims.play(charTexture.value, true)
|
||||
} else {
|
||||
characterSprite.value.anims.stop()
|
||||
characterSprite.value.setFrame(0)
|
||||
characterSprite.value.setTexture(charTexture.value)
|
||||
}
|
||||
}
|
||||
|
||||
const initializeSprite = async () => {
|
||||
const characterTypeStorage = new CharacterTypeStorage()
|
||||
const spriteId = await characterTypeStorage.getSpriteId(mapCharacter.character.characterType!)
|
||||
if (!spriteId) return
|
||||
|
||||
characterSpriteId.value = spriteId
|
||||
await loadSpriteTextures(scene, spriteId)
|
||||
|
||||
if (characterContainer.value) {
|
||||
characterContainer.value.setName(mapCharacter.character.name)
|
||||
}
|
||||
|
||||
if (characterSprite.value) {
|
||||
characterSprite.value.setTexture(charTexture.value)
|
||||
characterSprite.value.setFlipX(isFlippedX.value)
|
||||
}
|
||||
|
||||
updatePosition(mapCharacter.character.positionX, mapCharacter.character.positionY, mapCharacter.character.rotation)
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
tween.value?.stop()
|
||||
}
|
||||
|
||||
return {
|
||||
characterContainer,
|
||||
characterSpriteId,
|
||||
characterSprite,
|
||||
currentPositionX,
|
||||
currentPositionY,
|
||||
isometricDepth,
|
||||
isFlippedX,
|
||||
updatePosition,
|
||||
calcDirection,
|
||||
updateSprite,
|
||||
initializeSprite,
|
||||
cleanup
|
||||
}
|
||||
}
|
18
src/composables/useControlsComposable.ts
Normal file
18
src/composables/useControlsComposable.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { useGameControlsComposable } from '@/composables/controls/useGameControlsComposable'
|
||||
import { useMapEditorControlsComposable } from '@/composables/controls/useMapEditorControlsComposable'
|
||||
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 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()
|
||||
const cleanupControls = () => currentHandlers.value.cleanupControls()
|
||||
|
||||
return { setupControls, cleanupControls, camera }
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
import { computed, watch, type Ref } from 'vue'
|
||||
import { useGamePointerHandlers } from './pointerHandlers/useGamePointerHandlers'
|
||||
import { useMapEditorPointerHandlers } from './pointerHandlers/useMapEditorPointerHandlers'
|
||||
|
||||
export function usePointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
const mapEditor = useMapEditorComposable()
|
||||
const gameHandlers = useGamePointerHandlers(scene, layer, waypoint, camera)
|
||||
const mapEditorHandlers = useMapEditorPointerHandlers(scene, layer, waypoint, camera)
|
||||
|
||||
const currentHandlers = computed(() => (mapEditor.active.value ? mapEditorHandlers : gameHandlers))
|
||||
|
||||
const setupPointerHandlers = () => currentHandlers.value.setupPointerHandlers()
|
||||
const cleanupPointerHandlers = () => currentHandlers.value.cleanupPointerHandlers()
|
||||
|
||||
watch(
|
||||
() => mapEditor.active.value,
|
||||
() => {
|
||||
cleanupPointerHandlers()
|
||||
setupPointerHandlers()
|
||||
}
|
||||
)
|
||||
|
||||
return { setupPointerHandlers, cleanupPointerHandlers }
|
||||
}
|
98
src/composables/useTileProcessingComposable.ts
Normal file
98
src/composables/useTileProcessingComposable.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import config from '@/application/config'
|
||||
import type { Tile } from '@/application/types'
|
||||
import type { TileAnalysisResult, TileWorkerMessage } from '@/types/tileTypes'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// Constants for image processing
|
||||
const DOWNSCALE_WIDTH = 32
|
||||
const DOWNSCALE_HEIGHT = 16
|
||||
const COLOR_SIMILARITY_THRESHOLD = 30
|
||||
const EDGE_SIMILARITY_THRESHOLD = 20
|
||||
const BATCH_SIZE = 4
|
||||
|
||||
export function useTileProcessingComposable() {
|
||||
const tileAnalysisCache = ref<Map<string, { color: { r: number; g: number; b: number }; edge: number; namePrefix: string }>>(new Map())
|
||||
const processingQueue = ref<Tile[]>([])
|
||||
let isProcessing = false
|
||||
const worker = new Worker(new URL('@/workers/tileAnalyzerWorker.ts', import.meta.url), { type: 'module' })
|
||||
|
||||
worker.onmessage = (e: MessageEvent<TileAnalysisResult>) => {
|
||||
const { tileId, color, edge, namePrefix } = e.data
|
||||
tileAnalysisCache.value.set(tileId, { color, edge, namePrefix })
|
||||
isProcessing = false
|
||||
processBatch()
|
||||
}
|
||||
|
||||
async function processTileAsync(tile: Tile): Promise<void> {
|
||||
if (tileAnalysisCache.value.has(tile.id)) return
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image()
|
||||
img.crossOrigin = 'Anonymous'
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
canvas.width = DOWNSCALE_WIDTH
|
||||
canvas.height = DOWNSCALE_HEIGHT
|
||||
ctx.drawImage(img, 0, 0, DOWNSCALE_WIDTH, DOWNSCALE_HEIGHT)
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, DOWNSCALE_WIDTH, DOWNSCALE_HEIGHT)
|
||||
const message: TileWorkerMessage = {
|
||||
imageData,
|
||||
tileId: tile.id,
|
||||
tileName: tile.name
|
||||
}
|
||||
worker.postMessage(message)
|
||||
resolve()
|
||||
}
|
||||
img.onerror = () => resolve()
|
||||
img.src = `${config.server_endpoint}/textures/tiles/${tile.id}.png`
|
||||
})
|
||||
}
|
||||
|
||||
function processBatch() {
|
||||
if (isProcessing || processingQueue.value.length === 0) return
|
||||
isProcessing = true
|
||||
|
||||
const batch = processingQueue.value.splice(0, BATCH_SIZE)
|
||||
Promise.all(batch.map((tile) => processTileAsync(tile))).then(() => {
|
||||
isProcessing = false
|
||||
if (processingQueue.value.length > 0) {
|
||||
setTimeout(processBatch, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function processTile(tile: Tile) {
|
||||
if (!processingQueue.value.includes(tile)) {
|
||||
processingQueue.value.push(tile)
|
||||
processBatch()
|
||||
}
|
||||
}
|
||||
|
||||
function areTilesRelated(tile1: Tile, tile2: Tile): boolean {
|
||||
const data1 = tileAnalysisCache.value.get(tile1.id)
|
||||
const data2 = tileAnalysisCache.value.get(tile2.id)
|
||||
|
||||
if (!data1 || !data2) return false
|
||||
|
||||
const colorDifference = Math.sqrt(Math.pow(data1.color.r - data2.color.r, 2) + Math.pow(data1.color.g - data2.color.g, 2) + Math.pow(data1.color.b - data2.color.b, 2))
|
||||
|
||||
return colorDifference <= COLOR_SIMILARITY_THRESHOLD && Math.abs(data1.edge - data2.edge) <= EDGE_SIMILARITY_THRESHOLD && data1.namePrefix === data2.namePrefix
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
worker.terminate()
|
||||
}
|
||||
|
||||
return {
|
||||
processTile,
|
||||
areTilesRelated,
|
||||
cleanup
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user