Zone editor GUI (WIP), added a few helper functions
This commit is contained in:
parent
8227dfe4b3
commit
58bff2010f
1
public/assets/icons/zoneEditor/eraser-tool.svg
Normal file
1
public/assets/icons/zoneEditor/eraser-tool.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" height="25" viewBox="0 0 24 25" width="24" xmlns="http://www.w3.org/2000/svg"><g fill="#000"><path d="m11.671 20.8824h-2.342c-.668 0-1.296-.26-1.768-.732l-3-3c-.472-.472-.732-1.1-.732-1.768s.26-1.295.732-1.768l11.585-11.58497c.188-.188.52-.188.707 0l7 7c.195.195.195.512 0 .707l-10.414 10.41397c-.472.472-1.1.732-1.768.732zm4.829-17.79297-11.232 11.23297c-.283.283-.439.66-.439 1.061s.156.777.439 1.061l3 3c.284.283.66.439 1.061.439h2.343c.401 0 .777-.156 1.061-.439l10.06-10.06197z"/><path d="m15.5 17.8824c-.128 0-.256-.049-.354-.146l-7-7c-.195-.195-.195-.512 0-.707.195-.19497.512-.19497.707 0l7 7c.195.195.195.512 0 .707-.097.097-.225.146-.353.146z"/><path d="m5.5 20.8824h-5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h5c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m14.5 22.8824h-9c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h9c.276 0 .5.224.5.5s-.224.5-.5.5z"/></g></svg>
|
After Width: | Height: | Size: 875 B |
48
public/assets/icons/zoneEditor/tiles.svg
Normal file
48
public/assets/icons/zoneEditor/tiles.svg
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M506.816,268.181l-145.984-87.595l18.219-11.584c3.072-1.963,4.928-5.355,4.928-9.003s-1.856-7.04-4.928-9.003
|
||||||
|
L261.718,76.331c-3.499-2.219-7.957-2.219-11.456,0l-117.333,74.667C129.856,152.96,128,156.352,128,160s1.856,7.04,4.928,9.003
|
||||||
|
l18.219,11.605L5.184,268.181c-3.2,1.92-5.184,5.376-5.184,9.109c-0.021,3.733,1.92,7.211,5.12,9.152l245.333,149.333
|
||||||
|
c1.707,1.045,3.627,1.557,5.547,1.557c1.92,0,3.84-0.512,5.547-1.557L506.88,286.443c3.2-1.963,5.12-5.419,5.12-9.152
|
||||||
|
C511.979,273.557,510.016,270.101,506.816,268.181z M256,97.984L353.472,160L256,222.016L158.528,160L256,97.984z
|
||||||
|
M171.264,193.408l78.997,50.261c1.749,1.109,3.755,1.664,5.739,1.664c1.984,0,3.989-0.555,5.717-1.664l78.997-50.283
|
||||||
|
l14.059,8.448L256,264.704l-98.795-62.869L171.264,193.408z M31.296,277.355l105.472-63.253l99.371,63.232l-100.181,63.744
|
||||||
|
L31.296,277.355z M256,414.165l-99.733-60.715L256,289.984l99.733,63.467L256,414.165z M376.043,341.099l-100.181-63.765
|
||||||
|
l99.371-63.232l105.472,63.275L376.043,341.099z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -11,4 +11,5 @@ $light-gray: #d3d3d3;
|
|||||||
$blue-gray: #778899;
|
$blue-gray: #778899;
|
||||||
$cyan: #368f8b;
|
$cyan: #368f8b;
|
||||||
$dark-cyan: #376362;
|
$dark-cyan: #376362;
|
||||||
|
$light-cyan: #00b3b3;
|
||||||
$green: #09ad19;
|
$green: #09ad19;
|
@ -1,7 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="game-container">
|
<div class="game-container">
|
||||||
<GmUtilityWindow />
|
<GmTools />
|
||||||
<div class="top-ui"><Hud /></div>
|
<div class="top-ui" v-if="!zone.getEditorIsOpen">
|
||||||
|
<Hud />
|
||||||
|
</div>
|
||||||
|
|
||||||
<Game :config="gameConfig" class="game" @create="createGame">
|
<Game :config="gameConfig" class="game" @create="createGame">
|
||||||
<Scene name="main" @preload="preloadScene" @create="createScene" @play="playScene">
|
<Scene name="main" @preload="preloadScene" @create="createScene" @play="playScene">
|
||||||
@ -10,8 +12,13 @@
|
|||||||
</Game>
|
</Game>
|
||||||
|
|
||||||
<div class="bottom-ui">
|
<div class="bottom-ui">
|
||||||
<Chat />
|
<div v-if="!zone.getEditorIsOpen">
|
||||||
<Menubar />
|
<Chat />
|
||||||
|
<Menubar />
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<ZoneEditorToolbar />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -28,9 +35,12 @@ import Menubar from '@/components/game/Menu.vue'
|
|||||||
import { onUnmounted } from 'vue'
|
import { onUnmounted } from 'vue'
|
||||||
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import GmUtilityWindow from '@/components/utilities/gmTools/GmUtilityWindow.vue'
|
import GmTools from '@/components/utilities/GmTools.vue'
|
||||||
|
import { useZoneStore } from '@/stores/zone'
|
||||||
|
import ZoneEditorToolbar from '@/components/utilities/zoneEditor/ZoneEditorToolbar.vue'
|
||||||
|
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
const zone = useZoneStore()
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
socket.disconnectSocket()
|
socket.disconnectSocket()
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<TilemapLayerC v-if="zoneStore.isLoaded" :tilemap="tileMap" :tileset="zoneStore.getTiles" ref="tilemapLayer" :layerIndex="0" :cull-padding-x="10" :cull-padding-y="10" />
|
<TilemapLayerC v-if="zoneStore.isLoaded" :tilemap="tileMap" :tileset="zoneStore.getTiles" ref="tilemapLayer" :layerIndex="0" :cull-padding-x="10" :cull-padding-y="10" />
|
||||||
<Controls :layer="layer" />
|
<Controls :layer="layer" />
|
||||||
<Container v-if="zoneStore.isLoaded && !zoneEditorStore.isLoaded">
|
<!-- <Container v-if="zoneStore.isLoaded && !zoneEditorStore.isLoaded">-->
|
||||||
<Character :layer="layer" v-for="character in zoneStore.getCharacters" :key="character.id" :character="character" />
|
<!-- <Character :layer="layer" v-for="character in zoneStore.getCharacters" :key="character.id" :character="character" />-->
|
||||||
</Container>
|
<!-- </Container>-->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<Container>
|
<Container>
|
||||||
<Rectangle
|
<Rectangle
|
||||||
:x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x"
|
:x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y"
|
:y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:origin-x="0.5"
|
:origin-x="0.5"
|
||||||
:origin-y="10.5"
|
:origin-y="10.5"
|
||||||
:fillColor="0xFFFFFF"
|
:fillColor="0xFFFFFF"
|
||||||
@ -10,8 +10,8 @@
|
|||||||
:height="8"
|
:height="8"
|
||||||
>
|
>
|
||||||
<Rectangle
|
<Rectangle
|
||||||
:x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x"
|
:x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y"
|
:y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:origin-x="0.5"
|
:origin-x="0.5"
|
||||||
:origin-y="20.5"
|
:origin-y="20.5"
|
||||||
:fillColor="0x09ad19"
|
:fillColor="0x09ad19"
|
||||||
@ -22,8 +22,8 @@
|
|||||||
<Text
|
<Text
|
||||||
@create="createText"
|
@create="createText"
|
||||||
:text="props.character?.name"
|
:text="props.character?.name"
|
||||||
:x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x"
|
:x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y"
|
:y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:origin-x="0.5"
|
:origin-x="0.5"
|
||||||
:origin-y="4.5"
|
:origin-y="4.5"
|
||||||
:style="{
|
:style="{
|
||||||
@ -34,8 +34,8 @@
|
|||||||
/>
|
/>
|
||||||
<Sprite
|
<Sprite
|
||||||
ref="sprite"
|
ref="sprite"
|
||||||
:x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x"
|
:x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
:y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y"
|
:y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)"
|
||||||
play="walk" />
|
play="walk" />
|
||||||
</Container>
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
@ -45,7 +45,7 @@ import { Container, Rectangle, Sprite, Text, useScene } from 'phavuer'
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import { type Character as CharacterT } from '@/types'
|
import { type Character as CharacterT } from '@/types'
|
||||||
import { getTile, tileToWorldXY } from '@/services/zone'
|
import { getTile, tileToWorldX, tileToWorldXY, tileToWorldY } from '@/services/zone'
|
||||||
|
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<Image texture="waypoint" :x="waypoint.x" :y="waypoint.y" :visible="waypoint.visible" />
|
<Image texture="waypoint" :x="waypoint.x" :y="waypoint.y" :visible="waypoint.visible" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Image, useScene } from 'phavuer'
|
import { Image, useScene } from 'phavuer'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
|
import { getTile, tileToWorldXY } from '@/services/zone'
|
||||||
|
|
||||||
const scene = useScene()
|
const scene = useScene()
|
||||||
const pointer_tile = ref(undefined)
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
layer: Phaser.Tilemaps.TilemapLayer
|
layer: Phaser.Tilemaps.TilemapLayer
|
||||||
})
|
})
|
||||||
@ -21,34 +22,19 @@ function onPointerMove(pointer: Phaser.Input.Pointer) {
|
|||||||
const px = scene.cameras.main.worldView.x + pointer.x
|
const px = scene.cameras.main.worldView.x + pointer.x
|
||||||
const py = scene.cameras.main.worldView.y + pointer.y
|
const py = scene.cameras.main.worldView.y + pointer.y
|
||||||
|
|
||||||
pointer_tile.value = getTile(px, py, props.layer)
|
const pointer_tile = getTile(px, py, props.layer) as Phaser.Tilemaps.Tile
|
||||||
if (pointer_tile.value) {
|
if (!pointer_tile) {
|
||||||
waypoint.value.visible = true
|
|
||||||
|
|
||||||
// Convert tile coordinates to world coordinates
|
|
||||||
const worldPoint = props.layer.tileToWorldXY(pointer_tile.value.x, pointer_tile.value.y)
|
|
||||||
waypoint.value.x = worldPoint.x + config.tile_size.y
|
|
||||||
waypoint.value.y = worldPoint.y + config.tile_size.y + 15
|
|
||||||
} else {
|
|
||||||
waypoint.value.visible = false
|
waypoint.value.visible = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
waypoint.value.visible = true
|
||||||
|
|
||||||
|
// Convert tile coordinates to world coordinates
|
||||||
|
const worldPoint = tileToWorldXY(props.layer, pointer_tile.x, pointer_tile.y)
|
||||||
|
waypoint.value.x = worldPoint.position_x
|
||||||
|
waypoint.value.y = worldPoint.position_y + config.tile_size.y + 15
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
|
scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
|
||||||
|
|
||||||
function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
|
|
||||||
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
|
|
||||||
if (!tile) return undefined;
|
|
||||||
return tile
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTileAt(iX: number, iY: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
|
|
||||||
const tile: Phaser.Tilemaps.Tile = layer.getTileAt(iX, iY)
|
|
||||||
if (tile) return tile
|
|
||||||
else return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDepth(tile: Phaser.Tilemaps.Tile): number {
|
|
||||||
return 32
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import config from '@/config'
|
||||||
|
|
||||||
const tileWidth = 64;
|
const tileWidth = config.tile_size.x;
|
||||||
const tileHeight = 32;
|
const tileHeight = config.tile_size.y;
|
||||||
const tiles = ref([]);
|
const tiles = ref([]);
|
||||||
const selectedTile = ref(null);
|
const selectedTile = ref(null);
|
||||||
const tileCanvas = ref(null);
|
const tileCanvas = ref(null);
|
||||||
|
81
src/components/utilities/zoneEditor/ZoneEditorToolbar.vue
Normal file
81
src/components/utilities/zoneEditor/ZoneEditorToolbar.vue
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="tools">
|
||||||
|
<button :class="{ active: activeTool === 'tiles' }" @click="activeTool = 'tiles'">
|
||||||
|
<img src="/assets/icons/zoneEditor/tiles.svg" alt="Eraser tool" />
|
||||||
|
</button>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<button :class="{ active: activeTool === 'eraser' }" @click="activeTool = 'eraser'">
|
||||||
|
<img src="/assets/icons/zoneEditor/eraser-tool.svg" alt="Eraser tool" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="btn-cyan">Save</button>
|
||||||
|
<button class="btn-cyan">Load</button>
|
||||||
|
<button class="btn-cyan">Clear</button>
|
||||||
|
<button class="btn-cyan">Exit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const activeTool = ref('tiles');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '@/assets/scss/main';
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 5px;
|
||||||
|
opacity: 0.8;
|
||||||
|
display: flex;
|
||||||
|
background: $dark-gray;
|
||||||
|
border: 2px solid $cyan;
|
||||||
|
color: $light-gray;
|
||||||
|
padding: 5px;
|
||||||
|
min-width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 1px;
|
||||||
|
background: $cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical center
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-bottom: 3px solid $light-cyan;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: invert(1);
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -6,6 +6,16 @@ export function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLaye
|
|||||||
return tile
|
return tile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tileToWorldX(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number) {
|
||||||
|
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
|
||||||
|
return worldPoint.x + config.tile_size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tileToWorldY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number) {
|
||||||
|
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
|
||||||
|
return worldPoint.y
|
||||||
|
}
|
||||||
|
|
||||||
export function tileToWorldXY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number) {
|
export function tileToWorldXY(layer: Phaser.Tilemaps.TilemapLayer, pos_x: number, pos_y: number) {
|
||||||
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
|
const worldPoint = layer.tileToWorldXY(pos_x, pos_y)
|
||||||
const position_x = worldPoint.x + config.tile_size.y
|
const position_x = worldPoint.x + config.tile_size.y
|
||||||
|
@ -5,12 +5,14 @@ export const useZoneStore = defineStore('zone', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
loaded: false,
|
loaded: false,
|
||||||
tiles: undefined,
|
tiles: undefined,
|
||||||
characters: [] as Character[]
|
characters: [] as Character[],
|
||||||
|
editorIsOpen: true
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isLoaded: (state) => state.loaded,
|
isLoaded: (state) => state.loaded,
|
||||||
getTiles: (state) => state.tiles,
|
getTiles: (state) => state.tiles,
|
||||||
getCharacters: (state) => state.characters
|
getCharacters: (state) => state.characters,
|
||||||
|
getEditorIsOpen: (state) => state.editorIsOpen
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
loadTiles(tiles: any) {
|
loadTiles(tiles: any) {
|
||||||
@ -33,6 +35,9 @@ export const useZoneStore = defineStore('zone', {
|
|||||||
} else {
|
} else {
|
||||||
console.error(`Character with id ${character.id} not found`)
|
console.error(`Character with id ${character.id} not found`)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
setEditorIsOpen(isOpen: boolean) {
|
||||||
|
this.editorIsOpen = isOpen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user