From 58bff2010fb55b3b1d719559dc2f75bea52c3340 Mon Sep 17 00:00:00 2001 From: Dennis Postma <dennis@directonline.io> Date: Mon, 10 Jun 2024 17:46:25 +0200 Subject: [PATCH] Zone editor GUI (WIP), added a few helper functions --- .../assets/icons/zoneEditor/eraser-tool.svg | 1 + public/assets/icons/zoneEditor/tiles.svg | 48 +++++++++++ src/assets/scss/_variables.scss | 1 + src/components/Game.vue | 20 +++-- src/components/World.vue | 6 +- src/components/sprites/Character.vue | 18 ++--- src/components/utilities/Controls.vue | 38 +++------ .../GmUtilityWindow.vue => GmTools.vue} | 0 .../utilities/zoneEditor/ZoneEditor.vue | 5 +- .../zoneEditor/ZoneEditorToolbar.vue | 81 +++++++++++++++++++ src/services/zone.ts | 10 +++ src/stores/zone.ts | 9 ++- 12 files changed, 190 insertions(+), 47 deletions(-) create mode 100644 public/assets/icons/zoneEditor/eraser-tool.svg create mode 100644 public/assets/icons/zoneEditor/tiles.svg rename src/components/utilities/{gmTools/GmUtilityWindow.vue => GmTools.vue} (100%) create mode 100644 src/components/utilities/zoneEditor/ZoneEditorToolbar.vue diff --git a/public/assets/icons/zoneEditor/eraser-tool.svg b/public/assets/icons/zoneEditor/eraser-tool.svg new file mode 100644 index 0000000..c0e1bf9 --- /dev/null +++ b/public/assets/icons/zoneEditor/eraser-tool.svg @@ -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> \ No newline at end of file diff --git a/public/assets/icons/zoneEditor/tiles.svg b/public/assets/icons/zoneEditor/tiles.svg new file mode 100644 index 0000000..26cad8a --- /dev/null +++ b/public/assets/icons/zoneEditor/tiles.svg @@ -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> diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss index b66a71c..ad7825c 100644 --- a/src/assets/scss/_variables.scss +++ b/src/assets/scss/_variables.scss @@ -11,4 +11,5 @@ $light-gray: #d3d3d3; $blue-gray: #778899; $cyan: #368f8b; $dark-cyan: #376362; +$light-cyan: #00b3b3; $green: #09ad19; \ No newline at end of file diff --git a/src/components/Game.vue b/src/components/Game.vue index cb09aa3..c2ca608 100644 --- a/src/components/Game.vue +++ b/src/components/Game.vue @@ -1,7 +1,9 @@ <template> <div class="game-container"> - <GmUtilityWindow /> - <div class="top-ui"><Hud /></div> + <GmTools /> + <div class="top-ui" v-if="!zone.getEditorIsOpen"> + <Hud /> + </div> <Game :config="gameConfig" class="game" @create="createGame"> <Scene name="main" @preload="preloadScene" @create="createScene" @play="playScene"> @@ -10,8 +12,13 @@ </Game> <div class="bottom-ui"> - <Chat /> - <Menubar /> + <div v-if="!zone.getEditorIsOpen"> + <Chat /> + <Menubar /> + </div> + <div v-else> + <ZoneEditorToolbar /> + </div> </div> </div> </template> @@ -28,9 +35,12 @@ import Menubar from '@/components/game/Menu.vue' import { onUnmounted } from 'vue' import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.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 zone = useZoneStore() onUnmounted(() => { socket.disconnectSocket() diff --git a/src/components/World.vue b/src/components/World.vue index 8d01b5e..bbffa75 100644 --- a/src/components/World.vue +++ b/src/components/World.vue @@ -1,9 +1,9 @@ <template> <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" /> - <Container v-if="zoneStore.isLoaded && !zoneEditorStore.isLoaded"> - <Character :layer="layer" v-for="character in zoneStore.getCharacters" :key="character.id" :character="character" /> - </Container> +<!-- <Container v-if="zoneStore.isLoaded && !zoneEditorStore.isLoaded">--> +<!-- <Character :layer="layer" v-for="character in zoneStore.getCharacters" :key="character.id" :character="character" />--> +<!-- </Container>--> </template> <script setup lang="ts"> diff --git a/src/components/sprites/Character.vue b/src/components/sprites/Character.vue index def2b10..a5a4ea5 100644 --- a/src/components/sprites/Character.vue +++ b/src/components/sprites/Character.vue @@ -1,8 +1,8 @@ <template> <Container> <Rectangle - :x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x" - :y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y" + :x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)" + :y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)" :origin-x="0.5" :origin-y="10.5" :fillColor="0xFFFFFF" @@ -10,8 +10,8 @@ :height="8" > <Rectangle - :x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x" - :y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y" + :x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)" + :y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)" :origin-x="0.5" :origin-y="20.5" :fillColor="0x09ad19" @@ -22,8 +22,8 @@ <Text @create="createText" :text="props.character?.name" - :x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x" - :y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y" + :x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)" + :y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)" :origin-x="0.5" :origin-y="4.5" :style="{ @@ -34,8 +34,8 @@ /> <Sprite ref="sprite" - :x="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_x" - :y="tileToWorldXY(layer, props.character?.position_x, props.character?.position_y).position_y" + :x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)" + :y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)" play="walk" /> </Container> </template> @@ -45,7 +45,7 @@ import { Container, Rectangle, Sprite, Text, useScene } from 'phavuer' import { onMounted, ref } from 'vue' import { useSocketStore } from '@/stores/socket' import { type Character as CharacterT } from '@/types' -import { getTile, tileToWorldXY } from '@/services/zone' +import { getTile, tileToWorldX, tileToWorldXY, tileToWorldY } from '@/services/zone' const socket = useSocketStore() diff --git a/src/components/utilities/Controls.vue b/src/components/utilities/Controls.vue index b8221af..875a4c9 100644 --- a/src/components/utilities/Controls.vue +++ b/src/components/utilities/Controls.vue @@ -1,13 +1,14 @@ <template> <Image texture="waypoint" :x="waypoint.x" :y="waypoint.y" :visible="waypoint.visible" /> </template> + <script setup lang="ts"> import { Image, useScene } from 'phavuer' import { ref } from 'vue' import config from '@/config' +import { getTile, tileToWorldXY } from '@/services/zone' const scene = useScene() -const pointer_tile = ref(undefined) const props = defineProps({ layer: Phaser.Tilemaps.TilemapLayer }) @@ -21,34 +22,19 @@ function onPointerMove(pointer: Phaser.Input.Pointer) { const px = scene.cameras.main.worldView.x + pointer.x const py = scene.cameras.main.worldView.y + pointer.y - pointer_tile.value = getTile(px, py, props.layer) - if (pointer_tile.value) { - 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 { + const pointer_tile = getTile(px, py, props.layer) as Phaser.Tilemaps.Tile + if (!pointer_tile) { 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) - -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> diff --git a/src/components/utilities/gmTools/GmUtilityWindow.vue b/src/components/utilities/GmTools.vue similarity index 100% rename from src/components/utilities/gmTools/GmUtilityWindow.vue rename to src/components/utilities/GmTools.vue diff --git a/src/components/utilities/zoneEditor/ZoneEditor.vue b/src/components/utilities/zoneEditor/ZoneEditor.vue index 3a9b539..ce6c7e4 100644 --- a/src/components/utilities/zoneEditor/ZoneEditor.vue +++ b/src/components/utilities/zoneEditor/ZoneEditor.vue @@ -16,9 +16,10 @@ <script setup lang="ts"> import { ref, onMounted } from 'vue'; +import config from '@/config' -const tileWidth = 64; -const tileHeight = 32; +const tileWidth = config.tile_size.x; +const tileHeight = config.tile_size.y; const tiles = ref([]); const selectedTile = ref(null); const tileCanvas = ref(null); diff --git a/src/components/utilities/zoneEditor/ZoneEditorToolbar.vue b/src/components/utilities/zoneEditor/ZoneEditorToolbar.vue new file mode 100644 index 0000000..6187f16 --- /dev/null +++ b/src/components/utilities/zoneEditor/ZoneEditorToolbar.vue @@ -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> \ No newline at end of file diff --git a/src/services/zone.ts b/src/services/zone.ts index 793ed13..3eb7203 100644 --- a/src/services/zone.ts +++ b/src/services/zone.ts @@ -6,6 +6,16 @@ export function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLaye 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) { const worldPoint = layer.tileToWorldXY(pos_x, pos_y) const position_x = worldPoint.x + config.tile_size.y diff --git a/src/stores/zone.ts b/src/stores/zone.ts index c3de41d..9e9b61f 100644 --- a/src/stores/zone.ts +++ b/src/stores/zone.ts @@ -5,12 +5,14 @@ export const useZoneStore = defineStore('zone', { state: () => ({ loaded: false, tiles: undefined, - characters: [] as Character[] + characters: [] as Character[], + editorIsOpen: true }), getters: { isLoaded: (state) => state.loaded, getTiles: (state) => state.tiles, - getCharacters: (state) => state.characters + getCharacters: (state) => state.characters, + getEditorIsOpen: (state) => state.editorIsOpen }, actions: { loadTiles(tiles: any) { @@ -33,6 +35,9 @@ export const useZoneStore = defineStore('zone', { } else { console.error(`Character with id ${character.id} not found`) } + }, + setEditorIsOpen(isOpen: boolean) { + this.editorIsOpen = isOpen } } })