<template> <div class="flex justify-center"> <div class="toolbar fixed top-0 left-0 right-0 flex bg-gray-300 bg-opacity-80 solid border-solid border-b-2 border-b-cyan border-t-0 border-r-0 border-l-0 text-gray-50 p-1.5 px-3 p min-w-[90%] h-10"> <div 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-50 gap-2.5': zoneEditorStore.tool === 'move' }" @click="zoneEditorStore.setTool('move')"> <img class="invert w-5 h-5" src="/assets/icons/zoneEditor/move.svg" alt="Move camera" /> <span :class="{ 'ml-2': zoneEditorStore.tool !== 'move' }">(M)</span> </button> <div class="w-[1px] 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-50 gap-2.5': zoneEditorStore.tool === 'pencil' }" @click="zoneEditorStore.setTool('pencil')"> <img class="invert w-5 h-5" src="/assets/icons/zoneEditor/pencil.svg" alt="Pencil" /> <span :class="{ 'ml-2': zoneEditorStore.tool !== 'pencil' }">(P)</span> <div class="select" v-if="zoneEditorStore.tool === 'pencil'"> <div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectPencilOpen }" @click="selectPencilOpen = !selectPencilOpen"> {{ zoneEditorStore.drawMode }} <img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/zoneEditor/chevron.svg" /> </div> <div class="flex flex-col absolute top-[100%] mt-5 left-[50%] translate-x-[-50%] bg-gray-300 bg-opacity-80 rounded min-w-[100px] border border-cyan border-solid text-left" v-show="selectPencilOpen && zoneEditorStore.tool === 'pencil'"> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('tile')"> Tile <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('object')"> Object <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('teleport')"> Teleport <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('blocking tile')">Blocking tile</span> </div> </div> </button> <div class="w-[1px] 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-50 gap-2.5': zoneEditorStore.tool === 'eraser' }" @click="zoneEditorStore.setTool('eraser')"> <img class="invert w-5 h-5" src="/assets/icons/zoneEditor/eraser.svg" alt="Eraser" /> <span :class="{ 'ml-2': zoneEditorStore.tool !== 'eraser' }">(E)</span> <div class="select" v-if="zoneEditorStore.tool === 'eraser'"> <div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectEraserOpen }" @click="selectEraserOpen = !selectEraserOpen"> {{ zoneEditorStore.drawMode }} <img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/zoneEditor/chevron.svg" /> </div> <div class="flex flex-col absolute top-[100%] mt-5 left-[50%] translate-x-[-50%] bg-gray-300 bg-opacity-80 rounded min-w-[100px] border border-cyan border-solid text-left" v-show="selectEraserOpen"> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('tile')"> Tile <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('object')"> Object <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('teleport')"> Teleport <div class="absolute w-[80%] left-1/2 translate-x-[-50%] bottom-0 h-[1px] bg-cyan"></div> </span> <span class="py-[8px] px-[10px] relative hover:bg-cyan hover:bg-opacity-50" @click="setDrawMode('blocking tile')">Blocking tile</span> </div> </div> </button> <div class="w-[1px] 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-50 gap-2.5': zoneEditorStore.tool === 'paint' }" @click="zoneEditorStore.setTool('paint')"> <img class="invert w-5 h-5" src="/assets/icons/zoneEditor/paint.svg" alt="Paint bucket" /> <span :class="{ 'ml-2': zoneEditorStore.tool !== 'paint' }">(B)</span> </button> <div class="w-[1px] bg-cyan"></div> <button class="flex justify-center items-center min-w-10 p-0 relative" @click="() => zoneEditorStore.toggleSettingsModal()" v-if="zoneEditorStore.zone"> <img class="invert w-5 h-5" src="/assets/icons/zoneEditor/gear.svg" alt="Zone settings" /> <span :class="{ 'ml-2': zoneEditorStore.tool !== 'settings' }">(S)</span> </button> </div> <div class="flex gap-2.5 ml-auto"> <button class="btn-cyan px-3.5" @click="() => zoneEditorStore.toggleZoneListModal()">Load</button> <button class="btn-cyan px-3.5" @click="() => emit('save')" v-if="zoneEditorStore.zone">Save</button> <button class="btn-cyan px-3.5" @click="() => emit('clear')" v-if="zoneEditorStore.zone">Clear</button> <button class="btn-cyan px-3.5" @click="() => zoneEditorStore.toggleActive()">Exit</button> </div> </div> </div> </template> <script setup lang="ts"> import { onBeforeUnmount, ref } from 'vue' import { useScene } from 'phavuer' import { getTile } from '@/services/zone' import { useZoneEditorStore } from '@/stores/zoneEditor' 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 select state let selectPencilOpen = ref(false) let selectEraserOpen = ref(false) // drawMode function setDrawMode(value: string) { zoneEditorStore.setDrawMode(value) selectPencilOpen.value = false selectEraserOpen.value = false } function drawTile(pointer: Phaser.Input.Pointer) { if (zoneEditorStore.tool !== 'eraser' && zoneEditorStore.tool !== 'pencil' && zoneEditorStore.tool !== 'paint') { return } if (pointer.event.altKey) { 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 === '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 drawTile(pointer) } scene.input.on(Phaser.Input.Events.POINTER_UP, drawTile) scene.input.on(Phaser.Input.Events.POINTER_MOVE, drawTiles) onBeforeUnmount(() => { scene.input.off(Phaser.Input.Events.POINTER_UP, drawTile) scene.input.off(Phaser.Input.Events.POINTER_MOVE, drawTiles) }) // Key bindings const keyDown = (event: KeyboardEvent) => { if (event.key === 'm') { zoneEditorStore.setTool('move') } if (event.key === 'p') { zoneEditorStore.setTool('pencil') } if (event.key === 'e') { zoneEditorStore.setTool('eraser') } if (event.key === 'b') { zoneEditorStore.setTool('paint') } if (event.key === 's') { zoneEditorStore.toggleSettingsModal() } } addEventListener('keydown', keyDown) </script>