Map editor WIP

This commit is contained in:
Dennis Postma 2025-01-05 04:01:50 +01:00
parent 2d09715dc4
commit 0142850983
12 changed files with 107 additions and 109 deletions

View File

@ -7,7 +7,7 @@
<script setup lang="ts">
import type { MapCharacter, mapLoadData, UUID } from '@/application/types'
import Characters from '@/components/game/map/Characters.vue'
import MapObjects from '@/components/game/map/MapObjects.vue'
import MapObjects from '@/components/game/map/PlacedMapObjects.vue'
import MapTiles from '@/components/game/map/MapTiles.vue'
import { loadMapTilesIntoScene } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore'

View File

@ -1,14 +0,0 @@
<template>
<MapObject v-for="mapObject in mapStore.map?.mapObjects" :tilemap="tilemap" :placedMapObject />
</template>
<script setup lang="ts">
import MapObject from '@/components/game/map/partials/MapObject.vue'
import { useMapStore } from '@/stores/mapStore'
const mapStore = useMapStore()
defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
}>()
</script>

View File

@ -0,0 +1,14 @@
<template>
<PlacedMapObject v-for="placedMapObject in mapStore.map?.placedMapObjects" :tilemap="tilemap" :placedMapObject />
</template>
<script setup lang="ts">
import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue'
import { useMapStore } from '@/stores/mapStore'
const mapStore = useMapStore()
defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
}>()
</script>

View File

@ -16,7 +16,7 @@
<script setup lang="ts">
import { type Map } from '@/application/types'
import MapEventTiles from '@/components/gameMaster/mapEditor/mapPartials/MapEventTiles.vue'
import MapObjects from '@/components/gameMaster/mapEditor/mapPartials/MapObjects.vue'
import MapObjects from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue'
import MapTiles from '@/components/gameMaster/mapEditor/mapPartials/MapTiles.vue'
import MapList from '@/components/gameMaster/mapEditor/partials/MapList.vue'
import MapSettings from '@/components/gameMaster/mapEditor/partials/MapSettings.vue'
@ -53,9 +53,9 @@ function save() {
height: mapEditorStore.mapSettings.height,
tiles: mapEditorStore.map.tiles,
pvp: mapEditorStore.map.pvp,
mapEffects: mapEditorStore.map.mapEffects.map(({ id, mapId, effect, strength }) => ({ id, mapId, effect, strength })),
mapEventTiles: mapEditorStore.map.mapEventTiles.map(({ id, mapId, type, positionX, positionY, teleport }) => ({ id, mapId, type, positionX, positionY, teleport })),
placedMapObjects: mapEditorStore.map.placedMapObjects.map(({ id, mapId, objectId, depth, isRotated, positionX, positionY }) => ({ id, mapId, objectId, depth, isRotated, positionX, positionY }))
mapEffects: mapEditorStore.map.mapEffects?.map(({ id, effect, strength }) => ({ id, effect, strength })) ?? [],
mapEventTiles: mapEditorStore.map.mapEventTiles?.map(({ id, type, positionX, positionY, teleport }) => ({ id, type, positionX, positionY, teleport })) ?? [],
placedMapObjects: mapEditorStore.map.placedMapObjects?.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject, depth, isRotated, positionX, positionY })) ?? []
}
if (mapEditorStore.isSettingsModalShown) {

View File

@ -1,45 +0,0 @@
<template>
<Image v-if="gameStore.getLoadedAsset(props.mapObject.object.id)" v-bind="imageProps" />
</template>
<script setup lang="ts">
import type { AssetDataT, PlacedMapObject } from '@/application/types'
import { loadTexture } from '@/composables/gameComposable'
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore'
import { Image, useScene } from 'phavuer'
import { computed } from 'vue'
const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
mapObject: PlacedMapObject
selectedMapObject: PlacedMapObject | null
movingMapObject: PlacedMapObject | null
}>()
const gameStore = useGameStore()
const scene = useScene()
const imageProps = computed(() => ({
alpha: props.movingMapObject?.id === props.mapObject.id ? 0.5 : 1,
tint: props.selectedMapObject?.id === props.mapObject.id ? 0x00ff00 : 0xffffff,
depth: calculateIsometricDepth(props.mapObject.positionX, props.mapObject.positionY, props.mapObject.object.frameWidth, props.mapObject.object.frameHeight),
x: tileToWorldX(props.tilemap, props.mapObject.positionX, props.mapObject.positionY),
y: tileToWorldY(props.tilemap, props.mapObject.positionX, props.mapObject.positionY),
flipX: props.mapObject.isRotated,
texture: props.mapObject.object.id,
originY: Number(props.mapObject.object.originX),
originX: Number(props.mapObject.object.originY)
}))
loadTexture(scene, {
key: props.mapObject.object.id,
data: '/assets/objects/' + props.mapObject.object.id + '.png',
group: 'objects',
updatedAt: props.mapObject.object.updatedAt,
frameWidth: props.mapObject.object.frameWidth,
frameHeight: props.mapObject.object.frameHeight
} as AssetDataT).catch((error) => {
console.error('Error loading texture:', error)
})
</script>

View File

@ -0,0 +1,45 @@
<template>
<Image v-if="gameStore.getLoadedAsset(props.placedMapObject.mapObject.id)" v-bind="imageProps" />
</template>
<script setup lang="ts">
import type { AssetDataT, PlacedMapObject } from '@/application/types'
import { loadTexture } from '@/composables/gameComposable'
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore'
import { Image, useScene } from 'phavuer'
import { computed } from 'vue'
const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
placedMapObject: PlacedMapObject
selectedPlacedMapObject: PlacedMapObject | null
movingPlacedMapObject: PlacedMapObject | null
}>()
const gameStore = useGameStore()
const scene = useScene()
const imageProps = computed(() => ({
alpha: props.movingPlacedMapObject?.id === props.placedMapObject.id ? 0.5 : 1,
tint: props.selectedPlacedMapObject?.id === props.placedMapObject.id ? 0x00ff00 : 0xffffff,
depth: calculateIsometricDepth(props.placedMapObject.positionX, props.placedMapObject.positionY, props.placedMapObject.mapObject.frameWidth, props.placedMapObject.mapObject.frameHeight),
x: tileToWorldX(props.tilemap, props.placedMapObject.positionX, props.placedMapObject.positionY),
y: tileToWorldY(props.tilemap, props.placedMapObject.positionX, props.placedMapObject.positionY),
flipX: props.placedMapObject.isRotated,
texture: props.placedMapObject.mapObject.id,
originY: Number(props.placedMapObject.mapObject.originX),
originX: Number(props.placedMapObject.mapObject.originY)
}))
loadTexture(scene, {
key: props.placedMapObject.mapObject.id,
data: '/assets/map_objects/' + props.placedMapObject.mapObject.id + '.png',
group: 'map_objects',
updatedAt: props.placedMapObject.mapObject.updatedAt,
frameWidth: props.placedMapObject.mapObject.frameWidth,
frameHeight: props.placedMapObject.mapObject.frameHeight
} as AssetDataT).catch((error) => {
console.error('Error loading texture:', error)
})
</script>

View File

@ -1,22 +1,22 @@
<template>
<SelectedMapObject v-if="selectedMapObject" :mapObject="selectedMapObject" :movingMapObject="movingMapObject" @move="moveMapObject" @rotate="rotateMapObject" @delete="deleteMapObject" />
<MapObject v-for="mapObject in mapEditorStore.map?.placedMapObjects" :tilemap="tilemap" :mapObject :selectedMapObject :movingMapObject @pointerup="clickMapObject(mapObject)" />
<SelectedPlacedMapObjectComponent v-if="selectedPlacedMapObject" :placedMapObject="selectedPlacedMapObject" @move="moveMapObject" @rotate="rotateMapObject" @delete="deleteMapObject" />
<PlacedMapObject v-for="placedMapObject in mapEditorStore.map?.placedMapObjects" :tilemap="tilemap" :placedMapObject :selectedPlacedMapObject :movingPlacedMapObject @pointerup="clickPlacedMapObject(placedMapObject)" />
</template>
<script setup lang="ts">
import type { PlacedMapObject as MapObjectT } from '@/application/types'
import type { PlacedMapObject as PlacedMapObjectT } from '@/application/types'
import { uuidv4 } from '@/application/utilities'
import MapObject from '@/components/gameMaster/mapEditor/mapPartials/MapObject.vue'
import SelectedMapObject from '@/components/gameMaster/mapEditor/partials/SelectedMapObject.vue'
import { getTile } from '@/composables/mapComposable'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { useScene } from 'phavuer'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import PlacedMapObject from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObject.vue'
import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue'
const scene = useScene()
const mapEditorStore = useMapEditorStore()
const selectedMapObject = ref<MapObjectT | null>(null)
const movingMapObject = ref<MapObjectT | null>(null)
const selectedPlacedMapObject = ref<PlacedMapObjectT | null>(null)
const movingPlacedMapObject = ref<PlacedMapObjectT | null>(null)
const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap
@ -29,8 +29,8 @@ function pencil(pointer: Phaser.Input.Pointer) {
// Check if tool is pencil
if (mapEditorStore.tool !== 'pencil') return
// Check if draw mode is object
if (mapEditorStore.drawMode !== 'object') return
// Check if draw mode is map_object
if (mapEditorStore.drawMode !== 'map_object') return
// Check if there is a selected object
if (!mapEditorStore.selectedMapObject) return
@ -49,15 +49,13 @@ function pencil(pointer: Phaser.Input.Pointer) {
if (!tile) return
// Check if object already exists on position
const existingObject = mapEditorStore.map?.mapObjects.find((object) => object.positionX === tile.x && object.positionY === tile.y)
const existingObject = mapEditorStore.map.placedMapObjects.find((object) => object.positionX === tile.x && object.positionY === tile.y)
if (existingObject) return
const newObject = {
id: uuidv4(),
mapId: mapEditorStore.map.id,
map: mapEditorStore.map,
objectId: mapEditorStore.selectedMapObject.id,
object: mapEditorStore.selectedMapObject,
mapObject: mapEditorStore.selectedMapObject,
depth: 0,
isRotated: false,
positionX: tile.x,
@ -65,7 +63,7 @@ function pencil(pointer: Phaser.Input.Pointer) {
}
// Add new object to mapObjects
mapEditorStore.map.placedMapObjects = mapEditorStore.map.placedMapObjects.concat(newObject as MapObjectT)
mapEditorStore.map.placedMapObjects = mapEditorStore.map.placedMapObjects.concat(newObject as PlacedMapObjectT)
}
function eraser(pointer: Phaser.Input.Pointer) {
@ -75,8 +73,8 @@ function eraser(pointer: Phaser.Input.Pointer) {
// Check if tool is eraser
if (mapEditorStore.tool !== 'eraser') return
// Check if draw mode is object
if (mapEditorStore.eraserMode !== 'object') return
// Check if draw mode is map_object
if (mapEditorStore.eraserMode !== 'map_object') return
// Check if left mouse button is pressed
if (!pointer.isDown) return
@ -106,8 +104,8 @@ function objectPicker(pointer: Phaser.Input.Pointer) {
// Check if tool is pencil
if (mapEditorStore.tool !== 'pencil') return
// Check if draw mode is object
if (mapEditorStore.drawMode !== 'object') return
// Check if draw mode is map_object
if (mapEditorStore.drawMode !== 'map_object') return
// Check if left mouse button is pressed
if (!pointer.isDown) return
@ -134,23 +132,23 @@ function moveMapObject(id: string) {
// Check if map is set
if (!mapEditorStore.map) return
movingMapObject.value = mapEditorStore.map.placedMapObjects.find((object) => object.id === id) as MapObjectT
movingPlacedMapObject.value = mapEditorStore.map.placedMapObjects.find((object) => object.id === id) as PlacedMapObjectT
function handlePointerMove(pointer: Phaser.Input.Pointer) {
if (!movingMapObject.value) return
if (!movingPlacedMapObject.value) return
const tile = getTile(props.tilemap, pointer.worldX, pointer.worldY)
if (!tile) return
movingMapObject.value.positionX = tile.x
movingMapObject.value.positionY = tile.y
movingPlacedMapObject.value.positionX = tile.x
movingPlacedMapObject.value.positionY = tile.y
}
scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
function handlePointerUp() {
scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
movingMapObject.value = null
movingPlacedMapObject.value = null
}
scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp)
@ -176,15 +174,15 @@ function deleteMapObject(id: string) {
if (!mapEditorStore.map) return
mapEditorStore.map.placedMapObjects = mapEditorStore.map.placedMapObjects.filter((object) => object.id !== id)
selectedMapObject.value = null
selectedPlacedMapObject.value = null
}
function clickMapObject(mapObject: MapObjectT) {
selectedMapObject.value = mapObject
function clickPlacedMapObject(placedMapObject: PlacedMapObjectT) {
selectedPlacedMapObject.value = placedMapObject
// If alt is pressed, select the object
if (scene.input.activePointer.event.altKey) {
mapEditorStore.setSelectedMapObject(mapObject.mapObject)
mapEditorStore.setSelectedMapObject(placedMapObject.mapObject)
}
}
@ -210,7 +208,6 @@ watch(
(newObjects) => {
if (!mapEditorStore.map) return
console.log(mapEditorStore.map.placedMapObjects)
const updatedMapObjects = mapEditorStore.map.placedMapObjects.map((mapObject) => {
const updatedObject = newObjects.find((obj) => obj.id === mapObject.mapObject.id)
if (updatedObject) {
@ -229,7 +226,7 @@ watch(
// Update the map with the new mapObjects
mapEditorStore.setMap({
...mapEditorStore.map,
mapObjects: updatedMapObjects
placedMapObjects: updatedMapObjects
})
// Update selectedMapObject if it's set

View File

@ -1,7 +1,7 @@
<template>
<Modal :isModalOpen="mapEditorStore.isMapObjectListModalShown" :modal-width="645" :modal-height="260" @modal:close="() => (mapEditorStore.isMapObjectListModalShown = false)" :bg-style="'none'">
<template #modalHeader>
<h3 class="text-lg text-white">Objects</h3>
<h3 class="text-lg text-white">Map objects</h3>
</template>
<template #modalBody>
<div class="flex pt-4 pl-4">
@ -23,7 +23,7 @@
<div v-for="(mapObject, index) in filteredMapObjects" :key="index" class="max-w-1/4 inline-block">
<img
class="border-2 border-solid max-w-full"
:src="`${config.server_endpoint}/assets/objects/${mapObject.id}.png`"
:src="`${config.server_endpoint}/assets/map_objects/${mapObject.id}.png`"
alt="Object"
@click="mapEditorStore.setSelectedMapObject(mapObject)"
:class="{
@ -77,7 +77,7 @@ const toggleTag = (tag: string) => {
onMounted(async () => {
isModalOpen.value = true
gameStore.connection?.emit('', {}, (response: MapObject[]) => {
gameStore.connection?.emit('gm:mapObject:list', {}, (response: MapObject[]) => {
mapEditorStore.setMapObjectList(response)
})
})

View File

@ -14,20 +14,22 @@
import type { PlacedMapObject } from '@/application/types'
const props = defineProps<{
mapObject: PlacedMapObject
placedMapObject: PlacedMapObject
}>()
console.log(props.placedMapObject)
const emit = defineEmits(['move', 'rotate', 'delete'])
const handleMove = () => {
emit('move', props.mapObject.id)
emit('move', props.placedMapObject.id)
}
const handleRotate = () => {
emit('rotate', props.mapObject.id)
emit('rotate', props.placedMapObject.id)
}
const handleDelete = () => {
emit('delete', props.mapObject.id)
emit('delete', props.placedMapObject.id)
}
</script>

View File

@ -12,16 +12,16 @@
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/pencil.svg" alt="Pencil" /> <span class="h-5" :class="{ 'ml-2.5': mapEditorStore.tool !== 'pencil' }">(P)</span>
<div class="select" v-if="mapEditorStore.tool === 'pencil'">
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectPencilOpen }">
{{ mapEditorStore.drawMode }}
<img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/mapEditor/chevron.svg" />
{{ mapEditorStore.drawMode.replace('_', ' ') }}
<img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/mapEditor/chevron.svg" alt="" />
</div>
<div class="flex flex-col absolute bottom-full mb-5 left-1/2 -translate-x-1/2 bg-gray rounded min-w-28 border border-gray-500 border-solid text-left" v-show="selectPencilOpen && mapEditorStore.tool === 'pencil'">
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('tile')">
Tile
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
</span>
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('object')">
Object
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('map_object')">
Map object
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
</span>
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('teleport')">
@ -39,7 +39,7 @@
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/eraser.svg" alt="Eraser" /> <span class="h-5" :class="{ 'ml-2.5': mapEditorStore.tool !== 'eraser' }">(E)</span>
<div class="select" v-if="mapEditorStore.tool === 'eraser'">
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectEraserOpen }">
{{ mapEditorStore.eraserMode }}
{{ mapEditorStore.eraserMode.replace('_', ' ') }}
<img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/mapEditor/chevron.svg" />
</div>
<div class="flex flex-col absolute bottom-full mb-5 left-1/2 -translate-x-1/2 bg-gray rounded min-w-28 border border-gray-500 border-solid text-left" v-show="selectEraserOpen">
@ -47,8 +47,8 @@
Tile
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
</span>
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setEraserMode('object')">
Object
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setEraserMode('map_object')">
Map object
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
</span>
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setEraserMode('teleport')">
@ -100,7 +100,7 @@ let selectEraserOpen = ref(false)
// drawMode
function setDrawMode(value: string) {
mapEditorStore.isTileListModalShown = value === 'tile'
mapEditorStore.isObjectListModalShown = value === 'object'
mapEditorStore.isMapObjectListModalShown = value === 'map_object'
mapEditorStore.setDrawMode(value)
selectPencilOpen.value = false

View File

@ -110,7 +110,6 @@ export const useMapEditorStore = defineStore('mapEditor', {
triggerClearTiles() {
this.shouldClearTiles = true
},
resetClearTilesFlag() {
this.shouldClearTiles = false
},