forked from noxious/client
248 lines
7.0 KiB
Vue
248 lines
7.0 KiB
Vue
<template>
|
|
<div class="wrapper">
|
|
<div class="toolbar">
|
|
<div class="tools">
|
|
<button class="tool move" :class="{ active: zoneEditorStore.tool === 'move' }" @click="zoneEditorStore.setTool('move')">
|
|
<img src="/assets/icons/zoneEditor/move.svg" alt="Move camera" />
|
|
</button>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<button class="tool pencil" :class="{ active: zoneEditorStore.tool === 'pencil' }" @click="zoneEditorStore.setTool('pencil')">
|
|
<img src="/assets/icons/zoneEditor/pencil.svg" alt="Pencil" />
|
|
<div class="select" v-if="zoneEditorStore.tool === 'pencil'">
|
|
<div class="select-trigger" :class="{ open: selectPencilOpen }" @click="selectPencilOpen = !selectPencilOpen">
|
|
{{ zoneEditorStore.drawMode }}
|
|
<img src="/assets/icons/zoneEditor/chevron.svg" />
|
|
</div>
|
|
<div class="options" v-show="selectPencilOpen && zoneEditorStore.tool === 'pencil'">
|
|
<span class="option" @click="setDrawMode('tile')">Tile</span>
|
|
<span class="option" @click="setDrawMode('decoration')">Decoration</span>
|
|
<span class="option" @click="setDrawMode('teleport')">Teleport</span>
|
|
<span class="option" @click="setDrawMode('blocking tile')">Blocking tile</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<button class="tool eraser" :class="{ active: zoneEditorStore.tool === 'eraser' }" @click="zoneEditorStore.setTool('eraser')">
|
|
<img src="/assets/icons/zoneEditor/eraser.svg" alt="Eraser" />
|
|
<div class="select" v-if="zoneEditorStore.tool === 'eraser'">
|
|
<div class="select-trigger" :class="{ open: selectEraserOpen }" @click="selectEraserOpen = !selectEraserOpen">
|
|
{{ zoneEditorStore.drawMode }}
|
|
<img src="/assets/icons/zoneEditor/chevron.svg" />
|
|
</div>
|
|
<div class="options" v-show="selectEraserOpen">
|
|
<span class="option" @click="setDrawMode('tile')">Tile</span>
|
|
<span class="option" @click="setDrawMode('decoration')">Decoration</span>
|
|
<span class="option" @click="setDrawMode('teleport')">Teleport</span>
|
|
<span class="option" @click="setDrawMode('blocking tile')">Blocking tile</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="divider"></div>
|
|
<button class="tool settings" @click="() => zoneEditorStore.toggleSettingsModal()">
|
|
<img src="/assets/icons/zoneEditor/gear.svg" alt="Zone settings" />
|
|
</button>
|
|
</div>
|
|
|
|
<div class="buttons">
|
|
<button class="btn-cyan">Load</button>
|
|
<button class="btn-cyan" @click="() => emit('save')">Save</button>
|
|
<button class="btn-cyan" @click="clear">Clear</button>
|
|
<button class="btn-cyan" @click="() => zoneEditorStore.toggleActive()">Exit</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onBeforeUnmount, ref, watch } from 'vue'
|
|
import { useScene } from 'phavuer'
|
|
import { getTile, tileToWorldXY } from '@/services/zone'
|
|
import config from '@/config'
|
|
import { useZoneStore } from '@/stores/zone'
|
|
import { useZoneEditorStore } from '@/stores/zoneEditor'
|
|
|
|
const zoneEditorStore = useZoneEditorStore()
|
|
|
|
const props = defineProps({
|
|
layer: Phaser.Tilemaps.TilemapLayer
|
|
})
|
|
const scene = useScene()
|
|
const emit = defineEmits(['move', 'eraser', 'pencil', 'save'])
|
|
|
|
// 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') {
|
|
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 Phaser.Tilemaps.Tile
|
|
if (!pointer_tile) {
|
|
return
|
|
}
|
|
|
|
if (zoneEditorStore.tool === 'eraser') {
|
|
emit('eraser', pointer_tile)
|
|
}
|
|
|
|
if (zoneEditorStore.tool === 'pencil') {
|
|
emit('pencil', 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)
|
|
})
|
|
|
|
function clear() {
|
|
zoneEditorStore.setTiles(Array.from({ length: zoneEditorStore.width ?? 10 }, () => Array.from({ length: zoneEditorStore.height ?? 10 }, () => 0)))
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import '@/assets/scss/main';
|
|
|
|
.wrapper {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin: 10px;
|
|
}
|
|
|
|
.toolbar {
|
|
position: fixed;
|
|
top: 20px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
background: rgba($dark-gray, 0.8);
|
|
border: 2px solid $cyan;
|
|
color: $light-gray;
|
|
padding: 5px 5px 5px 10px;
|
|
min-width: 90%;
|
|
height: 40px;
|
|
|
|
.tools {
|
|
display: flex;
|
|
gap: 10px;
|
|
|
|
.divider {
|
|
width: 1px;
|
|
background: $cyan;
|
|
}
|
|
|
|
// vertical center
|
|
.tool {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-width: 40px;
|
|
padding: 0;
|
|
position: relative;
|
|
|
|
select {
|
|
display: none;
|
|
}
|
|
|
|
&.active {
|
|
border-bottom: 3px solid $light-cyan;
|
|
gap: 10px;
|
|
}
|
|
|
|
.select {
|
|
.select-trigger {
|
|
text-transform: capitalize;
|
|
display: flex;
|
|
gap: 15px;
|
|
|
|
img {
|
|
transform: rotate(0);
|
|
transition: ease-in-out 0.2s;
|
|
}
|
|
|
|
&.open {
|
|
img {
|
|
transform: rotate(180deg);
|
|
}
|
|
}
|
|
}
|
|
.options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: absolute;
|
|
top: calc(100% + 20px);
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: rgba($dark-gray, 0.8);
|
|
border-radius: 5px;
|
|
min-width: 100px;
|
|
border: 1px solid $cyan;
|
|
text-align: left;
|
|
.option {
|
|
padding: 8px 10px;
|
|
position: relative;
|
|
&:hover {
|
|
background-color: rgba($cyan, 0.5);
|
|
}
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 80%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
bottom: 0;
|
|
height: 1px;
|
|
background-color: $cyan;
|
|
}
|
|
&:last-child::after {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
img {
|
|
filter: invert(1);
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
}
|
|
|
|
.buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-left: auto;
|
|
|
|
button {
|
|
padding-left: 15px;
|
|
padding-right: 15px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|