forked from noxious/client
Teleport modal restored, and expanded undo/redo to include all placed and erase edits across each map element type (map object advanced actions WIP)
This commit is contained in:
parent
f258c65403
commit
ca307d4de3
@ -1,30 +1,109 @@
|
||||
<template>
|
||||
<MapTiles ref="mapTiles" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer />
|
||||
<PlacedMapObjects ref="mapObjects" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer />
|
||||
<MapEventTiles ref="eventTiles" v-if="tileMap" :tileMap />
|
||||
<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 { createTileLayer, createTileMap } from '@/services/mapService'
|
||||
import { cloneArray, createTileLayer, createTileMap, placeTiles } from '@/services/mapService'
|
||||
import { TileStorage } from '@/storage/storages'
|
||||
import { useScene } from 'phavuer'
|
||||
import { onBeforeUnmount, onMounted, onUnmounted, shallowRef, useTemplateRef } from 'vue'
|
||||
|
||||
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
|
||||
|
||||
@ -54,12 +133,12 @@ function handlePointerDown(pointer: Phaser.Input.Pointer) {
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
//CTRL+Y
|
||||
if (event.key === 'y' && event.ctrlKey) {
|
||||
mapTiles.value!.redo()
|
||||
redo()
|
||||
}
|
||||
|
||||
//CTRL+Z
|
||||
if (event.key === 'z' && event.ctrlKey) {
|
||||
mapTiles.value!.undo()
|
||||
undo()
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,8 +149,19 @@ function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
}
|
||||
|
||||
function handlePointerUp(pointer: Phaser.Input.Pointer) {
|
||||
if (mapEditor.drawMode.value === 'tile') {
|
||||
mapTiles.value?.finalizeCommand()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +169,11 @@ 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)
|
||||
|
@ -3,22 +3,72 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { MapEventTileType, type MapEventTile, type Map as MapT, type UUID } from '@/application/types'
|
||||
import { uuidv4 } from '@/application/utilities'
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
import { getTile, tileToWorldX, tileToWorldY } from '@/services/mapService'
|
||||
import { cloneArray, getTile, tileToWorldX, tileToWorldY } from '@/services/mapService'
|
||||
import { Image } from 'phavuer'
|
||||
import { shallowRef } from 'vue'
|
||||
import { type EditorCommand } from '@/components/gameMaster/mapEditor/Map.vue'
|
||||
|
||||
const mapEditor = useMapEditorComposable()
|
||||
|
||||
defineExpose({ handlePointer })
|
||||
defineExpose({ handlePointer, finalizeCommand })
|
||||
|
||||
const emit = defineEmits(['createCommand'])
|
||||
|
||||
const props = defineProps<{
|
||||
tileMap: Phaser.Tilemaps.Tilemap
|
||||
}>()
|
||||
|
||||
const tileLayer = shallowRef<Phaser.Tilemaps.TilemapLayer>()
|
||||
|
||||
// *** COMMAND STATE ***
|
||||
|
||||
let currentCommand: EventTileCommand | null = null
|
||||
|
||||
class EventTileCommand implements EditorCommand {
|
||||
public operation: 'draw' | 'erase' = 'draw'
|
||||
public type: 'event_tile' = 'event_tile'
|
||||
public affectedTiles: MapEventTile[] = []
|
||||
|
||||
apply(elements: MapEventTile[]) {
|
||||
let tileVersion = cloneArray(elements) as MapEventTile[]
|
||||
if (this.operation === 'draw') {
|
||||
tileVersion = tileVersion.concat(this.affectedTiles)
|
||||
}
|
||||
else if (this.operation === 'erase') {
|
||||
tileVersion = tileVersion.filter((v) => !this.affectedTiles.includes(v))
|
||||
}
|
||||
return tileVersion
|
||||
}
|
||||
|
||||
constructor(operation: 'draw' | 'erase') {
|
||||
this.operation = operation
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandUpdate(tile: MapEventTile, operation: 'draw' | 'erase') {
|
||||
if (!currentCommand) {
|
||||
currentCommand = new EventTileCommand(operation)
|
||||
}
|
||||
|
||||
//If position is already in, do not proceed
|
||||
for (const priorTile of currentCommand.affectedTiles) {
|
||||
if (priorTile.positionX === tile.positionX && priorTile.positionY == tile.positionY) return
|
||||
}
|
||||
|
||||
currentCommand.affectedTiles.push(tile)
|
||||
}
|
||||
|
||||
function finalizeCommand() {
|
||||
if (!currentCommand) return
|
||||
emit('createCommand', currentCommand)
|
||||
currentCommand = null
|
||||
}
|
||||
|
||||
|
||||
// *** HANDLERS ***
|
||||
|
||||
|
||||
function getImageProps(tile: MapEventTile) {
|
||||
return {
|
||||
@ -44,7 +94,7 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
const newEventTile = {
|
||||
id: uuidv4() as UUID,
|
||||
mapId: map.id,
|
||||
map: map.id,
|
||||
map: map,
|
||||
type: mapEditor.drawMode.value === 'blocking tile' ? MapEventTileType.BLOCK : MapEventTileType.TELEPORT,
|
||||
positionX: tile.x,
|
||||
positionY: tile.y,
|
||||
@ -59,6 +109,8 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
: undefined
|
||||
}
|
||||
|
||||
createCommandUpdate(newEventTile, 'draw')
|
||||
|
||||
map.mapEventTiles.push(newEventTile)
|
||||
}
|
||||
|
||||
@ -76,6 +128,8 @@ function erase(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
else return;
|
||||
}
|
||||
|
||||
createCommandUpdate(existingEventTile, 'erase')
|
||||
|
||||
// Remove existing event tile
|
||||
map.mapEventTiles = map.mapEventTiles.filter((eventTile) => eventTile.id !== existingEventTile.id)
|
||||
}
|
||||
|
@ -5,57 +5,70 @@
|
||||
<script setup lang="ts">
|
||||
import Controls from '@/components/utilities/Controls.vue'
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
import { createTileArray, getTile, placeTile, placeTiles } from '@/services/mapService'
|
||||
import { cloneArray, createTileArray, getTile, placeTile, placeTiles } from '@/services/mapService'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { type EditorCommand } from '@/components/gameMaster/mapEditor/Map.vue'
|
||||
|
||||
const mapEditor = useMapEditorComposable()
|
||||
|
||||
defineExpose({ handlePointer, finalizeCommand, undo, redo })
|
||||
defineExpose({ handlePointer, finalizeCommand })
|
||||
|
||||
const emit = defineEmits(['createCommand'])
|
||||
|
||||
const props = defineProps<{
|
||||
tileMap: Phaser.Tilemaps.Tilemap
|
||||
tileMapLayer: Phaser.Tilemaps.TilemapLayer
|
||||
}>()
|
||||
|
||||
class EditorCommand {
|
||||
|
||||
// *** COMMAND STATE ***
|
||||
|
||||
let currentCommand: TileCommand | null = null
|
||||
|
||||
class TileCommand implements EditorCommand {
|
||||
public operation: 'draw' | 'erase' = 'draw'
|
||||
public type: 'tile' = 'tile'
|
||||
public tileName: string = 'blank_tile'
|
||||
public affectedTiles: number[][]
|
||||
public affectedTiles: number[][] = []
|
||||
|
||||
apply(elements: string[][]) {
|
||||
let tileVersion = cloneArray(elements) as string[][]
|
||||
for (const position of this.affectedTiles) {
|
||||
tileVersion[position[1]][position[0]] = this.tileName
|
||||
}
|
||||
return tileVersion
|
||||
}
|
||||
|
||||
constructor(operation: 'draw' | 'erase', tileName: string) {
|
||||
this.operation = operation
|
||||
this.tileName = tileName
|
||||
this.affectedTiles = []
|
||||
}
|
||||
}
|
||||
|
||||
//Record of commands
|
||||
let commandStack: EditorCommand[] = []
|
||||
let currentCommand: EditorCommand | null = null
|
||||
let commandIndex = ref(0)
|
||||
let originTiles: string[][] = []
|
||||
function createCommandUpdate(x: number, y: number, tileName: string, operation: 'draw' | 'erase') {
|
||||
if (!currentCommand) {
|
||||
currentCommand = new TileCommand(operation, tileName)
|
||||
}
|
||||
|
||||
function pencil(pointer: Phaser.Input.Pointer) {
|
||||
let map = mapEditor.currentMap.value
|
||||
if (!map) return
|
||||
//If position is already in, do not proceed
|
||||
for (const vec of currentCommand.affectedTiles) {
|
||||
if (vec[0] === x && vec[1] === y) return
|
||||
}
|
||||
|
||||
// Check if there is a selected tile
|
||||
if (!mapEditor.selectedTile.value) return
|
||||
|
||||
// Check if there is a tile
|
||||
const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
|
||||
if (!tile) return
|
||||
|
||||
// Place tile
|
||||
placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, mapEditor.selectedTile.value)
|
||||
|
||||
createCommandUpdate(tile.x, tile.y, mapEditor.selectedTile.value, 'draw')
|
||||
|
||||
// Adjust mapEditorStore.map.tiles
|
||||
map.tiles[tile.y][tile.x] = mapEditor.selectedTile.value
|
||||
currentCommand.affectedTiles.push([x, y])
|
||||
}
|
||||
|
||||
function eraser(pointer: Phaser.Input.Pointer) {
|
||||
function finalizeCommand() {
|
||||
if (!currentCommand) return
|
||||
emit('createCommand', currentCommand)
|
||||
currentCommand = null
|
||||
}
|
||||
|
||||
|
||||
// *** HANDLERS ***
|
||||
|
||||
|
||||
function draw(pointer: Phaser.Input.Pointer, tileName: string) {
|
||||
let map = mapEditor.currentMap.value
|
||||
if (!map) return
|
||||
|
||||
@ -64,12 +77,12 @@ function eraser(pointer: Phaser.Input.Pointer) {
|
||||
if (!tile) return
|
||||
|
||||
// Place tile
|
||||
placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, 'blank_tile')
|
||||
placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, tileName)
|
||||
|
||||
createCommandUpdate(tile.x, tile.y, 'blank_tile', 'erase')
|
||||
createCommandUpdate(tile.x, tile.y, tileName, tileName === 'blank_tile' ? 'erase': 'draw')
|
||||
|
||||
// Adjust mapEditorStore.map.tiles
|
||||
map.tiles[tile.y][tile.x] = 'blank_tile'
|
||||
map.tiles[tile.y][tile.x] = tileName
|
||||
}
|
||||
|
||||
function paint(pointer: Phaser.Input.Pointer) {
|
||||
@ -113,10 +126,10 @@ function handlePointer(pointer: Phaser.Input.Pointer) {
|
||||
// Check if draw mode is tile
|
||||
switch (mapEditor.tool.value) {
|
||||
case 'pencil':
|
||||
pencil(pointer)
|
||||
draw(pointer, mapEditor.selectedTile.value!)
|
||||
break
|
||||
case 'eraser':
|
||||
eraser(pointer)
|
||||
draw(pointer, 'blank_tile')
|
||||
break
|
||||
case 'paint':
|
||||
paint(pointer)
|
||||
@ -124,70 +137,9 @@ function handlePointer(pointer: Phaser.Input.Pointer) {
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandUpdate(x: number, y: number, tileName: string, operation: 'draw' | 'erase') {
|
||||
if (!currentCommand) {
|
||||
currentCommand = new EditorCommand(operation, tileName)
|
||||
}
|
||||
|
||||
//If position is already in, do not proceed
|
||||
for (const vec of currentCommand.affectedTiles) {
|
||||
if (vec[0] === x && vec[1] === y) return
|
||||
}
|
||||
// *** LIFECYCLE ***
|
||||
|
||||
currentCommand.affectedTiles.push([x, y])
|
||||
}
|
||||
|
||||
function finalizeCommand() {
|
||||
if (!currentCommand) return
|
||||
//Cut the stack so the current edit is the last
|
||||
commandStack = commandStack.slice(0, commandIndex.value)
|
||||
commandStack.push(currentCommand)
|
||||
if (commandStack.length >= 9) {
|
||||
originTiles = applyCommands(originTiles, commandStack.shift()!)
|
||||
}
|
||||
|
||||
commandIndex.value = commandStack.length
|
||||
currentCommand = null
|
||||
}
|
||||
|
||||
function undo() {
|
||||
if (commandIndex.value > 0) {
|
||||
commandIndex.value--
|
||||
updateMapTiles()
|
||||
}
|
||||
}
|
||||
|
||||
function redo() {
|
||||
if (commandIndex.value <= 9 && commandIndex.value <= commandStack.length) {
|
||||
commandIndex.value++
|
||||
updateMapTiles()
|
||||
}
|
||||
}
|
||||
|
||||
function applyCommands(tiles: string[][], ...commands: EditorCommand[]): string[][] {
|
||||
let tileVersion = cloneArray(tiles)
|
||||
for (let command of commands) {
|
||||
for (const position of command.affectedTiles) {
|
||||
tileVersion[position[1]][position[0]] = command.tileName
|
||||
}
|
||||
}
|
||||
return tileVersion
|
||||
}
|
||||
|
||||
function updateMapTiles() {
|
||||
if (!mapEditor.currentMap.value) return
|
||||
|
||||
let indexedCommands = commandStack.slice(0, commandIndex.value)
|
||||
let modifiedTiles = applyCommands(originTiles, ...indexedCommands)
|
||||
|
||||
placeTiles(props.tileMap, props.tileMapLayer, modifiedTiles)
|
||||
mapEditor.currentMap.value.tiles = modifiedTiles
|
||||
}
|
||||
|
||||
//Recursive Array Clone
|
||||
function cloneArray(arr: any[]): any[] {
|
||||
return arr.map((item) => (item instanceof Array ? cloneArray(item) : item))
|
||||
}
|
||||
|
||||
watch(
|
||||
() => mapEditor.shouldClearTiles.value,
|
||||
@ -205,9 +157,6 @@ onMounted(async () => {
|
||||
if (!mapEditor.currentMap.value) return
|
||||
const mapState = mapEditor.currentMap.value
|
||||
|
||||
//Clone
|
||||
originTiles = cloneArray(mapState.tiles)
|
||||
|
||||
placeTiles(props.tileMap, props.tileMapLayer, mapState.tiles)
|
||||
})
|
||||
</script>
|
||||
|
@ -4,29 +4,98 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { MapObject, Map as MapT, PlacedMapObject as PlacedMapObjectT, UUID } from '@/application/types'
|
||||
import type {
|
||||
MapObject,
|
||||
Map as MapT,
|
||||
PlacedMapObject as PlacedMapObjectT,
|
||||
UUID,
|
||||
MapEventTile
|
||||
} from '@/application/types'
|
||||
import { uuidv4 } from '@/application/utilities'
|
||||
import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue'
|
||||
import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue'
|
||||
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
||||
import { getTile } from '@/services/mapService'
|
||||
import { cloneArray, getTile } from '@/services/mapService'
|
||||
import { useScene } from 'phavuer'
|
||||
|
||||
import Tilemap = Phaser.Tilemaps.Tilemap
|
||||
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
|
||||
import { computed } from 'vue'
|
||||
import { type EditorCommand } from '@/components/gameMaster/mapEditor/Map.vue'
|
||||
import Vector2 = Phaser.Math.Vector2
|
||||
import Tile = Phaser.Tilemaps.Tile
|
||||
|
||||
const scene = useScene()
|
||||
const mapEditor = useMapEditorComposable()
|
||||
const map = computed(() => mapEditor.currentMap.value)
|
||||
const map = computed(() => mapEditor.currentMap.value!)
|
||||
|
||||
defineExpose({ handlePointer })
|
||||
defineExpose({ handlePointer, finalizeCommand })
|
||||
|
||||
const emit = defineEmits(['createCommand'])
|
||||
|
||||
const props = defineProps<{
|
||||
tileMap: Tilemap
|
||||
tileMapLayer: TilemapLayer
|
||||
}>()
|
||||
|
||||
|
||||
// *** COMMAND STATE ***
|
||||
|
||||
let currentCommand: MapObjectCommand | null = null
|
||||
|
||||
class MapObjectCommand implements EditorCommand {
|
||||
public operation: 'place' | 'move' | 'delete' | 'rotate' = 'place'
|
||||
public type: 'map_object' = 'map_object'
|
||||
public affectedTiles: PlacedMapObjectT[] = []
|
||||
public targetPosition?: Phaser.Math.Vector2
|
||||
|
||||
apply(elements: PlacedMapObjectT[]) {
|
||||
let tileVersion = cloneArray(elements) as PlacedMapObjectT[]
|
||||
if (this.operation === 'place') {
|
||||
tileVersion = tileVersion.concat(this.affectedTiles)
|
||||
}
|
||||
else if (this.operation === 'delete') {
|
||||
tileVersion = tileVersion.filter((v) => !this.affectedTiles.includes(v))
|
||||
}
|
||||
else if (this.operation === 'move') {
|
||||
const targetObject = tileVersion.find((v) => this.affectedTiles[0].id === v.id)
|
||||
if (targetObject) {
|
||||
targetObject.positionX = this.targetPosition!.x
|
||||
targetObject.positionY = this.targetPosition!.y
|
||||
}
|
||||
}
|
||||
|
||||
return tileVersion
|
||||
}
|
||||
|
||||
constructor(operation: 'place' | 'move' | 'delete' | 'rotate') {
|
||||
this.operation = operation
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandUpdate(object: PlacedMapObjectT, operation: 'place' | 'move' | 'delete' | 'rotate', targetPosition?: Phaser.Math.Vector2) {
|
||||
if (!currentCommand) {
|
||||
currentCommand = new MapObjectCommand(operation)
|
||||
}
|
||||
else {
|
||||
if (targetPosition) {
|
||||
currentCommand.targetPosition = targetPosition
|
||||
}
|
||||
}
|
||||
|
||||
currentCommand.affectedTiles.push(object)
|
||||
}
|
||||
|
||||
function finalizeCommand() {
|
||||
if (!currentCommand) return
|
||||
emit('createCommand', currentCommand)
|
||||
currentCommand = null
|
||||
}
|
||||
|
||||
|
||||
// *** HANDLERS ***
|
||||
|
||||
|
||||
function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
|
||||
if (!tile) return
|
||||
@ -47,6 +116,9 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
|
||||
// Add new object to mapObjects
|
||||
map.placedMapObjects.push(newPlacedMapObject)
|
||||
|
||||
createCommandUpdate(newPlacedMapObject, 'place')
|
||||
|
||||
mapEditor.selectedPlacedObject.value = newPlacedMapObject
|
||||
}
|
||||
|
||||
@ -55,6 +127,8 @@ function eraser(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
const existingPlacedMapObject = findObjectByPointer(pointer, map)
|
||||
if (!existingPlacedMapObject) return
|
||||
|
||||
createCommandUpdate(existingPlacedMapObject, 'delete')
|
||||
|
||||
// Remove existing object
|
||||
map.placedMapObjects = map.placedMapObjects.filter((placedMapObject) => placedMapObject.id !== existingPlacedMapObject.id)
|
||||
}
|
||||
@ -78,11 +152,15 @@ function objectPicker(pointer: Phaser.Input.Pointer, map: MapT) {
|
||||
function moveMapObject(id: string, map: MapT) {
|
||||
mapEditor.movingPlacedObject.value = map.placedMapObjects.find((object) => object.id === id) as PlacedMapObjectT
|
||||
|
||||
let t: Tile
|
||||
|
||||
function handlePointerMove(pointer: Phaser.Input.Pointer) {
|
||||
if (!mapEditor.movingPlacedObject.value) return
|
||||
const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
|
||||
if (!tile) return
|
||||
|
||||
t = tile
|
||||
|
||||
mapEditor.movingPlacedObject.value.positionX = tile.x
|
||||
mapEditor.movingPlacedObject.value.positionY = tile.y
|
||||
}
|
||||
@ -92,6 +170,9 @@ function moveMapObject(id: string, map: MapT) {
|
||||
function handlePointerUp() {
|
||||
scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
|
||||
mapEditor.movingPlacedObject.value = null
|
||||
|
||||
createCommandUpdate(mapEditor.movingPlacedObject.value!, 'move', new Vector2(t.x, t.y))
|
||||
finalizeCommand()
|
||||
}
|
||||
|
||||
scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp)
|
||||
@ -104,6 +185,13 @@ function rotatePlacedMapObject(id: string, map: MapT) {
|
||||
|
||||
function deletePlacedMapObject(id: string, map: MapT) {
|
||||
let mapE = mapEditor.currentMap.value!
|
||||
|
||||
const foundObject = mapE.placedMapObjects.find((obj) => obj.id === id)
|
||||
if (!foundObject) return
|
||||
|
||||
createCommandUpdate(foundObject, 'delete')
|
||||
finalizeCommand()
|
||||
|
||||
mapE.placedMapObjects = map.placedMapObjects.filter((object) => object.id !== id)
|
||||
mapEditor.selectedPlacedObject.value = null
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const mapEditor = useMapEditorComposable()
|
||||
|
||||
const emit = defineEmits(['save', 'clear', 'open-maps', 'open-settings', 'close-editor', 'open-tile-list', 'open-map-object-list', 'close-lists'])
|
||||
const emit = defineEmits(['save', 'clear', 'open-maps', 'open-settings', 'open-teleport-settings', 'close-editor', 'open-tile-list', 'open-map-object-list', 'close-lists'])
|
||||
|
||||
// track when clicked outside of toolbar items
|
||||
const toolbar = ref(null)
|
||||
@ -115,6 +115,7 @@ function setDrawMode(value: string) {
|
||||
emit('close-lists')
|
||||
if (value === 'tile') emit('open-tile-list')
|
||||
if (value === 'map_object') emit('open-map-object-list')
|
||||
if (value === 'teleport') emit('open-teleport-settings')
|
||||
}
|
||||
|
||||
mapEditor.setDrawMode(value)
|
||||
@ -155,6 +156,7 @@ function handleClick(tool: string) {
|
||||
|
||||
selectPencilOpen.value = tool === 'pencil' ? !selectPencilOpen.value : false
|
||||
selectEraserOpen.value = tool === 'eraser' ? !selectEraserOpen.value : false
|
||||
|
||||
}
|
||||
|
||||
function cycleToolMode(tool: 'pencil' | 'eraser') {
|
||||
|
@ -11,6 +11,7 @@
|
||||
@clear="clear"
|
||||
@open-maps="mapModal?.open"
|
||||
@open-settings="mapSettingsModal?.open"
|
||||
@open-teleport-settings="teleportModal?.open"
|
||||
@close-editor="mapEditor.toggleActive"
|
||||
@close-lists="tileList?.close"
|
||||
@closeLists="objectList?.close"
|
||||
@ -45,6 +46,7 @@ import { MapStorage } from '@/storage/storages'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { Game, Scene } from 'phavuer'
|
||||
import { ref, useTemplateRef } from 'vue'
|
||||
import teleportModal from '@/components/gameMaster/mapEditor/partials/TeleportModal.vue'
|
||||
|
||||
const mapStorage = new MapStorage()
|
||||
const mapEditor = useMapEditorComposable()
|
||||
@ -54,6 +56,7 @@ const mapModal = useTemplateRef('mapModal')
|
||||
const tileList = useTemplateRef('tileList')
|
||||
const objectList = useTemplateRef('objectList')
|
||||
const mapSettingsModal = useTemplateRef('mapSettingsModal')
|
||||
const teleportSettings = useTemplateRef('teleportModal')
|
||||
|
||||
const isLoaded = ref(false)
|
||||
|
||||
@ -96,14 +99,8 @@ function save() {
|
||||
if (!currentMap) return
|
||||
|
||||
const data = {
|
||||
...currentMap,
|
||||
mapId: currentMap.id,
|
||||
name: currentMap.name,
|
||||
width: currentMap.width,
|
||||
height: currentMap.height,
|
||||
tiles: currentMap.tiles,
|
||||
pvp: currentMap.pvp,
|
||||
mapEffects: currentMap.mapEffects,
|
||||
mapEventTiles: currentMap.mapEventTiles,
|
||||
placedMapObjects: currentMap.placedMapObjects.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject, depth, isRotated, positionX, positionY })) ?? []
|
||||
}
|
||||
|
||||
|
@ -147,3 +147,8 @@ export function createTileLayer(tileMap: Phaser.Tilemaps.Tilemap, tilesArray: st
|
||||
|
||||
return layer
|
||||
}
|
||||
|
||||
//Recursive Array Clone
|
||||
export function cloneArray(arr: any[]): any[] {
|
||||
return arr.map((item) => (item instanceof Array ? cloneArray(item) : item))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user