1
0
forked from noxious/client
2024-09-29 21:50:27 +02:00

320 lines
12 KiB
Vue

<template>
<Toolbar :layer="tiles" @eraser="eraser" @pencil="pencil" @paint="paint" @clear="clear" @save="save" />
<ZoneList v-if="zoneEditorStore.isZoneListModalShown" />
<template v-if="zoneEditorStore.zone">
<Controls :layer="tiles as TilemapLayer" />
<Tiles />
<Objects />
<ZoneSettings />
<TeleportModal v-if="shouldShowTeleportModal" />
<Container :depth="2">
<Image v-for="object in zoneObjects" :depth="calculateIsometricDepth(object.positionX, object.positionY, 0)" :key="object.id" v-bind="getObjectImageProps(object)" @pointerup="() => setSelectedZoneObject(object)" />
</Container>
<Container :depth="3">
<Image v-for="tile in zoneEventTiles" :key="tile.id" v-bind="getEventTileImageProps(tile)" />
</Container>
<SelectedZoneObject v-if="zoneEditorStore.selectedZoneObject" @update_depth="updateZoneObjectDepth" @delete="deleteZoneObject" @move="handleMove" />
</template>
</template>
<script setup lang="ts">
import { computed, onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue'
import { Container, Image, useScene } from 'phavuer'
import { storeToRefs } from 'pinia'
import { useGameStore } from '@/stores/gameStore'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { calculateIsometricDepth, loadAssets, placeTile, setAllTiles, sortByIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
import { ZoneEventTileType, type ZoneObject, type ZoneEventTile, type Zone } from '@/types'
import { uuidv4 } from '@/utilities'
import config from '@/config'
// Components
import Controls from '@/components/utilities/Controls.vue'
import Toolbar from '@/components/gameMaster/zoneEditor/partials/Toolbar.vue'
import Tiles from '@/components/gameMaster/zoneEditor/partials/TileList.vue'
import SelectedZoneObject from '@/components/gameMaster/zoneEditor/partials/SelectedZoneObject.vue'
import ZoneSettings from '@/components/gameMaster/zoneEditor/partials/ZoneSettings.vue'
import Objects from '@/components/gameMaster/zoneEditor/partials/ObjectList.vue'
import ZoneList from '@/components/gameMaster/zoneEditor/partials/ZoneList.vue'
import TeleportModal from '@/components/gameMaster/zoneEditor/partials/TeleportModal.vue'
import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
/**
* @TODO:
* Clean all the code in this file
*/
const scene = useScene()
const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore()
const { objectList, zone, selectedTile, selectedObject, selectedZoneObject, eraserMode, drawMode } = storeToRefs(zoneEditorStore)
const zoneTilemap = createTilemap()
const tiles = createTileLayer()
const zoneObjects = ref<ZoneObject[]>([])
const zoneEventTiles = ref<ZoneEventTile[]>([])
let tileArray = createTileArray()
const shouldShowTeleportModal = computed(() => zoneEditorStore.tool === 'pencil' && drawMode.value === 'teleport')
function createTilemap() {
const zoneData = new Phaser.Tilemaps.MapData({
width: zone.value?.width ?? 10,
height: zone.value?.height ?? 10,
tileWidth: config.tile_size.x,
tileHeight: config.tile_size.y,
orientation: Phaser.Tilemaps.Orientation.ISOMETRIC,
format: Phaser.Tilemaps.Formats.ARRAY_2D
})
const tilemap = new Phaser.Tilemaps.Tilemap(scene, zoneData)
return tilemap
}
function createTileLayer() {
const tilesetImages = gameStore.assets.filter((asset) => asset.group === 'tiles').map((asset, index) => zoneTilemap.addTilesetImage(asset.key, asset.key, config.tile_size.x, config.tile_size.y, 1, 2, index + 1, { x: 0, y: -config.tile_size.y }))
tilesetImages.push(zoneTilemap.addTilesetImage('blank_tile', 'blank_tile', config.tile_size.x, config.tile_size.y, 1, 2, 0, { x: 0, y: -config.tile_size.y }))
const layer = zoneTilemap.createBlankLayer('tiles', tilesetImages as any, 0, config.tile_size.y) as Phaser.Tilemaps.TilemapLayer
layer.setDepth(0)
return layer
}
function createTileArray() {
return Array.from({ length: zoneTilemap.height || 0 }, () => Array.from({ length: zoneTilemap.width || 0 }, () => 'blank_tile'))
}
function getObjectImageProps(object: ZoneObject) {
return {
tint: selectedZoneObject.value?.id === object.id ? 0x00ff00 : 0xffffff,
x: tileToWorldX(zoneTilemap as any, object.positionX, object.positionY),
y: tileToWorldY(zoneTilemap as any, object.positionX, object.positionY),
texture: object.object.id,
originY: Number(object.object.originX),
originX: Number(object.object.originY)
}
}
function getEventTileImageProps(tile: ZoneEventTile) {
return {
x: tileToWorldX(zoneTilemap as any, tile.positionX, tile.positionY),
y: tileToWorldY(zoneTilemap as any, tile.positionX, tile.positionY),
texture: tile.type
}
}
function eraser(tile: Phaser.Tilemaps.Tile) {
if (eraserMode.value === 'tile') {
placeTile(zoneTilemap as Tilemap, tiles as TilemapLayer, tile.x, tile.y, 'blank_tile')
tileArray[tile.y][tile.x] = 'blank_tile'
} else if (eraserMode.value === 'object') {
zoneObjects.value = zoneObjects.value.filter((object) => object.positionX !== tile.x || object.positionY !== tile.y)
} else if (eraserMode.value === 'blocking tile' || eraserMode.value === 'teleport') {
zoneEventTiles.value = zoneEventTiles.value.filter((eventTile) => eventTile.positionX !== tile.x || eventTile.positionY !== tile.y || (eraserMode.value === 'teleport' && eventTile.type !== ZoneEventTileType.TELEPORT))
}
}
function pencil(tile: Phaser.Tilemaps.Tile) {
if (drawMode.value === 'tile' && selectedTile.value) {
placeTile(zoneTilemap as Tilemap, tiles as TilemapLayer, tile.x, tile.y, selectedTile.value.id)
tileArray[tile.y][tile.x] = selectedTile.value.id
} else if (drawMode.value === 'object' && selectedObject.value) {
addZoneObject(tile)
} else if (drawMode.value === 'blocking tile' || drawMode.value === 'teleport') {
addZoneEventTile(tile)
}
}
function addZoneObject(tile: Phaser.Tilemaps.Tile) {
// Check if object already exists on position
const existingObject = zoneObjects.value.find((object) => object.positionX === tile.x && object.positionY === tile.y)
if (existingObject) return
const newObject = {
id: uuidv4(),
zoneId: zone.value!.id,
zone: zone.value!,
objectId: selectedObject.value!.id,
object: selectedObject.value!,
depth: 0,
positionX: tile.x,
positionY: tile.y
}
// Add new object to zoneObjects
zoneObjects.value = zoneObjects.value.concat(newObject)
}
function addZoneEventTile(tile: Phaser.Tilemaps.Tile) {
// Check if event tile already exists on position
const existingEventTile = zoneEventTiles.value.find((eventTile) => eventTile.positionX === tile.x && eventTile.positionY === tile.y)
if (existingEventTile) return
const newEventTile = {
id: uuidv4(),
zoneId: zone.value!.id,
zone: zone.value!,
type: drawMode.value === 'blocking tile' ? ZoneEventTileType.BLOCK : ZoneEventTileType.TELEPORT,
positionX: tile.x,
positionY: tile.y,
teleport:
drawMode.value === 'teleport'
? {
toZoneId: zoneEditorStore.teleportSettings.toZoneId,
toPositionX: zoneEditorStore.teleportSettings.toPositionX,
toPositionY: zoneEditorStore.teleportSettings.toPositionY,
toRotation: zoneEditorStore.teleportSettings.toRotation
}
: undefined
}
zoneEventTiles.value = zoneEventTiles.value.concat(newEventTile as any)
}
function paint() {
if (!selectedTile.value) return
// Ensure tileArray is initialized with correct dimensions
if (!tileArray || tileArray.length !== zoneTilemap.height) {
tileArray = Array.from({ length: zoneTilemap.height }, () => Array.from({ length: zoneTilemap.width }, () => 'blank_tile'))
}
// Set all tiles in the tilemap to the selected tile's id
for (let y = 0; y < zoneTilemap.height; y++) {
if (!tileArray[y]) {
tileArray[y] = Array(zoneTilemap.width).fill('blank_tile')
}
for (let x = 0; x < zoneTilemap.width; x++) {
placeTile(zoneTilemap as Tilemap, tiles as TilemapLayer, x, y, selectedTile.value.id)
tileArray[y][x] = selectedTile.value.id
}
}
}
function save() {
if (!zone.value) return
const data = {
zoneId: zone.value.id,
name: zoneEditorStore.zoneSettings.name,
width: zoneEditorStore.zoneSettings.width,
height: zoneEditorStore.zoneSettings.height,
tiles: tileArray,
pvp: zone.value.pvp,
zoneEventTiles: zoneEventTiles.value.map(({ id, zoneId, type, positionX, positionY, teleport }) => ({ id, zoneId, type, positionX, positionY, teleport })),
zoneObjects: zoneObjects.value.map(({ id, zoneId, objectId, depth, positionX, positionY }) => ({ id, zoneId, objectId, depth, positionX, positionY }))
}
if (zoneEditorStore.isSettingsModalShown) {
zoneEditorStore.toggleSettingsModal()
}
gameStore.connection?.emit('gm:zone_editor:zone:update', data, (response: Zone) => {
console.log('zone updated')
zoneEditorStore.setZone(response)
})
}
function clear() {
for (let y = 0; y < zoneTilemap.height; y++) {
if (!tileArray[y]) {
tileArray[y] = Array(zoneTilemap.width).fill('blank_tile')
}
for (let x = 0; x < zoneTilemap.width; x++) {
placeTile(zoneTilemap as Tilemap, tiles as TilemapLayer, x, y, 'blank_tile')
tileArray[y][x] = 'blank_tile'
}
}
zoneEventTiles.value = []
zoneObjects.value = []
}
function updateZoneObjectDepth(depth: number) {
zoneObjects.value = zoneObjects.value.map((object) => (object.id === selectedZoneObject.value?.id ? { ...object, depth } : object))
}
function deleteZoneObject(objectId: string) {
zoneObjects.value = zoneObjects.value.filter((object) => object.id !== objectId)
}
function handleMove() {
console.log('move btn clicked')
}
// watch zoneEditorStore.objectList and update originX and originY of objects in zoneObjects
watch(
objectList,
(newObjects) => {
zoneObjects.value = zoneObjects.value.map((zoneObject) => {
const updatedObject = newObjects.find((obj) => obj.id === zoneObject.objectId)
if (updatedObject) {
return {
...zoneObject,
object: {
...zoneObject.object,
originX: updatedObject.originX,
originY: updatedObject.originY
}
}
}
return zoneObject
})
// Update selectedObject if it exists
if (zoneEditorStore.selectedObject) {
const updatedObject = newObjects.find((obj) => obj.id === zoneEditorStore.selectedObject?.id)
if (updatedObject) {
zoneEditorStore.setSelectedObject({
...zoneEditorStore.selectedObject,
originX: updatedObject.originX,
originY: updatedObject.originY
})
}
}
},
{ deep: true }
)
const setSelectedZoneObject = (zoneObject: ZoneObject | null) => {
if (!zoneObject) return
if (zoneEditorStore.tool !== 'move') return
zoneEditorStore.setSelectedZoneObject(zoneObject)
}
onBeforeMount(async () => {
await gameStore.fetchAllZoneAssets()
await loadAssets(scene)
tileArray.forEach((row, y) => row.forEach((_, x) => placeTile(zoneTilemap, tiles, x, y, 'blank_tile')))
if (zone.value?.tiles) {
setAllTiles(zoneTilemap, tiles, zone.value.tiles)
tileArray = zone.value.tiles.map((row) => row.map((tileId) => tileId || 'blank_tile'))
}
zoneEventTiles.value = zone.value?.zoneEventTiles ?? []
zoneObjects.value = sortByIsometricDepth(zone.value?.zoneObjects ?? [])
// Center camera
const centerY = (zoneTilemap.height * zoneTilemap.tileHeight) / 2
const centerX = (zoneTilemap.width * zoneTilemap.tileWidth) / 2
scene.cameras.main.centerOn(centerX, centerY)
})
onUnmounted(() => {
zoneEventTiles.value = []
zoneObjects.value = []
tiles?.destroy()
zoneTilemap?.removeAllLayers()
zoneTilemap?.destroy()
zoneEditorStore.reset()
})
</script>