1
0
forked from noxious/client

207 lines
6.4 KiB
Vue

<template>
<MapTiles ref="mapTiles" @createCommand="addCommand" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer />
<PlacedMapObjects ref="mapObjects" @createCommand="addCommand" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer />
<MapEventTiles ref="eventTiles" @createCommand="addCommand" v-if="tileMap" :tileMap />
</template>
<script setup lang="ts">
import MapEventTiles from '@/components/gameMaster/mapEditor/mapPartials/MapEventTiles.vue'
import MapTiles from '@/components/gameMaster/mapEditor/mapPartials/MapTiles.vue'
import PlacedMapObjects from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { cloneArray, createTileLayer, createTileMap, placeTiles } from '@/services/mapService'
import { TileStorage } from '@/storage/storages'
import { useScene } from 'phavuer'
import { onBeforeUnmount, onMounted, onUnmounted, ref, shallowRef, useTemplateRef, watch } from 'vue'
import type { MapEventTile, PlacedMapObject as PlacedMapObjectT } from '@/application/types'
const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>()
const tileMapLayer = shallowRef<Phaser.Tilemaps.TilemapLayer>()
const mapEditor = useMapEditorComposable()
const scene = useScene()
const mapTiles = useTemplateRef('mapTiles')
const mapObjects = useTemplateRef('mapObjects')
const eventTiles = useTemplateRef('eventTiles')
//Record of commands
let commandStack: EditorCommand[] = []
let commandIndex = ref(0)
let originTiles: string[][] = []
let originEventTiles: MapEventTile[] = []
let originObjects: PlacedMapObjectT[] = []
//Command Pattern basic interface, extended to store what elements have been changed by each edit
export interface EditorCommand {
apply: (elements: any[]) => any[]
type: 'tile' | 'map_object' | 'event_tile'
operation: 'draw' | 'erase' | 'place' | 'move' | 'delete' | 'rotate'
}
function applyCommands(tiles: any[], ...commands: EditorCommand[]): any[] {
let tileVersion = cloneArray(tiles)
for (let command of commands) {
tileVersion = command.apply(tileVersion)
}
return tileVersion
}
watch(() => commandIndex.value!, (val) => {
if (val !== undefined) {
update(commandStack.slice(0, val))
}
})
function update(commands: EditorCommand[]) {
if (!mapEditor.currentMap.value) return
const tileCommands = commands.filter((command) => command.type === 'tile')
const eventTileCommands = commands.filter((command) => command.type === 'event_tile')
const objectCommands = commands.filter((command) => command.type === 'map_object')
let modifiedTiles = applyCommands(originTiles, ...tileCommands)
placeTiles(tileMap.value!, tileMapLayer.value!, modifiedTiles)
mapEditor.currentMap.value.tiles = modifiedTiles
mapEditor.currentMap.value.mapEventTiles = applyCommands(originEventTiles, ...eventTileCommands)
mapEditor.currentMap.value.placedMapObjects = applyCommands(originObjects, ...objectCommands)
}
function addCommand(command: EditorCommand) {
commandStack = commandStack.slice(0, commandIndex.value)
commandStack.push(command)
if (commandStack.length >= 9) {
switch (commandStack[0].type) {
case 'tile':
originTiles = commandStack.shift()?.apply(originTiles) as string[][]
break
case 'map_object':
originObjects = commandStack.shift()?.apply(originObjects) as PlacedMapObjectT[]
break
case 'event_tile':
originEventTiles = commandStack.shift()?.apply(originEventTiles) as MapEventTile[]
break
}
}
commandIndex.value = commandStack.length
}
function undo() {
if (commandIndex.value > 0) {
commandIndex.value--
}
}
function redo() {
if (commandIndex.value <= 9 && commandIndex.value <= commandStack.length) {
commandIndex.value++
}
}
function handlePointerDown(pointer: Phaser.Input.Pointer) {
if (!mapTiles.value || !mapObjects.value || !eventTiles.value) return
// Check if left mouse button is pressed
if (!pointer.isDown) return
// Check if shift is not pressed, this means we are moving the camera
if (pointer.event.shiftKey) return
// Check if draw mode is tile
switch (mapEditor.drawMode.value) {
case 'tile':
mapTiles.value.handlePointer(pointer)
break
case 'map_object':
mapObjects.value.handlePointer(pointer)
break
case 'teleport':
eventTiles.value.handlePointer(pointer)
break
case 'blocking tile':
eventTiles.value.handlePointer(pointer)
break
}
}
function handleKeyDown(event: KeyboardEvent) {
//CTRL+Y
if (event.key === 'y' && event.ctrlKey) {
redo()
}
//CTRL+Z
if (event.key === 'z' && event.ctrlKey) {
undo()
}
}
function handlePointerMove(pointer: Phaser.Input.Pointer) {
if (mapEditor.inputMode.value === 'hold' && pointer.isDown) {
handlePointerDown(pointer)
}
}
function handlePointerUp(pointer: Phaser.Input.Pointer) {
switch(mapEditor.drawMode.value) {
case 'tile':
mapTiles.value!.finalizeCommand()
break
case 'map_object':
mapObjects.value!.finalizeCommand()
break
case 'teleport':
eventTiles.value!.finalizeCommand()
break
case 'blocking tile':
eventTiles.value!.finalizeCommand()
break
}
}
onMounted(async () => {
let mapValue = mapEditor.currentMap.value
if (!mapValue) return
//Clone
originTiles = cloneArray(mapValue.tiles)
originObjects = cloneArray(mapValue.placedMapObjects)
originEventTiles = cloneArray(mapValue.mapEventTiles)
const tileStorage = new TileStorage()
const allTiles = await tileStorage.getAll()
const allTileIds = allTiles.map((tile) => tile.id)
tileMap.value = createTileMap(scene, mapValue)
tileMapLayer.value = createTileLayer(tileMap.value, allTileIds)
addEventListener('keydown', handleKeyDown)
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)
})
onUnmounted(() => {
if (tileMap.value) {
tileMap.value.destroyLayer('tiles')
tileMap.value.removeAllLayers()
tileMap.value.destroy()
}
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)
mapEditor.reset()
})
onBeforeUnmount(() => {
removeEventListener('keydown', handleKeyDown)
})
</script>