Changes to mapeditorcomposable, fix pencil and fill tool for tiles

Locked in, made mapeditor my bi-
This commit is contained in:
Colin Kallemein 2025-01-26 23:28:15 +01:00
parent cfac1d508b
commit 14aa696197
15 changed files with 206 additions and 101 deletions

View File

@ -17,24 +17,24 @@ import BackgroundImageLoader from '@/components/utilities/BackgroundImageLoader.
import Debug from '@/components/utilities/Debug.vue' import Debug from '@/components/utilities/Debug.vue'
import Notifications from '@/components/utilities/Notifications.vue' import Notifications from '@/components/utilities/Notifications.vue'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { computed, watch } from 'vue' import { computed, watch } from 'vue'
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const currentScreen = computed(() => { const currentScreen = computed(() => {
if (!gameStore.game.isLoaded) return Loading if (!gameStore.game.isLoaded) return Loading
if (!gameStore.connection) return Login if (!gameStore.connection) return Login
if (!gameStore.token) return Login if (!gameStore.token) return Login
if (!gameStore.character) return Characters if (!gameStore.character) return Characters
if (mapEditorStore.active) return MapEditor if (mapEditor.active.value) return MapEditor
return Game return Game
}) })
// Watch mapEditorStore.active and empty gameStore.game.loadedAssets // Watch mapEditor.active and empty gameStore.game.loadedAssets
watch( watch(
() => mapEditorStore.active, () => mapEditor.active.value,
() => { () => {
gameStore.game.loadedTextures = [] gameStore.game.loadedTextures = []
} }

View File

@ -6,7 +6,7 @@
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Users</button> <button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Users</button>
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Chats</button> <button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Chats</button>
<button @mousedown.stop class="btn-cyan active py-1.5 px-4 min-w-24">Asset manager</button> <button @mousedown.stop class="btn-cyan active py-1.5 px-4 min-w-24">Asset manager</button>
<button class="btn-cyan py-1.5 px-4 min-w-24" type="button" @click="() => mapEditorStore.toggleActive()">Map editor</button> <button class="btn-cyan py-1.5 px-4 min-w-24" type="button" @click="() => mapEditor.toggleActive()">Map editor</button>
</div> </div>
</template> </template>
<template #modalBody> <template #modalBody>
@ -21,11 +21,11 @@
import AssetManager from '@/components/gameMaster/assetManager/AssetManager.vue' import AssetManager from '@/components/gameMaster/assetManager/AssetManager.vue'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { ref } from 'vue' import { ref } from 'vue'
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
let toggle = ref('asset-manager') let toggle = ref('asset-manager')
</script> </script>

View File

@ -29,7 +29,6 @@ import ChipsInput from '@/components/forms/ChipsInput.vue'
import { TileStorage } from '@/storage/storages' import { TileStorage } from '@/storage/storages'
import { useAssetManagerStore } from '@/stores/assetManagerStore' import { useAssetManagerStore } from '@/stores/assetManagerStore'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { computed, onBeforeUnmount, onMounted, ref, toRaw, watch } from 'vue' import { computed, onBeforeUnmount, onMounted, ref, toRaw, watch } from 'vue'
const gameStore = useGameStore() const gameStore = useGameStore()

View File

@ -8,13 +8,13 @@
import MapEventTiles from '@/components/gameMaster/mapEditor/mapPartials/MapEventTiles.vue' import MapEventTiles from '@/components/gameMaster/mapEditor/mapPartials/MapEventTiles.vue'
import MapTiles from '@/components/gameMaster/mapEditor/mapPartials/MapTiles.vue' import MapTiles from '@/components/gameMaster/mapEditor/mapPartials/MapTiles.vue'
import PlacedMapObjects from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue' import PlacedMapObjects from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { onMounted, onUnmounted, shallowRef } from 'vue' import { onMounted, onUnmounted, shallowRef } from 'vue'
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>() const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>()
onUnmounted(() => { onUnmounted(() => {
mapEditorStore.reset() mapEditor.reset()
}) })
</script> </script>

View File

@ -8,7 +8,6 @@ import Controls from '@/components/utilities/Controls.vue'
import { createTileArray, getTile, placeTile, setLayerTiles } from '@/composables/mapComposable' import { createTileArray, getTile, placeTile, setLayerTiles } from '@/composables/mapComposable'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { TileStorage } from '@/storage/storages' import { TileStorage } from '@/storage/storages'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { useScene } from 'phavuer' import { useScene } from 'phavuer'
import { onMounted, onUnmounted, shallowRef, watch } from 'vue' import { onMounted, onUnmounted, shallowRef, watch } from 'vue'
@ -18,7 +17,6 @@ const emit = defineEmits(['tileMap:create'])
const scene = useScene() const scene = useScene()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const mapEditorStore = useMapEditorStore()
const tileStorage = new TileStorage() const tileStorage = new TileStorage()
const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>() const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>()
@ -62,15 +60,16 @@ function pencil(pointer: Phaser.Input.Pointer) {
// Check if map is set // Check if map is set
if (!mapEditor.currentMap.value) return if (!mapEditor.currentMap.value) return
console.log(mapEditor.tool.value)
// Check if tool is pencil // Check if tool is pencil
if (mapEditorStore.tool !== 'pencil') return if (mapEditor.tool.value !== 'pencil') return
// Check if draw mode is tile // Check if draw mode is tile
if (mapEditorStore.drawMode !== 'tile') return if (mapEditor.drawMode.value !== 'tile') return
// Check if there is a selected tile // Check if there is a selected tile
if (!mapEditorStore.selectedTile) return if (!mapEditor.selectedTile.value) return // Changed this line to access .value
// Check if left mouse button is pressed // Check if left mouse button is pressed
if (!pointer.isDown) return if (!pointer.isDown) return
@ -83,10 +82,10 @@ function pencil(pointer: Phaser.Input.Pointer) {
if (!tile) return if (!tile) return
// Place tile // Place tile
placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, mapEditorStore.selectedTile) placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, mapEditor.selectedTile.value)
// Adjust mapEditorStore.map.tiles // Adjust mapEditor tiles
mapEditor.currentMap.value.tiles[tile.y][tile.x] = mapEditor.currentMap.value.tiles[tile.y][tile.x] mapEditor.currentMap.value.tiles[tile.y][tile.x] = mapEditor.selectedTile.value
} }
function eraser(pointer: Phaser.Input.Pointer) { function eraser(pointer: Phaser.Input.Pointer) {
@ -96,10 +95,10 @@ function eraser(pointer: Phaser.Input.Pointer) {
if (!mapEditor.currentMap.value) return if (!mapEditor.currentMap.value) return
// Check if tool is pencil // Check if tool is pencil
if (mapEditorStore.tool !== 'eraser') return if (mapEditor.tool.value !== 'eraser') return
// Check if draw mode is tile // Check if draw mode is tile
if (mapEditorStore.eraserMode !== 'tile') return if (mapEditor.eraserMode.value !== 'tile') return
// Check if left mouse button is pressed // Check if left mouse button is pressed
if (!pointer.isDown) return if (!pointer.isDown) return
@ -117,7 +116,7 @@ function eraser(pointer: Phaser.Input.Pointer) {
// Place tile // Place tile
placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, 'blank_tile') placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, 'blank_tile')
// Adjust mapEditorStore.map.tiles // Adjust mapEditor.map.tiles
mapEditor.currentMap.value.tiles[tile.y][tile.x] = 'blank_tile' mapEditor.currentMap.value.tiles[tile.y][tile.x] = 'blank_tile'
} }
@ -128,10 +127,10 @@ function paint(pointer: Phaser.Input.Pointer) {
if (!mapEditor.currentMap.value) return if (!mapEditor.currentMap.value) return
// Check if tool is pencil // Check if tool is pencil
if (mapEditorStore.tool !== 'paint') return if (mapEditor.tool.value !== 'paint') return
// Check if there is a selected tile // Check if there is a selected tile
if (!mapEditorStore.selectedTile) return if (!mapEditor.selectedTile.value) return
// Check if left mouse button is pressed // Check if left mouse button is pressed
if (!pointer.isDown) return if (!pointer.isDown) return
@ -143,10 +142,10 @@ function paint(pointer: Phaser.Input.Pointer) {
if (pointer.event.altKey) return if (pointer.event.altKey) return
// Set new tileArray with selected tile // Set new tileArray with selected tile
setLayerTiles(tileMap.value, tileLayer.value, createTileArray(tileMap.value.width, tileMap.value.height, mapEditorStore.selectedTile)) setLayerTiles(tileMap.value, tileLayer.value, createTileArray(tileMap.value.width, tileMap.value.height, mapEditor.selectedTile.value))
// Adjust mapEditorStore.map.tiles // Adjust mapEditor.map.tiles
mapEditor.currentMap.value.tiles = createTileArray(tileMap.value.width, tileMap.value.height, mapEditor.currentMap.value.tiles) mapEditor.currentMap.value.tiles = createTileArray(tileMap.value.width, tileMap.value.height, mapEditor.selectedTile.value)
} }
// When alt is pressed, and the pointer is down, select the tile that the pointer is over // When alt is pressed, and the pointer is down, select the tile that the pointer is over
@ -157,10 +156,10 @@ function tilePicker(pointer: Phaser.Input.Pointer) {
if (!mapEditor.currentMap.value) return if (!mapEditor.currentMap.value) return
// Check if tool is pencil // Check if tool is pencil
if (mapEditorStore.tool !== 'pencil') return if (mapEditor.tool.value !== 'pencil') return
// Check if draw mode is tile // Check if draw mode is tile
if (mapEditorStore.drawMode !== 'tile') return if (mapEditor.drawMode.value !== 'tile') return
// Check if left mouse button is pressed // Check if left mouse button is pressed
if (!pointer.isDown) return if (!pointer.isDown) return
@ -176,17 +175,17 @@ function tilePicker(pointer: Phaser.Input.Pointer) {
if (!tile) return if (!tile) return
// Select the tile // Select the tile
mapEditorStore.setSelectedMapObject(mapEditor.currentMap.value.tiles[tile.y][tile.x]) mapEditor.setSelectedTile(mapEditor.currentMap.value.tiles[tile.y][tile.x])
} }
watch( watch(
() => mapEditorStore.shouldClearTiles, () => mapEditor.shouldClearTiles.value,
(shouldClear) => { (shouldClear) => {
if (shouldClear && mapEditor.currentMap.value && tileMap.value && tileLayer.value) { if (shouldClear && mapEditor.currentMap.value && tileMap.value && tileLayer.value) {
const blankTiles = createTileArray(tileMap.value.width, tileMap.value.height, 'blank_tile') const blankTiles = createTileArray(tileMap.value.width, tileMap.value.height, 'blank_tile')
setLayerTiles(tileMap.value, tileLayer.value, blankTiles) setLayerTiles(tileMap.value, tileLayer.value, blankTiles)
mapEditor.currentMap.value.tiles = blankTiles mapEditor.currentMap.value.tiles = blankTiles
mapEditorStore.resetClearTilesFlag() mapEditor.resetClearTilesFlag()
} }
} }
) )

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal :isModalOpen="mapEditorStore.isCreateMapModalShown" @modal:close="() => mapEditorStore.toggleCreateMapModal()" :modal-width="300" :modal-height="420" :is-resizable="false" :bg-style="'none'"> <Modal :isModalOpen="mapEditor.isCreateMapModalShown.value" @modal:close="() => mapEditor.toggleCreateMapModal()" :modal-width="300" :modal-height="420" :is-resizable="false" :bg-style="'none'">
<template #modalHeader> <template #modalHeader>
<h3 class="m-0 font-medium shrink-0 text-white">Create new map</h3> <h3 class="m-0 font-medium shrink-0 text-white">Create new map</h3>
</template> </template>
@ -39,13 +39,13 @@ import type { Map } from '@/application/types'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
import { MapStorage } from '@/storage/storages' import { MapStorage } from '@/storage/storages'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { ref } from 'vue' import { ref } from 'vue'
const emit = defineEmits(['create']) const emit = defineEmits(['create'])
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const mapStorage = new MapStorage() const mapStorage = new MapStorage()
const name = ref('') const name = ref('')
@ -79,6 +79,6 @@ async function submit() {
}) })
// Close modal // Close modal
mapEditorStore.toggleCreateMapModal() mapEditor.toggleCreateMapModal()
} }
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal :is-modal-open="mapEditorStore.isMapListModalShown" @modal:close="() => mapEditorStore.toggleMapListModal()" :is-resizable="false" :modal-width="300" :modal-height="360" :bg-style="'none'"> <Modal :is-modal-open="mapEditor.isMapListModalShown.value" @modal:close="() => mapEditor.toggleMapListModal()" :is-resizable="false" :modal-width="300" :modal-height="360" :bg-style="'none'">
<template #modalHeader> <template #modalHeader>
<h3 class="text-lg text-white">Maps</h3> <h3 class="text-lg text-white">Maps</h3>
</template> </template>
@ -7,7 +7,7 @@
<div class="my-4 mx-auto h-full"> <div class="my-4 mx-auto h-full">
<div class="text-center mb-4 px-2 flex gap-2.5"> <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="fetchMaps">Refresh</button> <button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="fetchMaps">Refresh</button>
<button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="() => mapEditorStore.toggleCreateMapModal()">New</button> <button class="btn-cyan py-1.5 min-w-[calc(50%_-_5px)]" @click="() => mapEditor.toggleCreateMapModal()">New</button>
</div> </div>
<div class="overflow-y-auto h-[calc(100%-20px)]"> <div class="overflow-y-auto h-[calc(100%-20px)]">
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap" v-for="(map, index) in mapList" :key="map.id"> <div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap" v-for="(map, index) in mapList" :key="map.id">
@ -25,7 +25,7 @@
</template> </template>
</Modal> </Modal>
<CreateMap @create="fetchMaps" v-if="mapEditorStore.isMapListModalShown" /> <CreateMap @create="fetchMaps" v-if="mapEditor.isMapListModalShown.value" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -35,11 +35,9 @@ import Modal from '@/components/utilities/Modal.vue'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { MapStorage } from '@/storage/storages' import { MapStorage } from '@/storage/storages'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const mapStorage = new MapStorage() const mapStorage = new MapStorage()
@ -57,7 +55,7 @@ function loadMap(id: UUID) {
gameStore.connection?.emit('gm:map:request', { mapId: id }, (response: Map) => { gameStore.connection?.emit('gm:map:request', { mapId: id }, (response: Map) => {
mapEditor.loadMap(response) mapEditor.loadMap(response)
}) })
mapEditorStore.toggleMapListModal() mapEditor.toggleMapListModal()
} }
async function deleteMap(id: UUID) { async function deleteMap(id: UUID) {

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal :isModalOpen="mapEditorStore.isMapObjectListModalShown" :modal-width="645" :modal-height="260" @modal:close="() => (mapEditorStore.isMapObjectListModalShown = false)" :bg-style="'none'"> <Modal :isModalOpen="mapEditor.isMapObjectListModalShown.value" :modal-width="645" :modal-height="260" @modal:close="() => (mapEditor.isMapObjectListModalShown.value = false)" :bg-style="'none'">
<template #modalHeader> <template #modalHeader>
<h3 class="text-lg text-white">Map objects</h3> <h3 class="text-lg text-white">Map objects</h3>
</template> </template>
@ -25,11 +25,11 @@
class="border-2 border-solid max-w-full" class="border-2 border-solid max-w-full"
:src="`${config.server_endpoint}/textures/map_objects/${mapObject.id}.png`" :src="`${config.server_endpoint}/textures/map_objects/${mapObject.id}.png`"
alt="Object" alt="Object"
@click="mapEditorStore.setSelectedMapObject(mapObject)" @click="mapEditor.setSelectedMapObject(mapObject)"
:class="{ :class="{
'cursor-pointer transition-all duration-300': true, 'cursor-pointer transition-all duration-300': true,
'border-cyan shadow-lg scale-105': mapEditorStore.selectedMapObject?.id === mapObject.id, 'border-cyan shadow-lg scale-105': mapEditor.selectedMapObject.value?.id === mapObject.id,
'border-transparent hover:border-gray-300': mapEditorStore.selectedMapObject?.id !== mapObject.id 'border-transparent hover:border-gray-300': mapEditor.selectedMapObject.value?.id !== mapObject.id
}" }"
/> />
</div> </div>
@ -45,14 +45,13 @@ import config from '@/application/config'
import type { MapObject } from '@/application/types' import type { MapObject } from '@/application/types'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
import { MapObjectStorage } from '@/storage/storages' import { MapObjectStorage } from '@/storage/storages'
import { useGameStore } from '@/stores/gameStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { liveQuery } from 'dexie' import { liveQuery } from 'dexie'
import { computed, onMounted, onUnmounted, ref } from 'vue' import { computed, onMounted, onUnmounted, ref } from 'vue'
const mapObjectStorage = new MapObjectStorage() const mapObjectStorage = new MapObjectStorage()
const isModalOpen = ref(false) const isModalOpen = ref(false)
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const searchQuery = ref('') const searchQuery = ref('')
const selectedTags = ref<string[]>([]) const selectedTags = ref<string[]>([])
const mapObjectList = ref<MapObject[]>([]) const mapObjectList = ref<MapObject[]>([])

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal :is-modal-open="mapEditorStore.isSettingsModalShown" @modal:close="() => mapEditorStore.toggleSettingsModal()" :modal-width="600" :modal-height="430" :bg-style="'none'"> <Modal :is-modal-open="mapEditor.isSettingsModalShown.value" @modal:close="() => mapEditor.toggleSettingsModal()" :modal-width="600" :modal-height="430" :bg-style="'none'">
<template #modalHeader> <template #modalHeader>
<h3 class="m-0 font-medium shrink-0 text-white">Map settings</h3> <h3 class="m-0 font-medium shrink-0 text-white">Map settings</h3>
</template> </template>
@ -51,11 +51,9 @@ import type { UUID } from '@/application/types'
import { uuidv4 } from '@/application/utilities' import { uuidv4 } from '@/application/utilities'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const mapEditorStore = useMapEditorStore()
const screen = ref('settings') const screen = ref('settings')
const name = ref(mapEditor.currentMap.value?.name) const name = ref(mapEditor.currentMap.value?.name)

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal :isModalOpen="mapEditorStore.isTileListModalShown" :modal-width="645" :modal-height="600" @modal:close="() => (mapEditorStore.isTileListModalShown = false)" :bg-style="'none'"> <Modal :isModalOpen="mapEditor.isTileListModalShown.value" :modal-width="645" :modal-height="600" @modal:close="() => (mapEditor.isTileListModalShown.value = false)" :bg-style="'none'">
<template #modalHeader> <template #modalHeader>
<h3 class="text-lg text-white">Tiles</h3> <h3 class="text-lg text-white">Tiles</h3>
</template> </template>
@ -85,12 +85,12 @@ import config from '@/application/config'
import type { Tile } from '@/application/types' import type { Tile } from '@/application/types'
import Modal from '@/components/utilities/Modal.vue' import Modal from '@/components/utilities/Modal.vue'
import { TileStorage } from '@/storage/storages' import { TileStorage } from '@/storage/storages'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { liveQuery } from 'dexie' import { liveQuery } from 'dexie'
import { computed, onMounted, onUnmounted, ref } from 'vue' import { computed, onMounted, onUnmounted, ref } from 'vue'
const tileStorage = new TileStorage() const tileStorage = new TileStorage()
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const searchQuery = ref('') const searchQuery = ref('')
const selectedTags = ref<string[]>([]) const selectedTags = ref<string[]>([])
const tileCategories = ref<Map<string, string>>(new Map()) const tileCategories = ref<Map<string, string>>(new Map())
@ -222,11 +222,11 @@ function closeGroup() {
} }
function selectTile(tile: string) { function selectTile(tile: string) {
mapEditorStore.setSelectedTile(tile) mapEditor.setSelectedTile(tile)
} }
function isActiveTile(tile: Tile): boolean { function isActiveTile(tile: Tile): boolean {
return mapEditorStore.selectedTile === tile.id return mapEditor.selectedTile.value === tile.id
} }
let subscription: any = null let subscription: any = null

View File

@ -2,20 +2,20 @@
<div class="flex justify-center p-5"> <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 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="toolbar" class="tools flex gap-2.5" v-if="mapEditor.currentMap.value"> <div ref="toolbar" class="tools flex gap-2.5" v-if="mapEditor.currentMap.value">
<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': mapEditorStore.tool === 'move' }" @click="handleClick('move')"> <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': mapEditor.tool.value === 'move' }" @click="handleClick('move')">
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/move.svg" alt="Move camera" /> <span class="h-5" :class="{ 'ml-2.5': mapEditorStore.tool !== 'move' }">(M)</span> <img class="invert w-5 h-5" src="/assets/icons/mapEditor/move.svg" alt="Move camera" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'move' }">(M)</span>
</button> </button>
<div class="w-px bg-cyan"></div> <div class="w-px bg-cyan"></div>
<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': mapEditorStore.tool === 'pencil' }" @click="handleClick('pencil')"> <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': mapEditor.tool.value === 'pencil' }" @click="handleClick('pencil')">
<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> <img class="invert w-5 h-5" src="/assets/icons/mapEditor/pencil.svg" alt="Pencil" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'pencil' }">(P)</span>
<div class="select" v-if="mapEditorStore.tool === 'pencil'"> <div class="select" v-if="mapEditor.tool.value === 'pencil'">
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectPencilOpen }"> <div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectPencilOpen }">
{{ mapEditorStore.drawMode.replace('_', ' ') }} {{ mapEditor.drawMode.value.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="" /> <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>
<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'"> <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 && mapEditor.tool.value === 'pencil'">
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('tile')"> <span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="setDrawMode('tile')">
Tile Tile
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div> <div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
@ -35,11 +35,11 @@
<div class="w-px bg-cyan"></div> <div class="w-px bg-cyan"></div>
<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': mapEditorStore.tool === 'eraser' }" @click="handleClick('eraser')"> <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': mapEditor.tool.value === 'eraser' }" @click="handleClick('eraser')">
<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> <img class="invert w-5 h-5" src="/assets/icons/mapEditor/eraser.svg" alt="Eraser" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'eraser' }">(E)</span>
<div class="select" v-if="mapEditorStore.tool === 'eraser'"> <div class="select" v-if="mapEditor.tool.value === 'eraser'">
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectEraserOpen }"> <div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectEraserOpen }">
{{ mapEditorStore.eraserMode.replace('_', ' ') }} {{ mapEditor.eraserMode.value.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" /> <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>
<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"> <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">
@ -62,8 +62,8 @@
<div class="w-px bg-cyan"></div> <div class="w-px bg-cyan"></div>
<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': mapEditorStore.tool === 'paint' }" @click="handleClick('paint')"> <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': mapEditor.tool.value === 'paint' }" @click="handleClick('paint')">
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/paint.svg" alt="Paint bucket" /> <span class="h-5" :class="{ 'ml-2.5': mapEditorStore.tool !== 'paint' }">(B)</span> <img class="invert w-5 h-5" src="/assets/icons/mapEditor/paint.svg" alt="Paint bucket" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'paint' }">(B)</span>
</button> </button>
<div class="w-px bg-cyan"></div> <div class="w-px bg-cyan"></div>
@ -72,10 +72,10 @@
</div> </div>
<div class="toolbar fixed bottom-0 right-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 space-x-2"> <div class="toolbar fixed bottom-0 right-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 space-x-2">
<button class="btn-cyan px-3.5" @click="() => mapEditorStore.toggleMapListModal()">Load</button> <button class="btn-cyan px-3.5" @click="() => mapEditor.toggleMapListModal()">Load</button>
<button class="btn-cyan px-3.5" @click="() => emit('save')" v-if="mapEditor.currentMap.value">Save</button> <button class="btn-cyan px-3.5" @click="() => emit('save')" v-if="mapEditor.currentMap.value">Save</button>
<button class="btn-cyan px-3.5" @click="() => emit('clear')" v-if="mapEditor.currentMap.value">Clear</button> <button class="btn-cyan px-3.5" @click="() => emit('clear')" v-if="mapEditor.currentMap.value">Clear</button>
<button class="btn-cyan px-3.5" @click="() => mapEditorStore.toggleActive()">Exit</button> <button class="btn-cyan px-3.5" @click="() => mapEditor.toggleActive()">Exit</button>
</div> </div>
</div> </div>
</div> </div>
@ -83,12 +83,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
import { onBeforeUnmount, onMounted, ref } from 'vue' import { onBeforeUnmount, onMounted, ref } from 'vue'
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const mapEditorStore = useMapEditorStore()
const emit = defineEmits(['save', 'clear']) const emit = defineEmits(['save', 'clear'])
@ -101,24 +99,24 @@ let selectEraserOpen = ref(false)
// drawMode // drawMode
function setDrawMode(value: string) { function setDrawMode(value: string) {
mapEditorStore.isTileListModalShown = value === 'tile' mapEditor.isTileListModalShown.value = value === 'tile'
mapEditorStore.isMapObjectListModalShown = value === 'map_object' mapEditor.isMapObjectListModalShown.value = value === 'map_object'
mapEditorStore.setDrawMode(value) mapEditor.setDrawMode(value)
selectPencilOpen.value = false selectPencilOpen.value = false
} }
// drawMode // drawMode
function setEraserMode(value: string) { function setEraserMode(value: string) {
mapEditorStore.setEraserMode(value) mapEditor.setEraserMode(value)
selectEraserOpen.value = false selectEraserOpen.value = false
} }
function handleClick(tool: string) { function handleClick(tool: string) {
if (tool === 'settings') { if (tool === 'settings') {
mapEditorStore.toggleSettingsModal() mapEditor.toggleSettingsModal()
} else { } else {
mapEditorStore.setTool(tool) mapEditor.setTool(tool)
} }
selectPencilOpen.value = tool === 'pencil' ? !selectPencilOpen.value : false selectPencilOpen.value = tool === 'pencil' ? !selectPencilOpen.value : false
@ -127,8 +125,8 @@ function handleClick(tool: string) {
function cycleToolMode(tool: 'pencil' | 'eraser') { function cycleToolMode(tool: 'pencil' | 'eraser') {
const modes = ['tile', 'object', 'teleport', 'blocking tile'] const modes = ['tile', 'object', 'teleport', 'blocking tile']
const currentMode = tool === 'pencil' ? mapEditorStore.drawMode : mapEditorStore.eraserMode const currentMode = tool === 'pencil' ? mapEditor.drawMode : mapEditor.eraserMode
const currentIndex = modes.indexOf(currentMode) const currentIndex = modes.indexOf(currentMode.value)
const nextIndex = (currentIndex + 1) % modes.length const nextIndex = (currentIndex + 1) % modes.length
const nextMode = modes[nextIndex] const nextMode = modes[nextIndex]
@ -141,7 +139,7 @@ function cycleToolMode(tool: 'pencil' | 'eraser') {
function initKeyShortcuts(event: KeyboardEvent) { function initKeyShortcuts(event: KeyboardEvent) {
// Check if map is set // Check if map is set
if (!mapEditorStore.map) return if (!mapEditor.currentMap.value) return
// prevent if focused on composables // prevent if focused on composables
if (document.activeElement?.tagName === 'INPUT') return if (document.activeElement?.tagName === 'INPUT') return
@ -156,7 +154,7 @@ function initKeyShortcuts(event: KeyboardEvent) {
if (keyActions.hasOwnProperty(event.key)) { if (keyActions.hasOwnProperty(event.key)) {
const tool = keyActions[event.key] const tool = keyActions[event.key]
if ((tool === 'pencil' || tool === 'eraser') && mapEditorStore.tool === tool) { if ((tool === 'pencil' || tool === 'eraser') && mapEditor.tool.value === tool) {
cycleToolMode(tool) cycleToolMode(tool)
} else { } else {
handleClick(tool) handleClick(tool)

View File

@ -32,14 +32,12 @@ import { loadAllTilesIntoScene } from '@/composables/mapComposable'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { MapStorage } from '@/storage/storages' import { MapStorage } from '@/storage/storages'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { Game, Scene } from 'phavuer' import { Game, Scene } from 'phavuer'
import { ref } from 'vue' import { ref } from 'vue'
const mapStorage = new MapStorage() const mapStorage = new MapStorage()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore()
const isLoaded = ref(false) const isLoaded = ref(false)
@ -92,8 +90,8 @@ function save() {
placedMapObjects: mapEditor.currentMap.value.placedMapObjects?.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject, depth, isRotated, positionX, positionY })) ?? [] placedMapObjects: mapEditor.currentMap.value.placedMapObjects?.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject, depth, isRotated, positionX, positionY })) ?? []
} }
if (mapEditorStore.isSettingsModalShown) { if (mapEditor.isSettingsModalShown.value) {
mapEditorStore.toggleSettingsModal() mapEditor.toggleSettingsModal()
} }
gameStore.connection?.emit('gm:map:update', data, (response: MapT) => { gameStore.connection?.emit('gm:map:update', data, (response: MapT) => {
@ -106,6 +104,6 @@ function clear() {
// Clear placed objects, event tiles and tiles // Clear placed objects, event tiles and tiles
mapEditor.clearMap() mapEditor.clearMap()
mapEditorStore.triggerClearTiles() mapEditor.triggerClearTiles()
} }
</script> </script>

View File

@ -1,13 +1,13 @@
import config from '@/application/config' import config from '@/application/config'
import { getTile, tileToWorldXY } from '@/composables/mapComposable' import { getTile, tileToWorldXY } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useMapEditorStore } from '@/stores/mapEditorStore' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { computed, ref, type Ref } from 'vue' import { computed, ref, type Ref } from 'vue'
export function useMapEditorPointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) { export function useMapEditorPointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
const gameStore = useGameStore() const gameStore = useGameStore()
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const isMoveTool = computed(() => mapEditorStore.tool === 'move') const isMoveTool = computed(() => mapEditor.tool.value === 'move')
const pointerStartPosition = ref({ x: 0, y: 0 }) const pointerStartPosition = ref({ x: 0, y: 0 })
const dragThreshold = 5 // pixels const dragThreshold = 5 // pixels

View File

@ -1,7 +1,32 @@
import type { Map } from '@/application/types' import type { Map, MapObject } from '@/application/types'
import { ref } from 'vue' import { ref } from 'vue'
export type TeleportSettings = {
toMapId: string
toPositionX: number
toPositionY: number
toRotation: number
}
const currentMap = ref<Map | null>(null) const currentMap = ref<Map | null>(null)
const active = ref(false)
const tool = ref('move')
const drawMode = ref('tile')
const eraserMode = ref('tile')
const selectedTile = ref('')
const selectedMapObject = ref<MapObject | null>(null)
const isTileListModalShown = ref(false)
const isMapObjectListModalShown = ref(false)
const isMapListModalShown = ref(false)
const isCreateMapModalShown = ref(false)
const isSettingsModalShown = ref(false)
const shouldClearTiles = ref(false)
const teleportSettings = ref<TeleportSettings>({
toMapId: '',
toPositionX: 0,
toPositionY: 0,
toRotation: 0
})
export function useMapEditorComposable() { export function useMapEditorComposable() {
const loadMap = (map: Map) => { const loadMap = (map: Map) => {
@ -16,16 +41,107 @@ export function useMapEditorComposable() {
const clearMap = () => { const clearMap = () => {
if (!currentMap.value) return if (!currentMap.value) return
currentMap.value.placedMapObjects = [] currentMap.value.placedMapObjects = []
currentMap.value.mapEventTiles = [] currentMap.value.mapEventTiles = []
currentMap.value.tiles = [] currentMap.value.tiles = []
} }
const toggleActive = () => {
if (active.value) reset()
active.value = !active.value
}
const setTool = (newTool: string) => {
tool.value = newTool
}
const setDrawMode = (mode: string) => {
drawMode.value = mode
}
const setEraserMode = (mode: string) => {
eraserMode.value = mode
}
const setSelectedTile = (tile: string) => {
selectedTile.value = tile
}
const setSelectedMapObject = (object: MapObject) => {
selectedMapObject.value = object
}
const toggleSettingsModal = () => {
isSettingsModalShown.value = !isSettingsModalShown.value
}
const toggleMapListModal = () => {
isMapListModalShown.value = !isMapListModalShown.value
isCreateMapModalShown.value = false
}
const toggleCreateMapModal = () => {
isCreateMapModalShown.value = !isCreateMapModalShown.value
}
const setTeleportSettings = (settings: TeleportSettings) => {
teleportSettings.value = settings
}
const triggerClearTiles = () => {
shouldClearTiles.value = true
}
const resetClearTilesFlag = () => {
shouldClearTiles.value = false
}
const reset = () => {
tool.value = 'move'
drawMode.value = 'tile'
selectedTile.value = ''
selectedMapObject.value = null
isTileListModalShown.value = false
isMapObjectListModalShown.value = false
isSettingsModalShown.value = false
isMapListModalShown.value = false
isCreateMapModalShown.value = false
shouldClearTiles.value = false
}
return { return {
// State
currentMap, currentMap,
active,
tool,
drawMode,
eraserMode,
selectedTile,
selectedMapObject,
isTileListModalShown,
isMapObjectListModalShown,
isMapListModalShown,
isCreateMapModalShown,
isSettingsModalShown,
shouldClearTiles,
teleportSettings,
// Methods
loadMap, loadMap,
updateProperty, updateProperty,
clearMap clearMap,
toggleActive,
setTool,
setDrawMode,
setEraserMode,
setSelectedTile,
setSelectedMapObject,
toggleSettingsModal,
toggleMapListModal,
toggleCreateMapModal,
setTeleportSettings,
triggerClearTiles,
resetClearTilesFlag,
reset
} }
} }

View File

@ -1,20 +1,20 @@
import { useMapEditorStore } from '@/stores/mapEditorStore'
import { computed, watch, type Ref } from 'vue' import { computed, watch, type Ref } from 'vue'
import { useGamePointerHandlers } from './pointerHandlers/useGamePointerHandlers' import { useGamePointerHandlers } from './pointerHandlers/useGamePointerHandlers'
import { useMapEditorPointerHandlers } from './pointerHandlers/useMapEditorPointerHandlers' import { useMapEditorPointerHandlers } from './pointerHandlers/useMapEditorPointerHandlers'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
export function usePointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) { export function usePointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilemaps.TilemapLayer, waypoint: Ref<{ visible: boolean; x: number; y: number }>, camera: Phaser.Cameras.Scene2D.Camera) {
const mapEditorStore = useMapEditorStore() const mapEditor = useMapEditorComposable()
const gameHandlers = useGamePointerHandlers(scene, layer, waypoint, camera) const gameHandlers = useGamePointerHandlers(scene, layer, waypoint, camera)
const mapEditorHandlers = useMapEditorPointerHandlers(scene, layer, waypoint, camera) const mapEditorHandlers = useMapEditorPointerHandlers(scene, layer, waypoint, camera)
const currentHandlers = computed(() => (mapEditorStore.active ? mapEditorHandlers : gameHandlers)) const currentHandlers = computed(() => (mapEditor.active.value ? mapEditorHandlers : gameHandlers))
const setupPointerHandlers = () => currentHandlers.value.setupPointerHandlers() const setupPointerHandlers = () => currentHandlers.value.setupPointerHandlers()
const cleanupPointerHandlers = () => currentHandlers.value.cleanupPointerHandlers() const cleanupPointerHandlers = () => currentHandlers.value.cleanupPointerHandlers()
watch( watch(
() => mapEditorStore.active, () => mapEditor.active.value,
() => { () => {
cleanupPointerHandlers() cleanupPointerHandlers()
setupPointerHandlers() setupPointerHandlers()