210 lines
8.8 KiB
Vue

<template>
<div class="flex justify-center p-5">
<div class="toolbar fixed top-6 mx-6 rounded left-0 right-0 flex bg-gray-300/80 solid border-solid border-2 border-cyan ext-gray-50 p-1.5 px-3 p min-w-11/12 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.5': zoneEditorStore.tool !== 'move' }">(M)</span>
</button>
<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-50 gap-2.5': zoneEditorStore.tool === 'pencil' }" @click="handlePencilClick">
<img class="invert w-5 h-5" src="/assets/icons/zoneEditor/pencil.svg" alt="Pencil" /> <span :class="{ 'ml-2.5': 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 }">
{{ 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-full mt-5 left-1/2 -translate-x-1/2 bg-gray-300/80 rounded min-w-28 border border-cyan border-solid text-left" v-show="selectPencilOpen && zoneEditorStore.tool === 'pencil'">
<span class="py-2 px-2.5 relative hover:bg-cyan/50" @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/50" @click="setDrawMode('object')">
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/50" @click="setDrawMode('teleport')">
Teleport
<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/50" @click="setDrawMode('blocking tile')">Blocking tile</span>
</div>
</div>
</button>
<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-50 gap-2.5': zoneEditorStore.tool === 'eraser' }" @click="handleEraserClick">
<img class="invert w-5 h-5" src="/assets/icons/zoneEditor/eraser.svg" alt="Eraser" /> <span :class="{ 'ml-2.5': 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 }">
{{ zoneEditorStore.eraserMode }}
<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-full mt-5 left-1/2 -translate-x-1/2 bg-gray-300/80 rounded min-w-28 border border-cyan border-solid text-left" v-show="selectEraserOpen">
<span class="py-2 px-2.5 relative hover:bg-cyan/50" @click="setEraserMode('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/50" @click="setEraserMode('object')">
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/50" @click="setEraserMode('teleport')">
Teleport
<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/50" @click="setEraserMode('blocking tile')">Blocking tile</span>
</div>
</div>
</button>
<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-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.5': zoneEditorStore.tool !== 'paint' }">(B)</span>
</button>
<div class="w-px 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.5': zoneEditorStore.tool !== 'settings' }">(Z)</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, onMounted, 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) {
if (value === 'tile') {
zoneEditorStore.isTileListModalShown = true
}
if (value === 'object') {
zoneEditorStore.isObjectListModalShown = true
}
zoneEditorStore.setDrawMode(value)
selectPencilOpen.value = false
}
// drawMode
function setEraserMode(value: string) {
zoneEditorStore.setEraserMode(value)
selectEraserOpen.value = false
}
function clickTile(pointer: Phaser.Input.Pointer) {
if (zoneEditorStore.tool !== 'eraser' && 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 === '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 handlePencilClick() {
zoneEditorStore.setTool('pencil')
selectPencilOpen.value = !selectPencilOpen.value
}
function handleEraserClick() {
zoneEditorStore.setTool('eraser')
selectEraserOpen.value = !selectEraserOpen.value
}
// Key bindings
function initKeyShortcuts(event: KeyboardEvent) {
if (!zoneEditorStore.zone) return
// prevent if focussed on composables
if (document.activeElement?.tagName === 'INPUT') return
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 === 'z') {
console.log('settings')
zoneEditorStore.toggleSettingsModal()
}
}
</script>