Worked on zone manager
This commit is contained in:
parent
399e994bbe
commit
8e174a063a
2
package-lock.json
generated
2
package-lock.json
generated
@ -1221,6 +1221,7 @@
|
|||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||||
|
"deprecated": "Use @eslint/config-array instead",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1274,6 +1275,7 @@
|
|||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
||||||
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
||||||
|
"deprecated": "Use @eslint/object-schema instead",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<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>
|
|
Before Width: | Height: | Size: 875 B |
44
public/assets/icons/zoneEditor/eraser.svg
Normal file
44
public/assets/icons/zoneEditor/eraser.svg
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?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="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 469.333 469.333" style="enable-background:new 0 0 469.333 469.333;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M456.833,172.237L318.167,33.439c-8.061-8.068-19.109-12.103-30.159-12.105c-11.055-0.002-22.11,4.033-30.175,12.105
|
||||||
|
L12.5,279.006C4.437,287.076,0,297.794,0,309.201c0,11.407,4.406,22.094,12.594,30.289l95.51,93.318
|
||||||
|
c10.021,9.791,23.25,15.192,37.271,15.192h71.771c14.115,0,27.417-5.464,37.479-15.4l202.208-199.972
|
||||||
|
c8.063-8.07,12.5-18.789,12.5-30.195S464.896,180.308,456.833,172.237z M224.656,402.25c-2.052,2.021-4.646,3.083-7.51,3.083
|
||||||
|
h-71.771c-2.844,0-5.417-1.042-7.458-3.042l-95.25-92.958l110.708-110.708l137.844,137.854L224.656,402.25z"/>
|
||||||
|
</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.1 KiB |
78
public/assets/icons/zoneEditor/move.svg
Normal file
78
public/assets/icons/zoneEditor/move.svg
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?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 492.009 492.009" style="enable-background:new 0 0 492.009 492.009;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M314.343,62.977L255.399,4.033c-2.672-2.672-6.236-4.04-9.92-4.032c-3.752-0.036-7.396,1.36-10.068,4.032l-57.728,57.728
|
||||||
|
c-5.408,5.408-5.408,14.2,0,19.604l7.444,7.444c5.22,5.22,14.332,5.22,19.556,0l22.1-22.148v81.388
|
||||||
|
c0,0.248,0.144,0.452,0.188,0.684c0.6,7.092,6.548,12.704,13.8,12.704h10.52c7.644,0,13.928-6.208,13.928-13.852v-9.088
|
||||||
|
c0-0.04,0-0.068,0-0.1V67.869l22.108,22.152c5.408,5.408,14.18,5.408,19.584,0l7.432-7.436
|
||||||
|
C319.751,77.173,319.751,68.377,314.343,62.977z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M314.335,409.437l-7.44-7.456c-5.22-5.228-14.336-5.228-19.564,0l-22.108,22.152v-70.216c0-0.04,0-0.064,0-0.1v-9.088
|
||||||
|
c0-7.648-6.288-14.16-13.924-14.16h-10.528c-7.244,0-13.192,5.756-13.796,12.856c-0.044,0.236-0.188,0.596-0.188,0.84v81.084
|
||||||
|
l-22.1-22.148c-5.224-5.224-14.356-5.224-19.58,0l-7.44,7.444c-5.4,5.404-5.392,14.2,0.016,19.608l57.732,57.724
|
||||||
|
c2.604,2.612,6.08,4.032,9.668,4.032h0.52c3.716,0,7.184-1.416,9.792-4.032l58.94-58.94
|
||||||
|
C319.743,423.633,319.743,414.841,314.335,409.437z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M147.251,226.781l-1.184,0h-7.948c-0.028,0-0.056,0-0.088,0h-69.88l22.152-22.032c2.612-2.608,4.048-6.032,4.048-9.74
|
||||||
|
c0-3.712-1.436-7.164-4.048-9.768l-7.444-7.428c-5.408-5.408-14.204-5.4-19.604,0.008l-58.944,58.94
|
||||||
|
c-2.672,2.668-4.1,6.248-4.028,9.92c-0.076,3.82,1.356,7.396,4.028,10.068l57.728,57.732c2.704,2.704,6.252,4.056,9.804,4.056
|
||||||
|
s7.1-1.352,9.804-4.056l7.44-7.44c2.612-2.608,4.052-6.092,4.052-9.8c0-3.712-1.436-7.232-4.052-9.836l-22.144-22.184h80.728
|
||||||
|
c0.244,0,0.644-0.06,0.876-0.104c7.096-0.6,12.892-6.468,12.892-13.716v-10.536C161.439,233.229,154.895,226.781,147.251,226.781z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M487.695,236.765l-58.944-58.936c-5.404-5.408-14.2-5.408-19.604,0l-7.436,7.444c-2.612,2.604-4.052,6.088-4.052,9.796
|
||||||
|
c0,3.712,1.436,7.072,4.052,9.68l22.148,22.032h-70.328c-0.036,0-0.064,0-0.096,0h-9.084c-7.644,0-13.78,6.444-13.78,14.084
|
||||||
|
v10.536c0,7.248,5.564,13.108,12.664,13.712c0.236,0.048,0.408,0.108,0.648,0.108h81.188l-22.156,22.18
|
||||||
|
c-2.608,2.604-4.048,6.116-4.048,9.816c0,3.716,1.436,7.208,4.048,9.816l7.448,7.444c2.7,2.704,6.248,4.06,9.8,4.06
|
||||||
|
s7.096-1.352,9.8-4.056l57.736-57.732c2.664-2.664,4.092-6.244,4.028-9.92C491.787,243.009,490.359,239.429,487.695,236.765z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M246.011,207.541c-21.204,0-38.456,17.252-38.456,38.46c0,21.204,17.252,38.46,38.456,38.46
|
||||||
|
c21.204,0,38.46-17.256,38.46-38.46C284.471,224.793,267.215,207.541,246.011,207.541z"/>
|
||||||
|
</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: 3.0 KiB |
@ -1,18 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- 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"
|
<svg version="1.1" id="Capa_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">
|
viewBox="0 0 443.733 443.733" style="enable-background:new 0 0 443.733 443.733;" xml:space="preserve">
|
||||||
<g>
|
<g>
|
||||||
<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
|
<path d="M187.733,0H17.067C7.641,0,0,7.641,0,17.067v170.667c0,9.426,7.641,17.067,17.067,17.067h170.667
|
||||||
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
|
c9.426,0,17.067-7.641,17.067-17.067V17.067C204.8,7.641,197.159,0,187.733,0z"/>
|
||||||
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
|
</g>
|
||||||
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
|
</g>
|
||||||
C511.979,273.557,510.016,270.101,506.816,268.181z M256,97.984L353.472,160L256,222.016L158.528,160L256,97.984z
|
<g>
|
||||||
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
|
<g>
|
||||||
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
|
<path d="M426.667,0H256c-9.426,0-17.067,7.641-17.067,17.067v170.667c0,9.426,7.641,17.067,17.067,17.067h170.667
|
||||||
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
|
c9.426,0,17.067-7.641,17.067-17.067V17.067C443.733,7.641,436.092,0,426.667,0z"/>
|
||||||
l99.371-63.232l105.472,63.275L376.043,341.099z"/>
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M187.733,238.933H17.067C7.641,238.933,0,246.574,0,256v170.667c0,9.426,7.641,17.067,17.067,17.067h170.667
|
||||||
|
c9.426,0,17.067-7.641,17.067-17.067V256C204.8,246.574,197.159,238.933,187.733,238.933z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M426.667,238.933H256c-9.426,0-17.067,7.641-17.067,17.067v170.667c0,9.426,7.641,17.067,17.067,17.067h170.667
|
||||||
|
c9.426,0,17.067-7.641,17.067-17.067V256C443.733,246.574,436.092,238.933,426.667,238.933z"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
@ -18,7 +18,8 @@ import Game from '@/components/Game.vue'
|
|||||||
const screen: Ref<string> = ref('login')
|
const screen: Ref<string> = ref('login')
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
|
||||||
socket.$subscribe((mutation, state) => {
|
socket.$subscribe(
|
||||||
|
(mutation, state) => {
|
||||||
if (!state.connection) {
|
if (!state.connection) {
|
||||||
screen.value = 'login'
|
screen.value = 'login'
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,34 @@ body {
|
|||||||
user-select: none; /* Standard syntax */
|
user-select: none; /* Standard syntax */
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, button, a {
|
h1,
|
||||||
font-family: "Poppins", serif;
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
font-family: 'Poppins', serif;
|
||||||
color: $white;
|
color: $white;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
p, span, li, label {
|
p,
|
||||||
font-family: "Inter", serif;
|
span,
|
||||||
|
li,
|
||||||
|
label {
|
||||||
|
font-family: 'Inter', serif;
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
button, a {
|
button,
|
||||||
|
a {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-shadow: 0 4px 6px rgba($black, 0.25);
|
text-shadow: 0 4px 6px rgba($black, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
button, input {
|
button,
|
||||||
|
input {
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="game-container">
|
<div class="game-container">
|
||||||
|
<Game class="game" :config="gameConfig" @create="createGame">
|
||||||
|
<Scene name="main" @preload="preloadScene" @create="createScene">
|
||||||
<GmTools />
|
<GmTools />
|
||||||
<div class="top-ui" v-if="!zone.getEditorIsOpen">
|
<div v-if="!zoneEditorStore.isActive">
|
||||||
<Hud />
|
<div class="top-ui"><Hud /></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Game :config="gameConfig" class="game" @create="createGame">
|
|
||||||
<Scene name="main" @preload="preloadScene" @create="createScene" @play="playScene">
|
|
||||||
<World />
|
<World />
|
||||||
</Scene>
|
<div class="bottom-ui"><Chat /> <Menubar /></div>
|
||||||
</Game>
|
|
||||||
|
|
||||||
<div class="bottom-ui">
|
|
||||||
<div v-if="!zone.getEditorIsOpen">
|
|
||||||
<Chat />
|
|
||||||
<Menubar />
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<ZoneEditorToolbar />
|
<ZoneEditor />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Scene>
|
||||||
|
</Game>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -27,25 +20,28 @@
|
|||||||
import 'phaser'
|
import 'phaser'
|
||||||
import { Game, Scene } from 'phavuer'
|
import { Game, Scene } from 'phavuer'
|
||||||
import World from '@/components/World.vue'
|
import World from '@/components/World.vue'
|
||||||
import Pointer = Phaser.Input.Pointer
|
import Hud from '@/components/gui/Hud.vue'
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import Chat from '@/components/gui/Chat.vue'
|
||||||
import Hud from '@/components/game/Hud.vue'
|
import Menubar from '@/components/gui/Menu.vue'
|
||||||
import Chat from '@/components/game/Chat.vue'
|
|
||||||
import Menubar from '@/components/game/Menu.vue'
|
|
||||||
import { onUnmounted } from 'vue'
|
import { onUnmounted } from 'vue'
|
||||||
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
|
||||||
import GmTools from '@/components/utilities/GmTools.vue'
|
import GmTools from '@/components/utilities/GmTools.vue'
|
||||||
import { useZoneStore } from '@/stores/zone'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import ZoneEditorToolbar from '@/components/utilities/zoneEditor/ZoneEditorToolbar.vue'
|
import { useZoneEditorStore } from '@/stores/zoneEditor'
|
||||||
|
import Toolbar from '@/components/utilities/zoneEditor/Toolbar.vue'
|
||||||
|
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
||||||
|
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
const zone = useZoneStore()
|
const zoneEditorStore = useZoneEditorStore()
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
socket.disconnectSocket()
|
socket.disconnectSocket()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// on page close
|
||||||
|
addEventListener('beforeunload', () => {
|
||||||
|
socket.disconnectSocket()
|
||||||
|
})
|
||||||
|
|
||||||
const gameConfig = {
|
const gameConfig = {
|
||||||
name: 'New Quest',
|
name: 'New Quest',
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
@ -55,7 +51,7 @@ const gameConfig = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createGame = (game: Phaser.Game) => {
|
const createGame = (game: Phaser.Game) => {
|
||||||
window.addEventListener('resize', () => {
|
addEventListener('resize', () => {
|
||||||
game.scale.resize(window.innerWidth, window.innerHeight)
|
game.scale.resize(window.innerWidth, window.innerHeight)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -75,25 +71,23 @@ const preloadScene = (scene: Phaser.Scene) => {
|
|||||||
scene.load.spritesheet('characterW', '/assets/avatar/default/walk.png', { frameWidth: 36, frameHeight: 94 })
|
scene.load.spritesheet('characterW', '/assets/avatar/default/walk.png', { frameWidth: 36, frameHeight: 94 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const playScene = (scene: Phaser.Scene) => {
|
const playScene = (scene: Phaser.Scene) => {}
|
||||||
}
|
|
||||||
|
|
||||||
const createScene = (scene: Phaser.Scene) => {
|
const createScene = (scene: Phaser.Scene) => {
|
||||||
// Camera drag system
|
// Camera drag system
|
||||||
let cam = scene.cameras.main
|
let cam = scene.cameras.main
|
||||||
scene.input.on('pointermove', function (pointer: Pointer) {
|
scene.input.on('pointermove', function (pointer: Phaser.Input.Pointer) {
|
||||||
if (!pointer.isDown) return
|
if (!pointer.isDown) return
|
||||||
cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom
|
cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom
|
||||||
cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom
|
cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom
|
||||||
})
|
})
|
||||||
|
|
||||||
scene.anims.create({
|
scene.anims.create({
|
||||||
key: "walk",
|
key: 'walk',
|
||||||
frameRate: 7,
|
frameRate: 7,
|
||||||
frames: scene.anims.generateFrameNumbers("characterW", { start: 0, end: 3 }),
|
frames: scene.anims.generateFrameNumbers('characterW', { start: 0, end: 3 }),
|
||||||
repeat: -1,
|
repeat: -1
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
// const grid = scene.add.grid(0, 0, window.innerWidth, window.innerHeight, 64, 32, 0, 0, 0xff0000, 0.5).setOrigin(0, 0);
|
// const grid = scene.add.grid(0, 0, window.innerWidth, window.innerHeight, 64, 32, 0, 0, 0xff0000, 0.5).setOrigin(0, 0);
|
||||||
//
|
//
|
||||||
|
@ -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 :tilemap="tileMap" :tileset="zoneStore.tiles" 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>
|
||||||
<!-- <Character :layer="layer" v-for="character in zoneStore.getCharacters" :key="character.id" :character="character" />-->
|
<Character :layer="layer" v-for="character in zoneStore.characters" :key="character.id" :character="character" />
|
||||||
<!-- </Container>-->
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -13,11 +13,10 @@ import TilemapLayer = Phaser.Tilemaps.TilemapLayer
|
|||||||
import { Container, TilemapLayer as TilemapLayerC, useScene } from 'phavuer'
|
import { Container, TilemapLayer as TilemapLayerC, useScene } from 'phavuer'
|
||||||
import Character from '@/components/sprites/Character.vue'
|
import Character from '@/components/sprites/Character.vue'
|
||||||
import { type Character as CharacterType } from '@/types'
|
import { type Character as CharacterType } from '@/types'
|
||||||
import { onBeforeMount, ref, type Ref, watch } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, ref, type Ref, watch } from 'vue'
|
||||||
import Controls from '@/components/utilities/Controls.vue'
|
import Controls from '@/components/utilities/Controls.vue'
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import { useZoneStore } from '@/stores/zone'
|
import { useZoneStore } from '@/stores/zone'
|
||||||
import { useZoneEditorStore } from '@/stores/zoneEditor'
|
|
||||||
|
|
||||||
// Phavuer logic
|
// Phavuer logic
|
||||||
let scene = useScene()
|
let scene = useScene()
|
||||||
@ -41,44 +40,53 @@ scene.cameras.main.centerOn(centerX, centerY)
|
|||||||
|
|
||||||
// Multiplayer / server logics
|
// Multiplayer / server logics
|
||||||
const zoneStore = useZoneStore()
|
const zoneStore = useZoneStore()
|
||||||
const zoneEditorStore = useZoneEditorStore()
|
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
|
||||||
// Watch for changes in the zoneStore and update the layer
|
// Watch for changes in the zoneStore and update the layer
|
||||||
watch(() => zoneStore.tiles, () => {
|
watch(
|
||||||
|
() => zoneStore.tiles,
|
||||||
|
() => {
|
||||||
// @TODO : change to tiles for when loading other maps
|
// @TODO : change to tiles for when loading other maps
|
||||||
zoneStore.getTiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y)))
|
zoneStore.tiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y)))
|
||||||
}, { deep: true }
|
},
|
||||||
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load the zone from the server
|
// Load the zone from the server
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
socket.getConnection.emit('character:zone:load', {zoneId: socket.character.zoneId})
|
socket.connection.emit('character:zone:load', { zoneId: socket.character.zoneId })
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen for the zone event from the server and load the zone
|
// Listen for the zone event from the server and load the zone
|
||||||
socket.getConnection.on('character:zone:load', (data) => {
|
socket.connection.on('character:zone:load', (data) => {
|
||||||
console.log('character:zone:load', data)
|
console.log('character:zone:load', data)
|
||||||
zoneStore.loadTiles(data.zone.tiles)
|
zoneStore.setTiles(data.zone.tiles)
|
||||||
|
|
||||||
let characters = data.characters;
|
let characters = data.characters
|
||||||
zoneStore.setCharacters(characters);
|
zoneStore.setCharacters(characters)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen for player join events
|
// Listen for player join events
|
||||||
socket.getConnection.on('zone:character:join', (data: CharacterType) => {
|
socket.connection.on('zone:character:join', (data: CharacterType) => {
|
||||||
console.log('character:zone:join', data)
|
console.log('character:zone:join', data)
|
||||||
zoneStore.addCharacter(data)
|
zoneStore.addCharacter(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen for user:disconnect
|
// Listen for user:disconnect
|
||||||
socket.getConnection.on('user:disconnect', (data: CharacterType) => {
|
socket.connection.on('user:disconnect', (data: CharacterType) => {
|
||||||
zoneStore.removeCharacter(data)
|
zoneStore.removeCharacter(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.getConnection.on('character:moved', (data: CharacterType) => {
|
socket.connection.on('character:moved', (data: CharacterType) => {
|
||||||
console.log('character:moved', data)
|
console.log('character:moved', data)
|
||||||
zoneStore.updateCharacter(data);
|
zoneStore.updateCharacter(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
socket.connection.off('character:zone:load')
|
||||||
|
socket.connection.off('zone:character:join')
|
||||||
|
socket.connection.off('user:disconnect')
|
||||||
|
socket.connection.off('character:moved')
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,16 +6,16 @@
|
|||||||
<div class="hud">
|
<div class="hud">
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="player-details">
|
<div class="player-details">
|
||||||
<span class="player-name">{{ socket.getCharacter.name }}</span>
|
<span class="player-name">{{ socket.character.name }}</span>
|
||||||
<span class="player-lvl">lvl. {{ socket.getCharacter.level }}</span>
|
<span class="player-lvl">lvl. {{ socket.character.level }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bar">
|
<div class="bar">
|
||||||
<label for="hp">HP</label>
|
<label for="hp">HP</label>
|
||||||
<progress id="hp" :value="socket.getCharacter.hitpoints" max="100">{{ socket.getCharacter.hitpoints }}%</progress>
|
<progress id="hp" :value="socket.character.hitpoints" max="100">{{ socket.character.hitpoints }}%</progress>
|
||||||
</div>
|
</div>
|
||||||
<div class="bar">
|
<div class="bar">
|
||||||
<label for="mp">MP</label>
|
<label for="mp">MP</label>
|
||||||
<progress id="mp" :value="socket.getCharacter.mana" max="100">{{ socket.getCharacter.mana }}%</progress>
|
<progress id="mp" :value="socket.character.mana" max="100">{{ socket.character.mana }}%</progress>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
|
|
||||||
const socket = useSocketStore();
|
const socket = useSocketStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -35,7 +35,8 @@ const socket = useSocketStore();
|
|||||||
position: relative;
|
position: relative;
|
||||||
left: -32px;
|
left: -32px;
|
||||||
|
|
||||||
.hud, &::before {
|
.hud,
|
||||||
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 32px;
|
top: 32px;
|
||||||
left: 32px;
|
left: 32px;
|
||||||
@ -74,12 +75,14 @@ const socket = useSocketStore();
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 20px 0 50px;
|
padding: 0 20px 0 50px;
|
||||||
|
|
||||||
.player-details, .bar {
|
.player-details,
|
||||||
|
.bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
span, label {
|
span,
|
||||||
|
label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.player-name {
|
.player-name {
|
||||||
@ -105,18 +108,40 @@ const socket = useSocketStore();
|
|||||||
&#hp {
|
&#hp {
|
||||||
accent-color: $red;
|
accent-color: $red;
|
||||||
// Chrome, Safari, Edge, Opera
|
// Chrome, Safari, Edge, Opera
|
||||||
&::-webkit-progress-value { background: $red; border-radius: 8px; }
|
&::-webkit-progress-value {
|
||||||
&::-webkit-progress-bar { background: $white; border-radius: 8px; border: 2px solid $white; }
|
background: $red;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
background: $white;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid $white;
|
||||||
|
}
|
||||||
// Firefox
|
// Firefox
|
||||||
&::-moz-progress-bar { background: $red; border-radius: 8px; border: 2px solid $white;}
|
&::-moz-progress-bar {
|
||||||
|
background: $red;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid $white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&#mp {
|
&#mp {
|
||||||
accent-color: $light-blue;
|
accent-color: $light-blue;
|
||||||
// Chrome, Safari, Edge, Opera
|
// Chrome, Safari, Edge, Opera
|
||||||
&::-webkit-progress-value { background: $light-blue; border-radius: 8px; }
|
&::-webkit-progress-value {
|
||||||
&::-webkit-progress-bar { background: $white; border-radius: 8px; border: 2px solid $white;}
|
background: $light-blue;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
background: $white;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid $white;
|
||||||
|
}
|
||||||
// Firefox
|
// Firefox
|
||||||
&::-moz-progress-bar { background: $light-blue; border-radius: 8px; border: 2px solid $white;}
|
&::-moz-progress-bar {
|
||||||
|
background: $light-blue;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid $white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@
|
|||||||
<label :for="character.id">{{ character.name }}</label>
|
<label :for="character.id">{{ character.name }}</label>
|
||||||
<!-- @TODO : Add a confirmation dialog -->
|
<!-- @TODO : Add a confirmation dialog -->
|
||||||
<button class="delete" @click="delete_character(character.id)">
|
<button class="delete" @click="delete_character(character.id)">
|
||||||
<img draggable="false" src="/assets/icons/trashcan.svg">
|
<img draggable="false" src="/assets/icons/trashcan.svg" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="sprite-container">
|
<div class="sprite-container">
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<div class="button-wrapper" v-if="!isLoading">
|
<div class="button-wrapper" v-if="!isLoading">
|
||||||
<button class="btn-cyan" :disabled="!selected_character" @click="select_character()">
|
<button class="btn-cyan" :disabled="!selected_character" @click="select_character()">
|
||||||
PLAY
|
PLAY
|
||||||
<img draggable="false" src="/assets/icons/arrow.svg">
|
<img draggable="false" src="/assets/icons/arrow.svg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,51 +60,57 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import { onBeforeMount, onMounted, ref } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import { type Character as CharacterT } from '@/types'
|
import { type Character as CharacterT } from '@/types'
|
||||||
|
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const characters = ref([])
|
const characters = ref([])
|
||||||
const socket = useSocketStore();
|
const socket = useSocketStore()
|
||||||
|
|
||||||
// Fetch characters
|
// Fetch characters
|
||||||
socket.getConnection.on('character:list', (data: any) => {
|
socket.connection.on('character:list', (data: any) => {
|
||||||
characters.value = data
|
characters.value = data
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// wait 1.5 sec
|
// wait 1.5 sec
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
socket.getConnection.emit('character:list')
|
socket.connection.emit('character:list')
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}, 1000)
|
}, 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
// Select character logics
|
// Select character logics
|
||||||
const selected_character = ref(null)
|
const selected_character = ref(null)
|
||||||
function select_character() {
|
function select_character() {
|
||||||
if (!selected_character.value) return
|
if (!selected_character.value) return
|
||||||
socket.getConnection.emit('character:connect', { character_id: selected_character.value })
|
socket.connection.emit('character:connect', { character_id: selected_character.value })
|
||||||
socket.getConnection.on('character:connect', (data: CharacterT) => socket.setCharacter(data))
|
socket.connection.on('character:connect', (data: CharacterT) => socket.setCharacter(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete character logics
|
// Delete character logics
|
||||||
function delete_character(character_id: number) {
|
function delete_character(character_id: number) {
|
||||||
if (!character_id) return
|
if (!character_id) return
|
||||||
socket.getConnection.emit('character:delete', { character_id: character_id })
|
socket.connection.emit('character:delete', { character_id: character_id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create character logics
|
// Create character logics
|
||||||
const isModalOpen = ref(false)
|
const isModalOpen = ref(false)
|
||||||
const name = ref('')
|
const name = ref('')
|
||||||
function create() {
|
function create() {
|
||||||
socket.getConnection.on('character:create:success', (data: CharacterT) => {
|
socket.connection.on('character:create:success', (data: CharacterT) => {
|
||||||
socket.setCharacter(data)
|
socket.setCharacter(data)
|
||||||
isModalOpen.value = false
|
isModalOpen.value = false
|
||||||
})
|
})
|
||||||
socket.getConnection.emit('character:create', { name: name.value })
|
socket.connection.emit('character:create', { name: name.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
socket.connection.off('character:list')
|
||||||
|
socket.connection.off('character:connect')
|
||||||
|
socket.connection.off('character:create:success')
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -30,14 +30,12 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { login, register } from '@/services/authentication'
|
import { login, register } from '@/services/authentication'
|
||||||
import { useNotificationStore } from '@/stores/notifications'
|
import { useNotificationStore } from '@/stores/notifications'
|
||||||
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
|
|
||||||
const bgm = ref('bgm')
|
const bgm = ref('bgm')
|
||||||
if (bgm.value.paused) {
|
if (bgm.value.paused) {
|
||||||
window.addEventListener('click', () => bgm.value.play())
|
addEventListener('click', () => bgm.value.play())
|
||||||
window.addEventListener('keydown', () => bgm.value.play())
|
addEventListener('keydown', () => bgm.value.play())
|
||||||
}
|
}
|
||||||
|
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
@ -61,7 +59,7 @@ async function loginFunc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.setToken(response.token)
|
socket.setToken(response.token)
|
||||||
socket.initConnection();
|
socket.initConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerFunc() {
|
async function registerFunc() {
|
||||||
@ -80,7 +78,7 @@ async function registerFunc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.setToken(response.token)
|
socket.setToken(response.token)
|
||||||
socket.initConnection();
|
socket.initConnection()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,23 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<Container>
|
<Container>
|
||||||
<Rectangle
|
<Rectangle :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" :width="74" :height="8">
|
||||||
:x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)"
|
<Rectangle :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" :width="70" :height="4" />
|
||||||
:y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)"
|
|
||||||
:origin-x="0.5"
|
|
||||||
:origin-y="10.5"
|
|
||||||
:fillColor="0xFFFFFF"
|
|
||||||
:width="74"
|
|
||||||
:height="8"
|
|
||||||
>
|
|
||||||
<Rectangle
|
|
||||||
: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"
|
|
||||||
:width="70"
|
|
||||||
:height="4"
|
|
||||||
/>
|
|
||||||
</Rectangle>
|
</Rectangle>
|
||||||
<Text
|
<Text
|
||||||
@create="createText"
|
@create="createText"
|
||||||
@ -29,20 +13,16 @@
|
|||||||
:style="{
|
:style="{
|
||||||
fontFamily: 'Helvetica, Arial',
|
fontFamily: 'Helvetica, Arial',
|
||||||
color: '#FFF',
|
color: '#FFF',
|
||||||
fontSize: '14px',
|
fontSize: '14px'
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<Sprite
|
<Sprite ref="sprite" :x="tileToWorldX(layer, props.character?.position_x, props.character?.position_y)" :y="tileToWorldY(layer, props.character?.position_x, props.character?.position_y)" play="walk" />
|
||||||
ref="sprite"
|
|
||||||
: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>
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Container, Rectangle, Sprite, Text, useScene } from 'phavuer'
|
import { Container, Rectangle, Sprite, Text, useScene } from 'phavuer'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onBeforeMount, 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, tileToWorldX, tileToWorldXY, tileToWorldY } from '@/services/zone'
|
import { getTile, tileToWorldX, tileToWorldXY, tileToWorldY } from '@/services/zone'
|
||||||
@ -55,33 +35,32 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const scene = useScene()
|
const scene = useScene()
|
||||||
const isSelf = props.character.id === socket.character.id;
|
const isSelf = props.character.id === socket.character.id
|
||||||
|
|
||||||
const createText = (text: Phaser.GameObjects.Text) => {
|
const createText = (text: Phaser.GameObjects.Text) => {
|
||||||
text.setLetterSpacing(1.5);
|
text.setLetterSpacing(1.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onBeforeMount(() => {
|
||||||
if (isSelf) setupSelf()
|
if (isSelf) setupSelf()
|
||||||
});
|
})
|
||||||
|
|
||||||
function setupSelf()
|
function setupSelf() {
|
||||||
{
|
|
||||||
scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerClick)
|
scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPointerClick(pointer: Phaser.Input.Pointer) {
|
function onPointerClick(pointer: Phaser.Input.Pointer) {
|
||||||
if (!isSelf) return;
|
if (!isSelf) return
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
const pointer_tile = getTile(px, py, props.layer) as Phaser.Tilemaps.Tile
|
const pointer_tile = getTile(px, py, props.layer) as Phaser.Tilemaps.Tile
|
||||||
if (!pointer_tile) {
|
if (!pointer_tile) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.getConnection.emit('character:move', { position_x: pointer_tile.x, position_y: pointer_tile.y })
|
socket.connection.emit('character:move', { position_x: pointer_tile.x, position_y: pointer_tile.y })
|
||||||
|
|
||||||
//Directions for player sprites + animations
|
//Directions for player sprites + animations
|
||||||
if (px < 0 && py > 0) {
|
if (px < 0 && py > 0) {
|
||||||
@ -95,9 +74,6 @@ function onPointerClick(pointer: Phaser.Input.Pointer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// spacing
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resources:
|
* Resources:
|
||||||
* https://www.youtube.com/watch?v=9sWrGohw9qo
|
* https://www.youtube.com/watch?v=9sWrGohw9qo
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Image, useScene } from 'phavuer'
|
import { Image, useScene } from 'phavuer'
|
||||||
import { ref } from 'vue'
|
import { onBeforeUnmount, ref } from 'vue'
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
import { getTile, tileToWorldXY } from '@/services/zone'
|
import { getTile, tileToWorldXY } from '@/services/zone'
|
||||||
|
|
||||||
@ -37,4 +37,8 @@ function onPointerMove(pointer: Phaser.Input.Pointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
|
scene.input.on(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
scene.input.off(Phaser.Input.Events.POINTER_MOVE, onPointerMove)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal :isModalOpen="true" :closable="false" :modal-width="200" :modal-height="260">
|
<Modal :isModalOpen="true" :closable="false" :is-resizable="false" :modal-width="200" :modal-height="260">
|
||||||
<template #modalHeader>
|
<template #modalHeader>
|
||||||
<h3 class="modal-title">GM tools</h3>
|
<h3 class="modal-title">GM tools</h3>
|
||||||
</template>
|
</template>
|
||||||
<template #modalBody>
|
<template #modalBody>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button class="btn-cyan w-full" type="button">Zone manager</button>
|
<button class="btn-cyan w-full" type="button" @click="() => zoneEditorStore.toggleActive()">Zone manager</button>
|
||||||
<button class="btn-cyan w-full" type="button">Player manager</button>
|
<button class="btn-cyan w-full" type="button">Player manager</button>
|
||||||
<button class="btn-cyan w-full" type="button">Item manager</button>
|
<button class="btn-cyan w-full" type="button">Item manager</button>
|
||||||
<button class="btn-cyan w-full" type="button">NPC manager</button>
|
<button class="btn-cyan w-full" type="button">NPC manager</button>
|
||||||
@ -14,8 +14,10 @@
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ZoneEditor from '@/components/utilities/zoneEditor/ZoneEditor.vue'
|
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
|
import { useZoneEditorStore } from '@/stores/zoneEditor'
|
||||||
|
|
||||||
|
const zoneEditorStore = useZoneEditorStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -45,7 +45,9 @@ const properties = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => properties.isModalOpen, (value) => {
|
watch(
|
||||||
|
() => properties.isModalOpen,
|
||||||
|
(value) => {
|
||||||
isModalOpenRef.value = value
|
isModalOpenRef.value = value
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -122,12 +124,16 @@ const stopDrag = () => {
|
|||||||
isDragging.value = false
|
isDragging.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => properties.modalWidth, (value) => {
|
watch(
|
||||||
|
() => properties.modalWidth,
|
||||||
|
(value) => {
|
||||||
width.value = value
|
width.value = value
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(() => properties.modalHeight, (value) => {
|
watch(
|
||||||
|
() => properties.modalHeight,
|
||||||
|
(value) => {
|
||||||
height.value = value
|
height.value = value
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -148,14 +154,25 @@ onUnmounted(() => {
|
|||||||
removeEventListener('resize', handleResize)
|
removeEventListener('resize', handleResize)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Make sure modal doesn't go off screen
|
// Make sure modal doesn't go off screen
|
||||||
watch(() => x.value, (value) => {
|
watch(
|
||||||
if (value < 0) { x.value = 0 } else if (value + width.value > window.innerWidth) { x.value = window.innerWidth - width.value }
|
() => x.value,
|
||||||
|
(value) => {
|
||||||
|
if (value < 0) {
|
||||||
|
x.value = 0
|
||||||
|
} else if (value + width.value > window.innerWidth) {
|
||||||
|
x.value = window.innerWidth - width.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
watch(() => y.value, (value) => {
|
watch(
|
||||||
if (value < 0) { y.value = 0 } else if (value + height.value > window.innerHeight) { y.value = window.innerHeight - height.value }
|
() => y.value,
|
||||||
|
(value) => {
|
||||||
|
if (value < 0) {
|
||||||
|
y.value = 0
|
||||||
|
} else if (value + height.value > window.innerHeight) {
|
||||||
|
y.value = window.innerHeight - height.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -264,12 +281,12 @@ function handleResize() {
|
|||||||
|
|
||||||
label {
|
label {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-family: "Poppins";
|
font-family: 'Poppins';
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
font-family: "Poppins";
|
font-family: 'Poppins';
|
||||||
border: 1px solid $cyan;
|
border: 1px solid $cyan;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: rgba($white, 0.8);
|
background-color: rgba($white, 0.8);
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
import { useNotificationStore } from '@/stores/notifications'
|
import { useNotificationStore } from '@/stores/notifications'
|
||||||
import { useSocketStore } from '@/stores/socket'
|
import { useSocketStore } from '@/stores/socket'
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, watch } from 'vue'
|
||||||
|
|
||||||
const notifications = useNotificationStore()
|
const notifications = useNotificationStore()
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
@ -32,14 +32,14 @@ function setupNotificationListener(connection: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onBeforeMount(() => {
|
||||||
const connection = socket.getConnection
|
const connection = socket.connection
|
||||||
if (connection) {
|
if (connection) {
|
||||||
setupNotificationListener(connection)
|
setupNotificationListener(connection)
|
||||||
} else {
|
} else {
|
||||||
// Watch for changes in the socket connection
|
// Watch for changes in the socket connection
|
||||||
watch(
|
watch(
|
||||||
() => socket.getConnection,
|
() => socket.connection,
|
||||||
(newConnection) => {
|
(newConnection) => {
|
||||||
if (newConnection) setupNotificationListener(newConnection)
|
if (newConnection) setupNotificationListener(newConnection)
|
||||||
}
|
}
|
||||||
@ -47,8 +47,8 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onBeforeUnmount(() => {
|
||||||
const connection = socket.getConnection
|
const connection = socket.connection
|
||||||
if (connection) {
|
if (connection) {
|
||||||
connection.off('notification')
|
connection.off('notification')
|
||||||
}
|
}
|
||||||
|
103
src/components/utilities/zoneEditor/Tiles.vue
Normal file
103
src/components/utilities/zoneEditor/Tiles.vue
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<Modal v-if="isModalOpen" :isModalOpen="true" :modal-width="645" :modal-height="260">
|
||||||
|
<template #modalHeader>
|
||||||
|
<h3 class="modal-title">Tiles</h3>
|
||||||
|
</template>
|
||||||
|
<template #modalBody>
|
||||||
|
<canvas ref="canvas" :width="tileWidth" :height="tileHeight" style="display: none"></canvas>
|
||||||
|
<div class="tiles">
|
||||||
|
<img v-for="(tile, index) in tiles" :key="index" :src="tile" alt="Tile" @click="selectTile(index)" :class="{ selected: selectedTile === index }" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, nextTick } from 'vue'
|
||||||
|
import config from '@/config'
|
||||||
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
|
|
||||||
|
const tileWidth = config.tile_size.x
|
||||||
|
const tileHeight = config.tile_size.y
|
||||||
|
const tiles = ref<string[]>([])
|
||||||
|
const selectedTile = ref<number | null>(null)
|
||||||
|
const canvas = ref<HTMLCanvasElement | null>(null)
|
||||||
|
const isModalOpen = ref(false)
|
||||||
|
|
||||||
|
// Hardcoded image path
|
||||||
|
const imagePath = '/assets/tiles/default.png'
|
||||||
|
|
||||||
|
const loadImage = (src: string): Promise<HTMLImageElement> => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => resolve(img)
|
||||||
|
img.src = src
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitTiles = (img: HTMLImageElement) => {
|
||||||
|
if (!canvas.value) {
|
||||||
|
console.error('Canvas not found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const ctx = canvas.value.getContext('2d')
|
||||||
|
if (!ctx) {
|
||||||
|
console.error('Failed to get canvas context')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const tilesetWidth = img.width
|
||||||
|
const tilesetHeight = img.height
|
||||||
|
const columns = Math.floor(tilesetWidth / tileWidth)
|
||||||
|
const rows = Math.floor(tilesetHeight / tileHeight)
|
||||||
|
|
||||||
|
tiles.value = []
|
||||||
|
selectedTile.value = null
|
||||||
|
|
||||||
|
for (let row = 0; row < rows; row++) {
|
||||||
|
for (let col = 0; col < columns; col++) {
|
||||||
|
const x = col * tileWidth
|
||||||
|
const y = row * tileHeight
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, tileWidth, tileHeight)
|
||||||
|
ctx.drawImage(img, x, y, tileWidth, tileHeight, 0, 0, tileWidth, tileHeight)
|
||||||
|
|
||||||
|
const tileDataURL = canvas.value.toDataURL()
|
||||||
|
tiles.value.push(tileDataURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectTile = (index: number) => {
|
||||||
|
selectedTile.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
isModalOpen.value = true
|
||||||
|
const img = await loadImage(imagePath)
|
||||||
|
await nextTick()
|
||||||
|
splitTiles(img)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tiles {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiles img {
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: border 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiles img.selected {
|
||||||
|
border: 2px solid #ff0000;
|
||||||
|
}
|
||||||
|
</style>
|
125
src/components/utilities/zoneEditor/Toolbar.vue
Normal file
125
src/components/utilities/zoneEditor/Toolbar.vue
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="tools">
|
||||||
|
<button :class="{ active: activeTool === 'move' }" @click="activeTool = 'move'">
|
||||||
|
<img src="/assets/icons/zoneEditor/move.svg" alt="Eraser tool" />
|
||||||
|
</button>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<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.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" @click="() => zoneEditorStore.toggleActive()">Exit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeUnmount, ref } 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 activeTool = ref('move')
|
||||||
|
const emit = defineEmits(['erase', 'move', 'tile'])
|
||||||
|
|
||||||
|
function onPointerClick(pointer: Phaser.Input.Pointer) {
|
||||||
|
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 (activeTool.value === 'eraser') {
|
||||||
|
emit('erase', pointer_tile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.input.on(Phaser.Input.Events.POINTER_UP, onPointerClick)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
scene.input.off(Phaser.Input.Events.POINTER_UP, onPointerClick)
|
||||||
|
})
|
||||||
|
</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;
|
||||||
|
opacity: 0.8;
|
||||||
|
display: flex;
|
||||||
|
background: $dark-gray;
|
||||||
|
border: 2px solid $cyan;
|
||||||
|
color: $light-gray;
|
||||||
|
padding: 5px;
|
||||||
|
min-width: 90%;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: invert(1);
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,99 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<TilemapLayerC :tilemap="tileMap" :tileset="zoneStore.tiles" ref="tilemapLayer" :layerIndex="0" :cull-padding-x="10" :cull-padding-y="10" />
|
||||||
<canvas ref="tileCanvas" :width="tileWidth" :height="tileHeight" style="display: none;"></canvas>
|
<Controls :layer="layer" />
|
||||||
<div class="tiles">
|
<Toolbar :layer="layer" @erase="erase" @tile="tile" />
|
||||||
<img
|
<Tiles />
|
||||||
v-for="(tile, index) in tiles"
|
|
||||||
:key="index"
|
|
||||||
:src="tile"
|
|
||||||
alt="Tile"
|
|
||||||
@click="selectTile(index)"
|
|
||||||
:class="{ selected: selectedTile === index }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
|
import Tileset = Phaser.Tilemaps.Tileset
|
||||||
|
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
|
||||||
|
import { Container, TilemapLayer as TilemapLayerC, useScene } from 'phavuer'
|
||||||
|
import { onBeforeMount, ref, type Ref, watch } from 'vue'
|
||||||
|
import Controls from '@/components/utilities/Controls.vue'
|
||||||
|
import { useSocketStore } from '@/stores/socket'
|
||||||
|
import { useZoneStore } from '@/stores/zone'
|
||||||
|
import Toolbar from '@/components/utilities/zoneEditor/Toolbar.vue'
|
||||||
|
import Tiles from '@/components/utilities/zoneEditor/Tiles.vue'
|
||||||
|
|
||||||
const tileWidth = config.tile_size.x;
|
// Phavuer logic
|
||||||
const tileHeight = config.tile_size.y;
|
let scene = useScene()
|
||||||
const tiles = ref([]);
|
let tilemapLayer = ref()
|
||||||
const selectedTile = ref(null);
|
let zoneData = new Phaser.Tilemaps.MapData({
|
||||||
const tileCanvas = ref(null);
|
width: 10, // @TODO : get this from the server
|
||||||
|
height: 10, // @TODO : get this from the server
|
||||||
|
tileWidth: config.tile_size.x,
|
||||||
|
tileHeight: config.tile_size.y,
|
||||||
|
orientation: Phaser.Tilemaps.Orientation.ISOMETRIC,
|
||||||
|
format: Phaser.Tilemaps.Formats.ARRAY_2D
|
||||||
|
})
|
||||||
|
let tileMap = new Phaser.Tilemaps.Tilemap(scene, zoneData)
|
||||||
|
let tileset: Tileset = tileMap.addTilesetImage('default', 'tiles') as Tileset
|
||||||
|
let layer: TilemapLayer = tileMap.createBlankLayer('layer', tileset, 0, config.tile_size.y) as TilemapLayer
|
||||||
|
|
||||||
// Hardcoded image path
|
// center camera
|
||||||
const imagePath = '/assets/tiles/default.png';
|
const centerY = (tileMap.height * tileMap.tileHeight) / 2
|
||||||
|
const centerX = (tileMap.width * tileMap.tileWidth) / 2
|
||||||
|
scene.cameras.main.centerOn(centerX, centerY)
|
||||||
|
|
||||||
const loadImage = (src) => {
|
// Multiplayer / server logics
|
||||||
return new Promise((resolve) => {
|
const zoneStore = useZoneStore()
|
||||||
const img = new Image();
|
const socket = useSocketStore()
|
||||||
img.onload = () => resolve(img);
|
|
||||||
img.src = src;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const splitTiles = (img) => {
|
zoneStore.setTiles([
|
||||||
const canvas = tileCanvas.value;
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
const ctx = canvas.getContext('2d');
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
])
|
||||||
|
zoneStore.tiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y)))
|
||||||
|
|
||||||
const tilesetWidth = img.width;
|
// Watch for changes in the zoneStore and update the layer
|
||||||
const tilesetHeight = img.height;
|
watch(
|
||||||
const columns = Math.floor(tilesetWidth / tileWidth);
|
() => zoneStore.tiles,
|
||||||
const rows = Math.floor(tilesetHeight / tileHeight);
|
() => {
|
||||||
|
// @TODO : change to tiles for when loading other maps
|
||||||
|
zoneStore.tiles.forEach((row, y) => row.forEach((tile, x) => layer.putTileAt(tile, x, y)))
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
tiles.value = [];
|
function erase(tile: Phaser.Tilemaps.Tile) {
|
||||||
selectedTile.value = null;
|
layer.removeTileAt(tile.x, tile.y)
|
||||||
|
|
||||||
for (let row = 0; row < rows; row++) {
|
|
||||||
for (let col = 0; col < columns; col++) {
|
|
||||||
const x = col * tileWidth;
|
|
||||||
const y = row * tileHeight;
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, tileWidth, tileHeight);
|
|
||||||
ctx.drawImage(img, x, y, tileWidth, tileHeight, 0, 0, tileWidth, tileHeight);
|
|
||||||
|
|
||||||
const tileDataURL = canvas.toDataURL();
|
|
||||||
tiles.value.push(tileDataURL);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectTile = (index) => {
|
function tile() {}
|
||||||
selectedTile.value = index;
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
const img = await loadImage(imagePath);
|
|
||||||
splitTiles(img);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resources:
|
|
||||||
* https://codepen.io/Xymota/pen/gOOyxWB
|
|
||||||
* https://www.dynetisgames.com/2022/06/09/update-how-to-manage-big-isometric-maps-with-phaser-3-5/
|
|
||||||
* https://stackoverflow.com/questions/11533606/javascript-splitting-a-tileset-image-to-be-stored-in-2d-image-array
|
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.tiles {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tiles img {
|
|
||||||
width: 64px;
|
|
||||||
height: 32px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
transition: border 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tiles img.selected {
|
|
||||||
border: 2px solid #ff0000;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,81 +0,0 @@
|
|||||||
<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>
|
|
@ -2,7 +2,7 @@ import config from '@/config'
|
|||||||
|
|
||||||
export function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
|
export function getTile(x: number, y: number, layer: Phaser.Tilemaps.TilemapLayer): Phaser.Tilemaps.Tile | undefined {
|
||||||
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
|
const tile: Phaser.Tilemaps.Tile = layer.getTileAtWorldXY(x, y)
|
||||||
if (!tile) return undefined;
|
if (!tile) return undefined
|
||||||
return tile
|
return tile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,6 @@ export const useSocketStore: StoreDefinition = defineStore('socket', {
|
|||||||
user: null as User | null,
|
user: null as User | null,
|
||||||
character: null as Character | null
|
character: null as Character | null
|
||||||
}),
|
}),
|
||||||
getters: {
|
|
||||||
getToken: (state: any) => state.token as string,
|
|
||||||
getConnection: (state: any) => state.connection as Socket,
|
|
||||||
getUser: (state: any) => state.user as User,
|
|
||||||
getCharacter: (state: any) => state.character as Character
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
initConnection() {
|
initConnection() {
|
||||||
this.connection = io(config.server_endpoint, {
|
this.connection = io(config.server_endpoint, {
|
||||||
|
@ -3,21 +3,12 @@ import type { Character } from '@/types'
|
|||||||
|
|
||||||
export const useZoneStore = defineStore('zone', {
|
export const useZoneStore = defineStore('zone', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
loaded: false,
|
tiles: [] as number[][],
|
||||||
tiles: undefined,
|
characters: [] as Character[]
|
||||||
characters: [] as Character[],
|
|
||||||
editorIsOpen: true
|
|
||||||
}),
|
}),
|
||||||
getters: {
|
|
||||||
isLoaded: (state) => state.loaded,
|
|
||||||
getTiles: (state) => state.tiles,
|
|
||||||
getCharacters: (state) => state.characters,
|
|
||||||
getEditorIsOpen: (state) => state.editorIsOpen
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
loadTiles(tiles: any) {
|
setTiles(tiles: number[][]) {
|
||||||
this.tiles = tiles
|
this.tiles = tiles
|
||||||
this.loaded = true
|
|
||||||
},
|
},
|
||||||
setCharacters(characters: Character[]) {
|
setCharacters(characters: Character[]) {
|
||||||
this.characters = characters
|
this.characters = characters
|
||||||
@ -25,19 +16,13 @@ export const useZoneStore = defineStore('zone', {
|
|||||||
addCharacter(character: Character) {
|
addCharacter(character: Character) {
|
||||||
this.characters.push(character)
|
this.characters.push(character)
|
||||||
},
|
},
|
||||||
removeCharacter(character: Character) {
|
|
||||||
this.characters = this.characters.filter((c: Character) => c.id !== character.id)
|
|
||||||
},
|
|
||||||
updateCharacter(character: Character) {
|
updateCharacter(character: Character) {
|
||||||
const index = this.characters.findIndex((c) => c.id === character.id)
|
const index = this.characters.findIndex((c) => c.id === character.id)
|
||||||
if (index !== -1) {
|
if (index === -1) return
|
||||||
this.characters[index] = character
|
this.characters[index] = character
|
||||||
} else {
|
|
||||||
console.error(`Character with id ${character.id} not found`)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setEditorIsOpen(isOpen: boolean) {
|
removeCharacter(character: Character) {
|
||||||
this.editorIsOpen = isOpen
|
this.characters = this.characters.filter((c: Character) => c.id !== character.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import config from '@/config'
|
||||||
|
|
||||||
export const useZoneEditorStore = defineStore('zoneEditor', {
|
export const useZoneEditorStore = defineStore('zoneEditor', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
loaded: false,
|
active: true
|
||||||
tiles: undefined,
|
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isLoaded: (state) => state.loaded,
|
isActive: (state) => state.active
|
||||||
getTiles: (state) => state.tiles,
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
loadTiles(tiles: any) {
|
toggleActive() {
|
||||||
this.tiles = tiles
|
this.active = !this.active
|
||||||
this.loaded = true
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [[0,0,0,0,0,0,0,0,0,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,1,1,1,1,0],[0,0,0,0,0,0,0,0,0,0]]
|
||||||
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user