1
0
forked from noxious/client

Started refactoring zone editor

This commit is contained in:
Dennis Postma 2024-10-17 02:32:39 +02:00
parent 2fad54fd26
commit 3765cfe5e9
13 changed files with 245 additions and 427 deletions

24
package-lock.json generated
View File

@ -1991,9 +1991,9 @@
}
},
"node_modules/@types/node": {
"version": "20.16.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz",
"integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==",
"version": "20.16.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz",
"integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -3642,9 +3642,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.39",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.39.tgz",
"integrity": "sha512-4xkpSR6CjuiaNyvwiWDI85N9AxsvbPawB8xc7yzLPonYTuP19BVgYweKyUMFtHEZgIcHWMt1ks5Cqx2m+6/Grg==",
"version": "1.5.40",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.40.tgz",
"integrity": "sha512-LYm78o6if4zTasnYclgQzxEcgMoIcybWOhkATWepN95uwVVWV0/IW10v+2sIeHE+bIYWipLneTftVyQm45UY7g==",
"dev": true,
"license": "ISC"
},
@ -5798,9 +5798,9 @@
}
},
"node_modules/picocolors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
@ -6409,9 +6409,9 @@
"license": "MIT"
},
"node_modules/sass": {
"version": "1.79.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz",
"integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==",
"version": "1.79.6",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.79.6.tgz",
"integrity": "sha512-PVVjeeiUGx6Nj4PtEE/ecwu8ltwfPKzHxbbVmmLj4l1FYHhOyfA0scuVF8sVaa+b+VY4z7BVKjKq0cPUQdUU3g==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@ -12,6 +12,11 @@ import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore()
// See if there's a dat
const sceneRef = ref<Phaser.Scene | null>(null)
// Effect-related refs

View File

@ -0,0 +1,18 @@
<template>
</template>
<script setup lang="ts">
import type { ZoneEventTile } from '@/types'
import { tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
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
}
}
</script>

View File

@ -0,0 +1,31 @@
<template>
<SelectedZoneObject v-if="zoneEditorStore.selectedZoneObject" />
<Image v-for="object in zoneEditorStore.zone?.zoneObjects" :key="object.id" v-bind="getObjectImageProps(object)" />
</template>
<script setup lang="ts">
import { tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
import { Image } from 'phavuer'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import type { ZoneObject } from '@/types'
import { ZoneEventTileType } from '@/types'
import SelectedZoneObject from '@/components/gameMaster/zoneEditor/partials/SelectedZoneObject.vue'
const zoneEditorStore = useZoneEditorStore()
const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
}>()
function getObjectImageProps(object: ZoneObject) {
return {
// alpha: object.id === movingZoneObject.value?.id ? .5 : 1,
tint: zoneEditorStore.selectedZoneObject?.id === object.id ? 0x00ff00 : 0xffffff,
x: tileToWorldX(props.tilemap as any, object.positionX, object.positionY),
y: tileToWorldY(props.tilemap as any, object.positionX, object.positionY),
texture: object.object.id,
originY: Number(object.object.originX),
originX: Number(object.object.originY)
}
}
</script>

View File

@ -0,0 +1,95 @@
<template>
<Controls :layer="tiles" :depth="0" />
</template>
<script setup lang="ts">
import config from '@/config'
import { useScene } from 'phavuer'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { onBeforeMount, onBeforeUnmount } from 'vue'
import { getTile, placeTile, setAllTiles } from '@/composables/zoneComposable'
import Controls from '@/components/utilities/Controls.vue'
import { z } from 'zod'
import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
const emit = defineEmits(['tilemap:create'])
const zoneEditorStore = useZoneEditorStore()
const scene = useScene()
const zoneTilemap = createTilemap()
const tiles = createTileLayer()
let tileArray = createTileArray()
function createTilemap() {
const zoneData = new Phaser.Tilemaps.MapData({
width: zoneEditorStore.zone?.width,
height: zoneEditorStore.zone?.height,
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)
emit('tilemap:create', tilemap)
return tilemap
}
function createTileLayer() {
const tilesFromZone = zoneEditorStore.zone?.tiles || []
const uniqueTiles = new Set(tilesFromZone.flat().filter(Boolean))
const tilesetImages = Array.from(uniqueTiles).map((tile, index) => {
return zoneTilemap.addTilesetImage(tile, tile, config.tile_size.x, config.tile_size.y, 1, 2, index + 1, { x: 0, y: -config.tile_size.y })
}) as any
// Add blank tile
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, 0, config.tile_size.y) as Phaser.Tilemaps.TilemapLayer
layer.setDepth(0)
layer.setCullPadding(2, 2)
return layer
}
function createTileArray() {
return Array.from({ length: zoneTilemap.height || 0 }, () => Array.from({ length: zoneTilemap.width || 0 }, () => 'blank_tile'))
}
function handleTileClick(pointer: Phaser.Input.Pointer) {
// Check if tool is pencil
if (zoneEditorStore.tool !== 'pencil') return
// Check if there is a tile
const tile = getTile(pointer.worldX, pointer.worldY, tiles)
if (!tile) return
if (!zoneEditorStore.selectedTile) {
return
}
console.log('yolo')
placeTile(zoneTilemap as Tilemap, tiles as TilemapLayer, tile.x, tile.y, zoneEditorStore.selectedTile.id)
}
onBeforeMount(() => {
if (!zoneEditorStore.zone?.tiles) {
return
}
setAllTiles(zoneTilemap, tiles, zoneEditorStore.zone.tiles)
tileArray = zoneEditorStore.zone.tiles.map((row) => row.map((tileId) => tileId || 'blank_tile'))
scene.input.on(Phaser.Input.Events.POINTER_DOWN, handleTileClick)
})
onBeforeUnmount(() => {
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handleTileClick)
zoneTilemap.destroyLayer('tiles')
zoneTilemap.removeAllLayers()
zoneTilemap.destroy()
})
</script>

View File

@ -1,229 +1,56 @@
<template>
<Toolbar :layer="tiles" @eraser="eraser" @move="move" @pencil="pencil" @paint="paint" @clear="clear" @save="save" />
<ZoneList v-if="zoneEditorStore.isZoneListModalShown" />
<Tiles @tilemap:create="tileMap = $event" />
<Objects v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" />
<EventTiles v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" />
<template v-if="zoneEditorStore.zone">
<Controls :layer="tiles as TilemapLayer" />
<Toolbar @save="save" />
<Tiles />
<Objects />
<ZoneList />
<TileList />
<ObjectList />
<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)" :flipX="object.isRotated" />
</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" @rotate="handleRotate" />
</template>
<ZoneSettings />
<TeleportModal />
</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 { onBeforeMount, onUnmounted, ref } from 'vue'
import { useScene } from 'phavuer'
import { useGameStore } from '@/stores/gameStore'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import {
calculateIsometricDepth,
getTile,
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'
import { loadAssets } from '@/composables/zoneComposable'
import { type ZoneObject, type ZoneEventTile, type Zone } from '@/types'
// 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 TileList from '@/components/gameMaster/zoneEditor/partials/TileList.vue'
import ObjectList from '@/components/gameMaster/zoneEditor/partials/ObjectList.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
import InputManager = Phaser.Input.InputManager
import MouseManager = Phaser.Input.Mouse.MouseManager
/**
* @TODO:
* Clean all the code in this file
*/
import Tiles from '@/components/gameMaster/zoneEditor/Tiles.vue'
import Objects from '@/components/gameMaster/zoneEditor/Objects.vue'
import EventTiles from '@/components/gameMaster/zoneEditor/EventTiles.vue'
const scene = useScene()
const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore()
const { objectList, zone, selectedTile, selectedObject, selectedZoneObject, eraserMode, drawMode } = storeToRefs(zoneEditorStore)
const movingZoneObject = ref<ZoneObject|null>(null);
const zoneTilemap = createTilemap()
const tiles = createTileLayer()
const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null)
const tileArray = ref<string[][]>([])
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 {
alpha: object.id === movingZoneObject.value?.id ? .5 : 1,
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 move(_tile: Phaser.Tilemaps.Tile) {
movingZoneObject.value = null;
}
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,
isRotated: false,
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
if (!zoneEditorStore.zone) return
const data = {
zoneId: zone.value.id,
zoneId: zoneEditorStore.zone.id,
name: zoneEditorStore.zoneSettings.name,
width: zoneEditorStore.zoneSettings.width,
height: zoneEditorStore.zoneSettings.height,
tiles: tileArray,
pvp: zone.value.pvp,
pvp: zoneEditorStore.zone.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, isRotated, positionX, positionY }) => ({ id, zoneId, objectId, depth, isRotated, positionX, positionY }))
}
@ -233,130 +60,16 @@ function save() {
}
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(objectId: string) {
const object = zoneObjects.value.find((obj) => obj.id === objectId)
if (object) {
movingZoneObject.value = object;
}
}
function handleRotate(objectId: string) {
const object = zoneObjects.value.find((obj) => obj.id === objectId)
if (object) {
object.isRotated = !object.isRotated
}
}
// 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)
console.log('loaded assets')
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'))
function handlePointerMove(pointer: Phaser.Input.Pointer) {
const { x: px, y: py } = scene.cameras.main.getWorldPoint(pointer.x, pointer.y)
const pointerTile = getTile(px, py, zoneTilemap as any)
if (pointerTile) {
console.log(pointerTile.x, pointerTile.y)
if(movingZoneObject.value) {
movingZoneObject.value.positionX = pointerTile.x
movingZoneObject.value.positionY = pointerTile.y
}
}
}
setTimeout(() => {
scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
})
}
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>

View File

@ -1,9 +1,8 @@
<template>
<Modal :is-modal-open="true" @modal:close="() => zoneEditorStore.setTool('move')" :modal-width="300" :modal-height="350" :is-resizable="false">
<Modal :is-modal-open="showTeleportModal" @modal:close="() => zoneEditorStore.setTool('move')" :modal-width="300" :modal-height="350" :is-resizable="false">
<template #modalHeader>
<h3 class="m-0 font-medium shrink-0 text-white">Teleport settings</h3>
</template>
<template #modalBody>
<div class="m-4">
<form method="post" @submit.prevent="" class="inline">
@ -40,12 +39,13 @@
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { computed, onMounted, ref, watch } from 'vue'
import Modal from '@/components/utilities/Modal.vue'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { useGameStore } from '@/stores/gameStore'
import type { Zone } from '@/types'
const showTeleportModal = computed(() => zoneEditorStore.tool === 'pencil' && zoneEditorStore.drawMode === 'teleport')
const zoneEditorStore = useZoneEditorStore()
const gameStore = useGameStore()

View File

@ -1,7 +1,7 @@
<template>
<div class="flex justify-center p-5">
<div class="toolbar fixed bottom-0 left-0 m-3 rounded flex bg-gray solid border-solid border-2 border-gray-500 text-gray-300 p-1.5 px-3 h-10">
<div ref="clickOutsideElement" class="tools flex gap-2.5" v-if="zoneEditorStore.zone">
<div ref="toolbar" class="tools flex gap-2.5" v-if="zoneEditorStore.zone">
<button class="flex justify-center items-center min-w-10 p-0 relative" :class="{ 'border-0 border-b-[3px] border-solid border-cyan gap-2.5': zoneEditorStore.tool === 'move' }" @click="handleClick('move')">
<img class="invert w-5 h-5" src="/assets/icons/zoneEditor/move.svg" alt="Move camera" /> <span :class="{ 'ml-2.5': zoneEditorStore.tool !== 'move' }">(M)</span>
</button>
@ -83,22 +83,15 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { useScene } from 'phavuer'
import { getTile } from '@/composables/zoneComposable'
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { onClickOutside } from '@vueuse/core'
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
const zoneEditorStore = useZoneEditorStore()
const props = defineProps({
layer: Phaser.Tilemaps.TilemapLayer
})
const scene = useScene()
const emit = defineEmits(['move', 'eraser', 'pencil', 'paint', 'save', 'clear'])
// track when clicked outside of toolbar items
const clickOutsideElement = ref(null)
const toolbar = ref(null)
// track select state
let selectPencilOpen = ref(false)
@ -119,51 +112,6 @@ function setEraserMode(value: string) {
selectEraserOpen.value = false
}
function clickTile(pointer: Phaser.Input.Pointer) {
if (zoneEditorStore.tool !== 'eraser' && zoneEditorStore.tool !== 'move' && zoneEditorStore.tool !== 'pencil' && zoneEditorStore.tool !== 'paint') return
if (pointer.event.shiftKey) return
const px = scene.cameras.main.worldView.x + pointer.x
const py = scene.cameras.main.worldView.y + pointer.y
const pointer_tile = getTile(px, py, props.layer as TilemapLayer) as Phaser.Tilemaps.Tile
if (!pointer_tile) return
if (zoneEditorStore.tool === 'move') {
emit('move', pointer_tile)
}
if (zoneEditorStore.tool === 'eraser') {
emit('eraser', pointer_tile)
}
if (zoneEditorStore.tool === 'pencil') {
emit('pencil', pointer_tile)
}
if (zoneEditorStore.tool === 'paint') {
emit('paint', pointer_tile)
}
}
function drawTiles(pointer: Phaser.Input.Pointer) {
if (!pointer.isDown) return
clickTile(pointer)
}
scene.input.on(Phaser.Input.Events.POINTER_UP, clickTile)
scene.input.on(Phaser.Input.Events.POINTER_MOVE, drawTiles)
onMounted(() => {
addEventListener('keydown', initKeyShortcuts)
})
onBeforeUnmount(() => {
scene.input.off(Phaser.Input.Events.POINTER_UP, clickTile)
scene.input.off(Phaser.Input.Events.POINTER_MOVE, drawTiles)
removeEventListener('keydown', initKeyShortcuts)
})
function handleClick(tool: string) {
if (tool === 'settings') {
zoneEditorStore.toggleSettingsModal()
@ -212,10 +160,17 @@ function initKeyShortcuts(event: KeyboardEvent) {
}
}
onClickOutside(clickOutsideElement, handleClickOutside)
function handleClickOutside() {
selectPencilOpen.value = false
selectEraserOpen.value = false
}
onClickOutside(toolbar, handleClickOutside)
onMounted(() => {
addEventListener('keydown', initKeyShortcuts)
})
onBeforeUnmount(() => {
removeEventListener('keydown', initKeyShortcuts)
})
</script>

View File

@ -1,31 +1,28 @@
<template>
<CreateZone v-if="zoneEditorStore.isCreateZoneModalShown" />
<Teleport to="body">
<Modal @modal:close="() => zoneEditorStore.toggleZoneListModal()" :is-resizable="false" :is-modal-open="true" :modal-width="300" :modal-height="360">
<template #modalHeader>
<h3 class="text-lg text-white">Zones</h3>
</template>
<template #modalBody>
<div class="my-4 mx-auto">
<div class="text-center mb-4 px-2 flex gap-2.5">
<button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="fetchZones">Refresh</button>
<button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="() => zoneEditorStore.toggleCreateZoneModal()">New</button>
</div>
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap" v-for="(zone, index) in zoneEditorStore.zoneList" :key="zone.id">
<div class="absolute left-0 top-0 w-full h-px bg-gray-500" v-if="index === 0"></div>
<div class="flex gap-3 items-center w-full" @click="() => loadZone(zone.id)">
<span>{{ zone.name }}</span>
<span class="ml-auto gap-1 flex">
<button class="btn-red w-11 h-11 z-50" @click.stop="() => deleteZone(zone.id)">X</button>
</span>
</div>
<div class="absolute left-0 bottom-0 w-full h-px bg-gray-500"></div>
</div>
<Modal :is-modal-open="zoneEditorStore.isZoneListModalShown" @modal:close="() => zoneEditorStore.toggleZoneListModal()" :is-resizable="false" :modal-width="300" :modal-height="360">
<template #modalHeader>
<h3 class="text-lg text-white">Zones</h3>
</template>
<template #modalBody>
<div class="my-4 mx-auto">
<div class="text-center mb-4 px-2 flex gap-2.5">
<button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="fetchZones">Refresh</button>
<button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="() => zoneEditorStore.toggleCreateZoneModal()">New</button>
</div>
</template>
</Modal>
</Teleport>
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap" v-for="(zone, index) in zoneEditorStore.zoneList" :key="zone.id">
<div class="absolute left-0 top-0 w-full h-px bg-gray-500" v-if="index === 0"></div>
<div class="flex gap-3 items-center w-full" @click="() => loadZone(zone.id)">
<span>{{ zone.name }}</span>
<span class="ml-auto gap-1 flex">
<button class="btn-red w-11 h-11 z-50" @click.stop="() => deleteZone(zone.id)">X</button>
</span>
</div>
<div class="absolute left-0 bottom-0 w-full h-px bg-gray-500"></div>
</div>
</div>
</template>
</Modal>
</template>
<script setup lang="ts">

View File

@ -41,15 +41,15 @@ import { useZoneEditorStore } from '@/stores/zoneEditorStore'
const zoneEditorStore = useZoneEditorStore()
zoneEditorStore.setZoneName(zoneEditorStore.zone.name)
zoneEditorStore.setZoneWidth(zoneEditorStore.zone.width)
zoneEditorStore.setZoneHeight(zoneEditorStore.zone.height)
zoneEditorStore.setZonePvp(zoneEditorStore.zone.pvp)
zoneEditorStore.setZoneName(zoneEditorStore.zone?.name)
zoneEditorStore.setZoneWidth(zoneEditorStore.zone?.width)
zoneEditorStore.setZoneHeight(zoneEditorStore.zone?.height)
zoneEditorStore.setZonePvp(zoneEditorStore.zone?.pvp)
const name = ref(zoneEditorStore.zoneSettings.name)
const width = ref(zoneEditorStore.zoneSettings.width)
const height = ref(zoneEditorStore.zoneSettings.height)
const pvp = ref(zoneEditorStore.zoneSettings.pvp)
const name = ref(zoneEditorStore.zoneSettings?.name)
const width = ref(zoneEditorStore.zoneSettings?.width)
const height = ref(zoneEditorStore.zoneSettings?.height)
const pvp = ref(zoneEditorStore.zoneSettings?.pvp)
watch(name, (value) => {
zoneEditorStore.setZoneName(value)

View File

@ -52,16 +52,15 @@ function createTileLayer() {
}
function createTileArray() {
return Array.from({ length: zoneStore.zone?.width ?? 0 }, () => Array.from({ length: zoneStore.zone?.height ?? 0 }, () => 'blank_tile'))
return Array.from({ length: zoneTilemap.height || 0 }, () => Array.from({ length: zoneTilemap.width || 0 }, () => 'blank_tile'))
}
onBeforeMount(() => {
if (zoneStore.zone?.tiles) {
setAllTiles(zoneTilemap, tiles, zoneStore.zone.tiles)
tileArray = zoneStore.zone.tiles.map((row) => row.map((tileId) => tileId || 'blank_tile'))
} else {
tileArray.forEach((row, y) => row.forEach((_, x) => placeTile(zoneTilemap, tiles, x, y, 'blank_tile')))
if (!zoneStore.zone?.tiles) {
return
}
setAllTiles(zoneTilemap, tiles, zoneStore.zone.tiles)
tileArray = zoneStore.zone.tiles.map((row) => row.map((tileId) => tileId || 'blank_tile'))
})
onBeforeUnmount(() => {

View File

@ -2,15 +2,16 @@ import config from '@/config'
import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
import Tileset = Phaser.Tilemaps.Tileset
import Tile = Phaser.Tilemaps.Tile
import { useGameStore } from '@/stores/gameStore'
export function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
export function getTile(x: number, y: number, layer: TilemapLayer): Tile | undefined {
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
if (!tile) return undefined
return tile
}
export function tileToWorldXY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number) {
export function tileToWorldXY(layer: TilemapLayer, pos_x: number, pos_y: number) {
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
const positionX = worldPoint.x + config.tile_size.y
const positionY = worldPoint.y
@ -18,7 +19,7 @@ export function tileToWorldXY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number
return { positionX, positionY }
}
export function tileToWorldX(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number): number {
export function tileToWorldX(layer: TilemapLayer, pos_x: number, pos_y: number): number {
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
return worldPoint.x + config.tile_size.x / 2
}
@ -58,6 +59,10 @@ export const sortByIsometricDepth = <T extends { positionX: number; positionY: n
})
}
export const clearAssets = (scene: Phaser.Scene) => {
}
export const loadAssets = (scene: Phaser.Scene): Promise<void> => {
return new Promise((resolve) => {
const gameStore = useGameStore()

View File

@ -12,7 +12,7 @@ export type TeleportSettings = {
export const useZoneEditorStore = defineStore('zoneEditor', {
state: () => {
return {
active: false,
active: true,
zone: null as Zone | null,
tool: 'move',
drawMode: 'tile',
@ -114,8 +114,8 @@ export const useZoneEditorStore = defineStore('zoneEditor', {
setTeleportSettings(teleportSettings: TeleportSettings) {
this.teleportSettings = teleportSettings
},
reset() {
// this.zone = null
reset(resetZone = false) {
if (resetZone) this.zone = null
this.zoneList = []
this.tileList = []
this.objectList = []