import config from '@/application/config' import { Direction } from '@/application/enums' import { type MapCharacter } from '@/application/types' import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/services/mapService' import { loadSpriteTextures } from '@/services/textureService' import { CharacterTypeStorage } from '@/storage/storages' import { refObj } from 'phavuer' import { computed, ref } from 'vue' export function useCharacterSpriteComposable(scene: Phaser.Scene, tilemap: Phaser.Tilemaps.Tilemap, mapCharacter: MapCharacter) { const characterContainer = refObj() const characterSpriteId = ref('') const characterSprite = refObj() const currentPositionX = ref(0) const currentPositionY = ref(0) const isometricDepth = ref(1) const isInitialPosition = ref(true) const tween = ref(null) const updateIsometricDepth = (positionX: number, positionY: number) => { isometricDepth.value = calculateIsometricDepth(positionX, positionY, 30, 95, true) } const updatePosition = (positionX: number, positionY: number) => { 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 tileDistance = Math.sqrt(Math.pow((newPositionX - currentPositionX.value) / config.tile_size.width, 2) + Math.pow((newPositionY - currentPositionY.value) / config.tile_size.height, 2)) const baseDuration = 300 // milliseconds per tile const duration = Math.min(baseDuration * tileDistance, baseDuration) tween.value = tilemap.scene.tweens.add({ targets: characterContainer.value, x: newPositionX, y: newPositionY, duration, ease: 'Linear', onStart: () => { updateIsometricDepth(positionX, positionY) }, onUpdate: () => { updateIsometricDepth(positionX, positionY) currentPositionX.value = characterContainer.value?.x ?? currentPositionX.value currentPositionY.value = characterContainer.value?.y ?? currentPositionY.value }, onComplete: () => { updateIsometricDepth(positionX, positionY) } }) } const playAnimation = (animation: string, loop = false, ignoreIfPlaying = true) => { if (!characterSprite.value || !characterSpriteId.value) return const fullAnimationName = `${characterSpriteId.value}-${animation}_${currentDirection.value}` // Remove any existing animation complete listeners characterSprite.value.off(Phaser.Animations.Events.ANIMATION_COMPLETE) // Add new listener characterSprite.value.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => { characterSprite.value!.setFrame(0) characterSprite.value!.setTexture(charTexture.value) }) characterSprite.value.anims.play( { key: fullAnimationName, repeat: loop ? -1 : 0 }, ignoreIfPlaying ) } 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) } const cleanup = () => { tween.value?.stop() } return { characterContainer, characterSpriteId, characterSprite, currentPositionX, currentPositionY, isometricDepth, isFlippedX, updatePosition, playAnimation, calcDirection, updateSprite, initializeSprite, cleanup } }