1
0
forked from noxious/client

Merge remote-tracking branch 'origin/main' into feature/#182-reset-password

# Conflicts:
#	src/screens/Login.vue
#	src/stores/gameStore.ts
This commit is contained in:
Colin Kallemein 2024-11-02 21:47:57 +01:00
commit 584262a59b
27 changed files with 711 additions and 525 deletions

View File

@ -1,4 +1,4 @@
VITE_NAME=New Quest VITE_NAME=Sylvan Quest
VITE_DEVELOPMENT=true VITE_DEVELOPMENT=true
VITE_SERVER_ENDPOINT=http://localhost:4000 VITE_SERVER_ENDPOINT=http://localhost:4000
VITE_TILE_SIZE_X=64 VITE_TILE_SIZE_X=64

540
package-lock.json generated
View File

@ -43,6 +43,7 @@
"tailwindcss": "^3.4.13", "tailwindcss": "^3.4.13",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^5.4.9", "vite": "^5.4.9",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.5.2", "vite-plugin-vue-devtools": "^7.5.2",
"vitest": "^2.0.3", "vitest": "^2.0.3",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.6.5"
@ -86,9 +87,9 @@
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.26.0", "version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -101,9 +102,9 @@
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.26.0", "version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
"integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -152,13 +153,13 @@
} }
}, },
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.26.0", "version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
"integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.26.0", "@babel/parser": "^7.26.2",
"@babel/types": "^7.26.0", "@babel/types": "^7.26.0",
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25", "@jridgewell/trace-mapping": "^0.3.25",
@ -394,9 +395,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.26.1", "version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
"integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.26.0" "@babel/types": "^7.26.0"
@ -1004,9 +1005,9 @@
} }
}, },
"node_modules/@eslint-community/regexpp": { "node_modules/@eslint-community/regexpp": {
"version": "4.11.2", "version": "4.12.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.2.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
"integrity": "sha512-2WwyTYNVaMNUWPZTOJdkax9iqTdirrApgTbk+Qoq5EPX6myqZvG8QGFRgdKmkjKVG6/G/a565vpPauHk0+hpBA==", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -1283,6 +1284,7 @@
"integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"detect-libc": "^1.0.3", "detect-libc": "^1.0.3",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -1631,9 +1633,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz",
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1645,9 +1647,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz",
"integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1659,9 +1661,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz",
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1673,9 +1675,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz",
"integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1686,10 +1688,38 @@
"darwin" "darwin"
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz",
"integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz",
"integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz",
"integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1701,9 +1731,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz",
"integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1715,9 +1745,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz",
"integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1729,9 +1759,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz",
"integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1743,9 +1773,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz",
"integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -1757,9 +1787,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz",
"integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -1771,9 +1801,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz",
"integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -1785,9 +1815,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz",
"integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1799,9 +1829,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz",
"integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1813,9 +1843,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz",
"integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1827,9 +1857,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz",
"integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -1841,9 +1871,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz",
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1900,9 +1930,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.17.1", "version": "20.17.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.5.tgz",
"integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==", "integrity": "sha512-n8FYY/pRxu496441gIcAQFZPKXbhsd6VZygcq+PTSZ75eMh/Ke0hCAROdUa21qiFqKNsPPYic46yXDO1JGiPBQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -2137,15 +2167,15 @@
} }
}, },
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz",
"integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/spy": "2.1.3", "@vitest/spy": "2.1.4",
"@vitest/utils": "2.1.3", "@vitest/utils": "2.1.4",
"chai": "^5.1.1", "chai": "^5.1.2",
"tinyrainbow": "^1.2.0" "tinyrainbow": "^1.2.0"
}, },
"funding": { "funding": {
@ -2153,22 +2183,21 @@
} }
}, },
"node_modules/@vitest/mocker": { "node_modules/@vitest/mocker": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz",
"integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/spy": "2.1.3", "@vitest/spy": "2.1.4",
"estree-walker": "^3.0.3", "estree-walker": "^3.0.3",
"magic-string": "^0.30.11" "magic-string": "^0.30.12"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/vitest" "url": "https://opencollective.com/vitest"
}, },
"peerDependencies": { "peerDependencies": {
"@vitest/spy": "2.1.3", "msw": "^2.4.9",
"msw": "^2.3.5",
"vite": "^5.0.0" "vite": "^5.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
@ -2191,9 +2220,9 @@
} }
}, },
"node_modules/@vitest/pretty-format": { "node_modules/@vitest/pretty-format": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz",
"integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -2204,13 +2233,13 @@
} }
}, },
"node_modules/@vitest/runner": { "node_modules/@vitest/runner": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz",
"integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/utils": "2.1.3", "@vitest/utils": "2.1.4",
"pathe": "^1.1.2" "pathe": "^1.1.2"
}, },
"funding": { "funding": {
@ -2218,14 +2247,14 @@
} }
}, },
"node_modules/@vitest/snapshot": { "node_modules/@vitest/snapshot": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz",
"integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "2.1.3", "@vitest/pretty-format": "2.1.4",
"magic-string": "^0.30.11", "magic-string": "^0.30.12",
"pathe": "^1.1.2" "pathe": "^1.1.2"
}, },
"funding": { "funding": {
@ -2233,27 +2262,27 @@
} }
}, },
"node_modules/@vitest/spy": { "node_modules/@vitest/spy": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz",
"integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tinyspy": "^3.0.0" "tinyspy": "^3.0.2"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/vitest" "url": "https://opencollective.com/vitest"
} }
}, },
"node_modules/@vitest/utils": { "node_modules/@vitest/utils": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz",
"integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "2.1.3", "@vitest/pretty-format": "2.1.4",
"loupe": "^3.1.1", "loupe": "^3.1.2",
"tinyrainbow": "^1.2.0" "tinyrainbow": "^1.2.0"
}, },
"funding": { "funding": {
@ -2399,14 +2428,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/devtools-core": { "node_modules/@vue/devtools-core": {
"version": "7.5.4", "version": "7.6.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.5.4.tgz", "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.6.2.tgz",
"integrity": "sha512-igB2iUKsCUrXkp0wKLn3n5X8jz3AgXWk7if0QpLu3Do16QmlTO0e+/VvTpX0ZbLMh8OOAxDKyfPvJMMO/4QJ5w==", "integrity": "sha512-hJfjNR3ai94Mb6i0PB42kxUPkPreS6Dl07FUaHAcw+umtkUX55jTXe7+mhsHx9NI6NFT+1WMFREIy8O81KLYyA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-kit": "^7.5.4", "@vue/devtools-kit": "^7.6.2",
"@vue/devtools-shared": "^7.5.4", "@vue/devtools-shared": "^7.6.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nanoid": "^3.3.4", "nanoid": "^3.3.4",
"pathe": "^1.1.2", "pathe": "^1.1.2",
@ -2417,13 +2446,13 @@
} }
}, },
"node_modules/@vue/devtools-kit": { "node_modules/@vue/devtools-kit": {
"version": "7.5.4", "version": "7.6.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.5.4.tgz", "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.2.tgz",
"integrity": "sha512-0i7WFgc1B2TL52tstn82zlb9opSA0aIiHfkUYFXtZb8CIpmlFMTkHtgwVl6PMWNBj3LNhYou1YJCLpCYvJYYoA==", "integrity": "sha512-k61BxHRmcTtIQZFouF9QWt9nCCNtSdw12lhg8VNtHq5/XOBGD+ewiK27a40UJ8UPYoCJvi80hbvbYr5E/Zeu1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-shared": "^7.5.4", "@vue/devtools-shared": "^7.6.2",
"birpc": "^0.2.19", "birpc": "^0.2.19",
"hookable": "^5.5.3", "hookable": "^5.5.3",
"mitt": "^3.0.1", "mitt": "^3.0.1",
@ -2433,9 +2462,9 @@
} }
}, },
"node_modules/@vue/devtools-shared": { "node_modules/@vue/devtools-shared": {
"version": "7.5.4", "version": "7.6.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.5.4.tgz", "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.2.tgz",
"integrity": "sha512-dwuq4YmwTyLc7eBOqX63s3JB8il7qnKsNgENglSMkUPwiItHkVAYYfPESN1rxSdYkl1RCux1l5TBidYqfUDNAA==", "integrity": "sha512-lcjyJ7hCC0W0kNwnCGMLVTMvDLoZgjcq9BvboPgS+6jQyDul7fpzRSKTGtGhCHoxrDox7qBAKGbAl2Rcf7GE1A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -2767,9 +2796,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.13.0", "version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@ -3095,9 +3124,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001672", "version": "1.0.30001676",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001672.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz",
"integrity": "sha512-XhW1vRo1ob6aeK2w3rTohwTPBLse/rvjq+s3RTSBwnlZqoFFjx9cHsShJjAIbLsLjyoacaTxpLZy9v3gg6zypw==", "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -3331,6 +3360,17 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dagre": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"graphlib": "^2.1.8",
"lodash": "^4.17.15"
}
},
"node_modules/data-urls": { "node_modules/data-urls": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
@ -3451,6 +3491,7 @@
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true,
"bin": { "bin": {
"detect-libc": "bin/detect-libc.js" "detect-libc": "bin/detect-libc.js"
}, },
@ -3557,9 +3598,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.47", "version": "1.5.50",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz",
"integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==", "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@ -3799,9 +3840,9 @@
} }
}, },
"node_modules/eslint-plugin-vue": { "node_modules/eslint-plugin-vue": {
"version": "9.29.1", "version": "9.30.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz",
"integrity": "sha512-MH/MbVae4HV/tM8gKAVWMPJbYgW04CK7SuzYRrlNERpxbO0P3+Zdsa2oAcFBW6xNu7W6lIkGOsFAMCRTYmrlWQ==", "integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -3951,6 +3992,16 @@
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/execa": { "node_modules/execa": {
"version": "8.0.1", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
@ -3975,6 +4026,16 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1" "url": "https://github.com/sindresorhus/execa?sponsor=1"
} }
}, },
"node_modules/expect-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
"integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -4338,6 +4399,38 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/graphlib": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash": "^4.17.15"
}
},
"node_modules/graphology": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz",
"integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"events": "^3.3.0",
"obliterator": "^2.0.2"
},
"peerDependencies": {
"graphology-types": ">=0.24.0"
}
},
"node_modules/graphology-types": {
"version": "0.24.7",
"resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.7.tgz",
"integrity": "sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -5230,7 +5323,8 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"optional": true
}, },
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
@ -5456,6 +5550,13 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/obliterator": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==",
"dev": true,
"license": "MIT"
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -5579,9 +5680,9 @@
} }
}, },
"node_modules/parse5": { "node_modules/parse5": {
"version": "7.2.0", "version": "7.2.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
"integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -5696,13 +5797,15 @@
} }
}, },
"node_modules/phaser3-rex-plugins": { "node_modules/phaser3-rex-plugins": {
"version": "1.80.9", "version": "1.80.10",
"resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.9.tgz", "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.10.tgz",
"integrity": "sha512-yx+WSAf4MOF2AimVL/Dv7eN65/YuO4LVNlYihDP0tAgFfCoTBunAd0YDbv82eoSkOAd0gy6w3Qh71p3kq1eRTA==", "integrity": "sha512-7qpWkpFmXobkpMSFEIRcVnotpAYZDksGzgCuZrzDAy+vtmKex5bTdSEZ8UDYYwYgkuAhVoMsveFF6oDfGOSY4Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"dagre": "^0.8.5",
"eventemitter3": "^3.1.2", "eventemitter3": "^3.1.2",
"graphology": "^0.25.4",
"i18next": "^22.5.1", "i18next": "^22.5.1",
"i18next-http-backend": "^2.5.2", "i18next-http-backend": "^2.5.2",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
@ -5772,9 +5875,9 @@
} }
}, },
"node_modules/pinia": { "node_modules/pinia": {
"version": "2.2.4", "version": "2.2.5",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.4.tgz", "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.5.tgz",
"integrity": "sha512-K7ZhpMY9iJ9ShTC0cR2+PnxdQRuwVIsXDO/WIEV/RnMC/vmSoKDTKW/exNQYPI+4ij10UjXqdNiEHwn47McANQ==", "integrity": "sha512-T4PEQ4uFv2KIRC8A1Y3k1ceQGTDtxtd7nngYGu1IJUUSpuQoYfGq7w7rOc+f5YN1vx3mEs2NjjtN2IFbNS7jqA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-api": "^6.6.3", "@vue/devtools-api": "^6.6.3",
@ -5786,7 +5889,7 @@
"peerDependencies": { "peerDependencies": {
"@vue/composition-api": "^1.4.0", "@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4", "typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.3.0" "vue": "^2.6.14 || ^3.5.11"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@vue/composition-api": { "@vue/composition-api": {
@ -6254,9 +6357,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.24.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz",
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -6270,22 +6373,24 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.24.0", "@rollup/rollup-android-arm-eabi": "4.24.3",
"@rollup/rollup-android-arm64": "4.24.0", "@rollup/rollup-android-arm64": "4.24.3",
"@rollup/rollup-darwin-arm64": "4.24.0", "@rollup/rollup-darwin-arm64": "4.24.3",
"@rollup/rollup-darwin-x64": "4.24.0", "@rollup/rollup-darwin-x64": "4.24.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0", "@rollup/rollup-freebsd-arm64": "4.24.3",
"@rollup/rollup-linux-arm-musleabihf": "4.24.0", "@rollup/rollup-freebsd-x64": "4.24.3",
"@rollup/rollup-linux-arm64-gnu": "4.24.0", "@rollup/rollup-linux-arm-gnueabihf": "4.24.3",
"@rollup/rollup-linux-arm64-musl": "4.24.0", "@rollup/rollup-linux-arm-musleabihf": "4.24.3",
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", "@rollup/rollup-linux-arm64-gnu": "4.24.3",
"@rollup/rollup-linux-riscv64-gnu": "4.24.0", "@rollup/rollup-linux-arm64-musl": "4.24.3",
"@rollup/rollup-linux-s390x-gnu": "4.24.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3",
"@rollup/rollup-linux-x64-gnu": "4.24.0", "@rollup/rollup-linux-riscv64-gnu": "4.24.3",
"@rollup/rollup-linux-x64-musl": "4.24.0", "@rollup/rollup-linux-s390x-gnu": "4.24.3",
"@rollup/rollup-win32-arm64-msvc": "4.24.0", "@rollup/rollup-linux-x64-gnu": "4.24.3",
"@rollup/rollup-win32-ia32-msvc": "4.24.0", "@rollup/rollup-linux-x64-musl": "4.24.3",
"@rollup/rollup-win32-x64-msvc": "4.24.0", "@rollup/rollup-win32-arm64-msvc": "4.24.3",
"@rollup/rollup-win32-ia32-msvc": "4.24.3",
"@rollup/rollup-win32-x64-msvc": "4.24.3",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -6341,13 +6446,12 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.80.4", "version": "1.80.6",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.80.4.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz",
"integrity": "sha512-rhMQ2tSF5CsuuspvC94nPM9rToiAFw2h3JTrLlgmNw1MH79v8Cr3DH6KF6o6r+8oofY3iYVPUf66KzC8yuVN1w==", "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@parcel/watcher": "^2.4.1",
"chokidar": "^4.0.0", "chokidar": "^4.0.0",
"immutable": "^4.0.0", "immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0" "source-map-js": ">=0.6.2 <2.0.0"
@ -6357,6 +6461,9 @@
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
} }
}, },
"node_modules/saxes": { "node_modules/saxes": {
@ -6965,9 +7072,9 @@
} }
}, },
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "1.3.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz",
"integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -6985,9 +7092,9 @@
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.0", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true, "dev": true,
"license": "0BSD" "license": "0BSD"
}, },
@ -7191,14 +7298,14 @@
} }
}, },
"node_modules/vite-node": { "node_modules/vite-node": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz",
"integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cac": "^6.7.14", "cac": "^6.7.14",
"debug": "^4.3.6", "debug": "^4.3.7",
"pathe": "^1.1.2", "pathe": "^1.1.2",
"vite": "^5.0.0" "vite": "^5.0.0"
}, },
@ -7212,6 +7319,46 @@
"url": "https://opencollective.com/vitest" "url": "https://opencollective.com/vitest"
} }
}, },
"node_modules/vite-plugin-compression": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz",
"integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"debug": "^4.3.3",
"fs-extra": "^10.0.0"
},
"peerDependencies": {
"vite": ">=2.0.0"
}
},
"node_modules/vite-plugin-compression/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/vite-plugin-compression/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/vite-plugin-inspect": { "node_modules/vite-plugin-inspect": {
"version": "0.8.7", "version": "0.8.7",
"resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-0.8.7.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-0.8.7.tgz",
@ -7260,15 +7407,15 @@
} }
}, },
"node_modules/vite-plugin-vue-devtools": { "node_modules/vite-plugin-vue-devtools": {
"version": "7.5.4", "version": "7.6.2",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.5.4.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.6.2.tgz",
"integrity": "sha512-6yTcGrF+YdplDhNiNCkwj23BQDHA/jp06FR4Bo3rui1GW+8VdFcc26au2gtynPwRDNJXNueTxiVtVb6dq+lNZA==", "integrity": "sha512-YPE/8AIBsomvHadZ02Kkp8yZo2FR0SFNjbC2lcMgW+hNA1ZoXu9b5oi18gTMzJcLLFRNNSMNjShA4RLqXlIR/A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-core": "^7.5.4", "@vue/devtools-core": "^7.6.2",
"@vue/devtools-kit": "^7.5.4", "@vue/devtools-kit": "^7.6.2",
"@vue/devtools-shared": "^7.5.4", "@vue/devtools-shared": "^7.6.2",
"execa": "^8.0.1", "execa": "^8.0.1",
"sirv": "^3.0.0", "sirv": "^3.0.0",
"vite-plugin-inspect": "^0.8.7", "vite-plugin-inspect": "^0.8.7",
@ -7303,30 +7450,31 @@
} }
}, },
"node_modules/vitest": { "node_modules/vitest": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz",
"integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/expect": "2.1.3", "@vitest/expect": "2.1.4",
"@vitest/mocker": "2.1.3", "@vitest/mocker": "2.1.4",
"@vitest/pretty-format": "^2.1.3", "@vitest/pretty-format": "^2.1.4",
"@vitest/runner": "2.1.3", "@vitest/runner": "2.1.4",
"@vitest/snapshot": "2.1.3", "@vitest/snapshot": "2.1.4",
"@vitest/spy": "2.1.3", "@vitest/spy": "2.1.4",
"@vitest/utils": "2.1.3", "@vitest/utils": "2.1.4",
"chai": "^5.1.1", "chai": "^5.1.2",
"debug": "^4.3.6", "debug": "^4.3.7",
"magic-string": "^0.30.11", "expect-type": "^1.1.0",
"magic-string": "^0.30.12",
"pathe": "^1.1.2", "pathe": "^1.1.2",
"std-env": "^3.7.0", "std-env": "^3.7.0",
"tinybench": "^2.9.0", "tinybench": "^2.9.0",
"tinyexec": "^0.3.0", "tinyexec": "^0.3.1",
"tinypool": "^1.0.0", "tinypool": "^1.0.1",
"tinyrainbow": "^1.2.0", "tinyrainbow": "^1.2.0",
"vite": "^5.0.0", "vite": "^5.0.0",
"vite-node": "2.1.3", "vite-node": "2.1.4",
"why-is-node-running": "^2.3.0" "why-is-node-running": "^2.3.0"
}, },
"bin": { "bin": {
@ -7341,8 +7489,8 @@
"peerDependencies": { "peerDependencies": {
"@edge-runtime/vm": "*", "@edge-runtime/vm": "*",
"@types/node": "^18.0.0 || >=20.0.0", "@types/node": "^18.0.0 || >=20.0.0",
"@vitest/browser": "2.1.3", "@vitest/browser": "2.1.4",
"@vitest/ui": "2.1.3", "@vitest/ui": "2.1.4",
"happy-dom": "*", "happy-dom": "*",
"jsdom": "*" "jsdom": "*"
}, },
@ -7389,9 +7537,9 @@
} }
}, },
"node_modules/vue-component-type-helpers": { "node_modules/vue-component-type-helpers": {
"version": "2.1.8", "version": "2.1.10",
"resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.1.8.tgz", "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.1.10.tgz",
"integrity": "sha512-ii36gDzrYAfOQIkOlo44yceDdT5269gKmNGxf07Qx6seH2U50+tQ2ol02XLhYPmxrh6YabAsOdte8WDrpaO6Tw==", "integrity": "sha512-lfgdSLQKrUmADiSV6PbBvYgQ33KF3Ztv6gP85MfGaGaSGMTXORVaHT1EHfsqCgzRNBstPKYDmvAV9Do5CmJ07A==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View File

@ -50,6 +50,7 @@
"tailwindcss": "^3.4.13", "tailwindcss": "^3.4.13",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^5.4.9", "vite": "^5.4.9",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.5.2", "vite-plugin-vue-devtools": "^7.5.2",
"vitest": "^2.0.3", "vitest": "^2.0.3",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.6.5"

View File

@ -2,7 +2,6 @@
<Notifications /> <Notifications />
<GmTools v-if="gameStore.character?.role === 'gm'" /> <GmTools v-if="gameStore.character?.role === 'gm'" />
<GmPanel v-if="gameStore.character?.role === 'gm'" /> <GmPanel v-if="gameStore.character?.role === 'gm'" />
<component :is="currentScreen" /> <component :is="currentScreen" />
</template> </template>
@ -15,19 +14,34 @@ import GmPanel from '@/components/gameMaster/GmPanel.vue'
import Login from '@/screens/Login.vue' import Login from '@/screens/Login.vue'
import Characters from '@/screens/Characters.vue' import Characters from '@/screens/Characters.vue'
import Game from '@/screens/Game.vue' import Game from '@/screens/Game.vue'
// import Loading from '@/screens/Loading.vue'
import ZoneEditor from '@/screens/ZoneEditor.vue' import ZoneEditor from '@/screens/ZoneEditor.vue'
import { computed } from 'vue' import { computed, watch } from 'vue'
const gameStore = useGameStore() const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore() const zoneEditorStore = useZoneEditorStore()
const currentScreen = computed(() => { const currentScreen = computed(() => {
// if (!gameStore.isAssetsLoaded) return Loading
if (!gameStore.connection) return Login if (!gameStore.connection) return Login
if (!gameStore.token) return Login if (!gameStore.token) return Login
if (!gameStore.character) return Characters if (!gameStore.character) return Characters
if (zoneEditorStore.active) return ZoneEditor if (zoneEditorStore.active) return ZoneEditor
return Game return Game
}) })
// Watch zoneEditorStore.active and empty gameStore.game.loadedAssets
watch(
() => zoneEditorStore.active,
() => {
gameStore.game.loadedAssets = []
}
)
// #209: Play sound when a button is pressed
addEventListener('click', (event) => {
if (!(event.target instanceof HTMLButtonElement)) {
return
}
const audio = new Audio('/assets/music/click-btn.mp3')
audio.play()
})
</script> </script>

View File

@ -1,7 +1,5 @@
<template> <template>
<div v-if="isLoaded">
<ZoneTiles @tilemap:create="tileMap = $event" /> <ZoneTiles @tilemap:create="tileMap = $event" />
</div>
<ZoneObjects v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" /> <ZoneObjects v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" />
<ZoneEventTiles v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" /> <ZoneEventTiles v-if="tileMap" :tilemap="tileMap as Phaser.Tilemaps.Tilemap" />
@ -16,11 +14,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useScene } from 'phavuer' import { onUnmounted, ref } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useZoneEditorStore } from '@/stores/zoneEditorStore' import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { type AssetT, type Zone } from '@/types' import { type Zone } from '@/types'
// Components // Components
import Toolbar from '@/components/gameMaster/zoneEditor/partials/Toolbar.vue' import Toolbar from '@/components/gameMaster/zoneEditor/partials/Toolbar.vue'
@ -32,14 +29,10 @@ import TeleportModal from '@/components/gameMaster/zoneEditor/partials/TeleportM
import ZoneTiles from '@/components/gameMaster/zoneEditor/ZoneTiles.vue' import ZoneTiles from '@/components/gameMaster/zoneEditor/ZoneTiles.vue'
import ZoneObjects from '@/components/gameMaster/zoneEditor/ZoneObjects.vue' import ZoneObjects from '@/components/gameMaster/zoneEditor/ZoneObjects.vue'
import ZoneEventTiles from '@/components/gameMaster/zoneEditor/ZoneEventTiles.vue' import ZoneEventTiles from '@/components/gameMaster/zoneEditor/ZoneEventTiles.vue'
import config from '@/config'
import { loadZoneTileTexture } from '@/composables/zoneComposable'
const scene = useScene()
const gameStore = useGameStore() const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore() const zoneEditorStore = useZoneEditorStore()
const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null) const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null)
const isLoaded = ref(false)
function save() { function save() {
if (!zoneEditorStore.zone) return if (!zoneEditorStore.zone) return
@ -65,14 +58,6 @@ function save() {
}) })
} }
onMounted(async () => {
const tiles: AssetT[] = await fetch(config.server_endpoint + '/assets/list_tiles').then((response) => response.json())
for (const tile of tiles) {
await loadZoneTileTexture(scene, tile.key, tile.updatedAt)
}
isLoaded.value = true
})
onUnmounted(() => { onUnmounted(() => {
zoneEditorStore.reset() zoneEditorStore.reset()
}) })

View File

@ -1,6 +1,6 @@
<template> <template>
<SelectedZoneObject v-if="selectedZoneObject" :zoneObject="selectedZoneObject" @move="moveZoneObject" @rotate="rotateZoneObject" @delete="deleteZoneObject" /> <SelectedZoneObject v-if="selectedZoneObject" :zoneObject="selectedZoneObject" :movingZoneObject="movingZoneObject" @move="moveZoneObject" @rotate="rotateZoneObject" @delete="deleteZoneObject" />
<ZoneObject v-for="zoneObject in zoneEditorStore.zone?.zoneObjects" :tilemap="tilemap" :zoneObject @pointerup="() => (selectedZoneObject = zoneObject)" /> <ZoneObject v-for="zoneObject in zoneEditorStore.zone?.zoneObjects" :tilemap="tilemap" :zoneObject :selectedZoneObject :movingZoneObject @pointerup="() => (selectedZoneObject = zoneObject)" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -7,13 +7,15 @@ import config from '@/config'
import { useScene } from 'phavuer' import { useScene } from 'phavuer'
import { useZoneEditorStore } from '@/stores/zoneEditorStore' import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import { onMounted, onUnmounted } from 'vue' import { onMounted, onUnmounted } from 'vue'
import { createTileArray, FlattenZoneArray, getTile, placeTile, setLayerTiles } from '@/composables/zoneComposable' import { createTileArray, getTile, placeTile, setLayerTiles } from '@/composables/zoneComposable'
import Controls from '@/components/utilities/Controls.vue' import Controls from '@/components/utilities/Controls.vue'
import { unduplicateArray } from '@/utilities' import { useGameStore } from '@/stores/gameStore'
import type { AssetDataT } from '@/types'
const emit = defineEmits(['tilemap:create']) const emit = defineEmits(['tilemap:create'])
const scene = useScene() const scene = useScene()
const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore() const zoneEditorStore = useZoneEditorStore()
const zoneTilemap = createTilemap() const zoneTilemap = createTilemap()
const tiles = createTileLayer() const tiles = createTileLayer()
@ -41,10 +43,10 @@ function createTilemap() {
* A Tileset is a combination of a single image containing the tiles and a container for data about each tile. * A Tileset is a combination of a single image containing the tiles and a container for data about each tile.
*/ */
function createTileLayer() { function createTileLayer() {
const tilesArray = unduplicateArray(FlattenZoneArray(zoneEditorStore.zone?.tiles ?? [])) const tilesArray = gameStore.getLoadedAssetsByGroup('tiles')
const tilesetImages = Array.from(tilesArray).map((tile: any, index: number) => { const tilesetImages = Array.from(tilesArray).map((tile: AssetDataT, index: number) => {
return zoneTilemap.addTilesetImage(tile, tile, config.tile_size.x, config.tile_size.y, 1, 2, index + 1, { x: 0, y: -config.tile_size.y }) return zoneTilemap.addTilesetImage(tile.key, tile.key, config.tile_size.x, config.tile_size.y, 1, 2, index + 1, { x: 0, y: -config.tile_size.y })
}) as any }) as any
// Add blank tile // Add blank tile

View File

@ -5,18 +5,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { Image, useScene } from 'phavuer' import { Image, useScene } from 'phavuer'
import { calculateIsometricDepth, loadZoneObjectTexture, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable' import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
import type { ZoneObject } from '@/types' import { loadTexture } from '@/composables/gameComposable'
import type { AssetDataT, ZoneObject } from '@/types'
const props = defineProps<{ const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap tilemap: Phaser.Tilemaps.Tilemap
zoneObject: ZoneObject zoneObject: ZoneObject
selectedZoneObject: ZoneObject | null
movingZoneObject: ZoneObject | null
}>() }>()
const scene = useScene() const scene = useScene()
const isTextureLoaded = ref(false) const isTextureLoaded = ref(false)
const imageProps = computed(() => ({ const imageProps = computed(() => ({
alpha: props.movingZoneObject?.id === props.zoneObject.id ? 0.5 : 1,
tint: props.selectedZoneObject?.id === props.zoneObject.id ? 0x00ff00 : 0xffffff,
depth: calculateIsometricDepth(props.zoneObject.positionX, props.zoneObject.positionY, props.zoneObject.object.frameWidth, props.zoneObject.object.frameHeight), depth: calculateIsometricDepth(props.zoneObject.positionX, props.zoneObject.positionY, props.zoneObject.object.frameWidth, props.zoneObject.object.frameHeight),
x: tileToWorldX(props.tilemap, props.zoneObject.positionX, props.zoneObject.positionY), x: tileToWorldX(props.tilemap, props.zoneObject.positionX, props.zoneObject.positionY),
y: tileToWorldY(props.tilemap, props.zoneObject.positionX, props.zoneObject.positionY), y: tileToWorldY(props.tilemap, props.zoneObject.positionX, props.zoneObject.positionY),
@ -26,11 +31,14 @@ const imageProps = computed(() => ({
originX: Number(props.zoneObject.object.originY) originX: Number(props.zoneObject.object.originY)
})) }))
loadZoneObjectTexture(scene, props.zoneObject.object.id, props.zoneObject.object.updatedAt) loadTexture(scene, {
.then((loaded) => { key: props.zoneObject.object.id,
isTextureLoaded.value = loaded data: '/assets/objects/' + props.zoneObject.object.id + '.png',
}) group: 'objects',
.catch((error) => { updatedAt: props.zoneObject.object.updatedAt,
frameWidth: props.zoneObject.object.frameWidth,
frameHeight: props.zoneObject.object.frameHeight
} as AssetDataT).catch((error) => {
console.error('Error loading texture:', error) console.error('Error loading texture:', error)
}) })
</script> </script>

View File

@ -18,12 +18,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import config from '@/config' import config from '@/config'
import { type ExtendedCharacter } from '@/types' import { type ExtendedCharacter, type Sprite as SpriteT } from '@/types'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useZoneStore } from '@/stores/zoneStore' import { useZoneStore } from '@/stores/zoneStore'
import { watch, computed, ref, onMounted, onUnmounted } from 'vue' import { watch, computed, ref, onMounted, onUnmounted } from 'vue'
import { Container, refObj, RoundRectangle, Sprite, Text, useGame, useScene } from 'phavuer' import { Container, refObj, RoundRectangle, Sprite, Text, useGame, useScene } from 'phavuer'
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable' import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
import { loadSpriteTextures } from '@/composables/gameComposable'
enum Direction { enum Direction {
POSITIVE, POSITIVE,
@ -181,6 +182,15 @@ watch(
watch(() => props.character.isMoving, updateSprite) watch(() => props.character.isMoving, updateSprite)
watch(() => props.character.rotation, updateSprite) watch(() => props.character.rotation, updateSprite)
loadSpriteTextures(scene, props.character.characterType?.sprite as SpriteT)
.then(() => {
charSprite.value!.setTexture(charTexture.value)
charSprite.value!.setFlipX(isFlippedX.value)
})
.catch((error) => {
console.error('Error loading texture:', error)
})
onMounted(() => { onMounted(() => {
charChatContainer.value!.setName(`${props.character!.name}_chatContainer`) charChatContainer.value!.setName(`${props.character!.name}_chatContainer`)
charChatContainer.value!.setVisible(false) charChatContainer.value!.setVisible(false)
@ -194,10 +204,6 @@ onMounted(() => {
scene.cameras.main.stopFollow() scene.cameras.main.stopFollow()
} }
// Set sprite
charSprite.value!.setTexture(charTexture.value)
charSprite.value!.setFlipX(isFlippedX.value)
updatePosition(props.character.positionX, props.character.positionY, props.character.rotation) updatePosition(props.character.positionX, props.character.positionY, props.character.rotation)
}) })

View File

@ -1,20 +1,22 @@
<template> <template>
<Image v-if="isTextureLoaded" v-bind="imageProps" /> <Image v-if="gameStore.getLoadedAsset(props.zoneObject.object.id)" v-bind="imageProps" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { computed, onMounted } from 'vue'
import { Image, useScene } from 'phavuer' import { Image, useScene } from 'phavuer'
import { calculateIsometricDepth, loadZoneObjectTexture, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable' import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
import type { ZoneObject } from '@/types' import { loadTexture } from '@/composables/gameComposable'
import type { AssetDataT, ZoneObject } from '@/types'
import { useGameStore } from '@/stores/gameStore'
const props = defineProps<{ const props = defineProps<{
tilemap: Phaser.Tilemaps.Tilemap tilemap: Phaser.Tilemaps.Tilemap
zoneObject: ZoneObject zoneObject: ZoneObject
}>() }>()
const gameStore = useGameStore()
const scene = useScene() const scene = useScene()
const isTextureLoaded = ref(false)
const imageProps = computed(() => ({ const imageProps = computed(() => ({
depth: calculateIsometricDepth(props.zoneObject.positionX, props.zoneObject.positionY, props.zoneObject.object.frameWidth, props.zoneObject.object.frameHeight), depth: calculateIsometricDepth(props.zoneObject.positionX, props.zoneObject.positionY, props.zoneObject.object.frameWidth, props.zoneObject.object.frameHeight),
@ -26,11 +28,14 @@ const imageProps = computed(() => ({
originX: Number(props.zoneObject.object.originY) originX: Number(props.zoneObject.object.originY)
})) }))
loadZoneObjectTexture(scene, props.zoneObject.object.id, props.zoneObject.object.updatedAt) loadTexture(scene, {
.then((loaded) => { key: props.zoneObject.object.id,
isTextureLoaded.value = loaded data: '/assets/objects/' + props.zoneObject.object.id + '.png',
}) group: 'objects',
.catch((error) => { updatedAt: props.zoneObject.object.updatedAt,
frameWidth: props.zoneObject.object.frameWidth,
frameHeight: props.zoneObject.object.frameHeight
} as AssetDataT).catch((error) => {
console.error('Error loading texture:', error) console.error('Error loading texture:', error)
}) })
</script> </script>

View File

@ -0,0 +1,34 @@
export function createSceneLoader(scene: Phaser.Scene) {
const width = scene.cameras.main.width
const height = scene.cameras.main.height
const progressBox = scene.add.graphics()
const progressBar = scene.add.graphics()
progressBox.fillStyle(0x222222, 0.8)
progressBox.fillRect(width / 2 - 180, height / 2, 320, 50)
const loadingText = scene.make.text({
x: width / 2,
y: height / 2 - 50,
text: 'Loading...',
style: {
font: '20px monospace',
// @ts-ignore
fill: '#ffffff'
}
})
loadingText.setOrigin(0.5, 0.5)
scene.load.on(Phaser.Loader.Events.PROGRESS, function (value: any) {
progressBar.clear()
progressBar.fillStyle(0x368f8b, 1)
progressBar.fillRect(width / 2 - 180 + 10, height / 2 + 10, 300 * value, 30)
})
scene.load.on(Phaser.Loader.Events.COMPLETE, function () {
progressBar.destroy()
progressBox.destroy()
loadingText.destroy()
return true
})
}

View File

@ -0,0 +1,86 @@
import type { AssetDataT, Sprite } from '@/types'
import { useGameStore } from '@/stores/gameStore'
import { AssetStorage } from '@/storage/assetStorage'
import config from '@/config'
const textureLoadingPromises = new Map<string, Promise<boolean>>()
export async function loadTexture(scene: Phaser.Scene, assetData: AssetDataT): Promise<boolean> {
const gameStore = useGameStore()
const assetStorage = new AssetStorage()
// Check if the texture is already loaded in Phaser
if (gameStore.game.loadedAssets.find((asset) => asset.key === assetData.key)) {
return Promise.resolve(true)
}
// If there's already a loading promise for this texture, return it
if (textureLoadingPromises.has(assetData.key)) {
return await textureLoadingPromises.get(assetData.key)!
}
// Create new loading promise
const loadingPromise = (async () => {
// Check if the asset is already cached
let asset = await assetStorage.get(assetData.key)
// If asset is not found, download it
if (!asset) {
await assetStorage.download(assetData)
asset = await assetStorage.get(assetData.key)
}
// If asset is found, add it to the scene
if (asset) {
return new Promise<boolean>((resolve) => {
// Remove existing texture if it exists
if (scene.textures.exists(asset.key)) {
scene.textures.remove(asset.key)
}
scene.textures.addBase64(asset.key, asset.data)
scene.textures.once(`addtexture-${asset.key}`, () => {
gameStore.game.loadedAssets.push(assetData)
textureLoadingPromises.delete(assetData.key) // Clean up the promise
resolve(true)
})
})
}
textureLoadingPromises.delete(assetData.key) // Clean up the promise
return Promise.resolve(false)
})()
// Store the loading promise
textureLoadingPromises.set(assetData.key, loadingPromise)
return loadingPromise
}
export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
const sprite_actions = await fetch(config.server_endpoint + '/assets/list_sprite_actions/' + sprite?.id).then((response) => response.json())
for await (const sprite_action of sprite_actions) {
await loadTexture(scene, {
key: sprite_action.key,
data: sprite_action.data,
group: sprite_action.isAnimated ? 'sprite_animations' : 'sprites',
updatedAt: sprite_action.updatedAt,
frameCount: sprite_action.frameCount,
frameWidth: sprite_action.frameWidth,
frameHeight: sprite_action.frameHeight
} as AssetDataT)
// If the sprite is not animated, skip
if (!sprite_action.isAnimated) continue
// Add the animation to the scene
const anim = scene.textures.get(sprite_action.key)
scene.textures.addSpriteSheet(sprite_action.key, anim, { frameWidth: sprite_action.frameWidth ?? 0, frameHeight: sprite_action.frameHeight ?? 0 })
scene.anims.create({
key: sprite_action.key,
frameRate: 7,
frames: scene.anims.generateFrameNumbers(sprite_action.key, { start: 0, end: sprite_action.frameCount! - 1 }),
repeat: -1
})
}
return Promise.resolve(true)
}

View File

@ -31,7 +31,7 @@ export function useGamePointerHandlers(scene: Phaser.Scene, layer: Phaser.Tilema
const { worldX, worldY } = pointer const { worldX, worldY } = pointer
updateWaypoint(worldX, worldY) updateWaypoint(worldX, worldY)
if (gameStore.isPlayerDraggingCamera) { if (gameStore.game.isPlayerDraggingCamera) {
const distance = Phaser.Math.Distance.Between(pointerStartPosition.value.x, pointerStartPosition.value.y, pointer.x, pointer.y) const distance = Phaser.Math.Distance.Between(pointerStartPosition.value.x, pointerStartPosition.value.y, pointer.x, pointer.y)
if (distance > dragThreshold) { if (distance > dragThreshold) {

View File

@ -26,7 +26,7 @@ export function useZoneEditorPointerHandlers(scene: Phaser.Scene, layer: Phaser.
} }
function dragZone(pointer: Phaser.Input.Pointer) { function dragZone(pointer: Phaser.Input.Pointer) {
if (gameStore.isPlayerDraggingCamera) { if (gameStore.game.isPlayerDraggingCamera) {
const { x, y, prevPosition } = pointer const { x, y, prevPosition } = pointer
const { scrollX, scrollY, zoom } = camera const { scrollX, scrollY, zoom } = camera
camera.setScroll(scrollX - (x - prevPosition.x) / zoom, scrollY - (y - prevPosition.y) / zoom) camera.setScroll(scrollX - (x - prevPosition.x) / zoom, scrollY - (y - prevPosition.y) / zoom)

View File

@ -3,8 +3,8 @@ import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer import TilemapLayer = Phaser.Tilemaps.TilemapLayer
import Tileset = Phaser.Tilemaps.Tileset import Tileset = Phaser.Tilemaps.Tileset
import Tile = Phaser.Tilemaps.Tile import Tile = Phaser.Tilemaps.Tile
import { useAssetManager } from '@/managers/assetManager' import type { AssetDataT, Zone as ZoneT } from '@/types'
import type { AssetT, Zone as ZoneT } from '@/types' import { loadTexture } from '@/composables/gameComposable'
export function getTile(layer: TilemapLayer | Tilemap, x: number, y: number): Tile | undefined { export function getTile(layer: TilemapLayer | Tilemap, x: number, y: number): Tile | undefined {
const tile = layer.getTileAtWorldXY(x, y) const tile = layer.getTileAtWorldXY(x, y)
@ -86,63 +86,11 @@ export function FlattenZoneArray(tiles: string[][]) {
} }
export async function loadZoneTilesIntoScene(zone: ZoneT, scene: Phaser.Scene) { export async function loadZoneTilesIntoScene(zone: ZoneT, scene: Phaser.Scene) {
const tileArray: AssetT[] = await fetch(config.server_endpoint + '/assets/list_tiles/' + zone.id).then((response) => response.json()) // Fetch the list of tiles from the server
console.log(tileArray) const tileArray: AssetDataT[] = await fetch(config.server_endpoint + '/assets/list_tiles/' + zone.id).then((response) => response.json())
// Load each tile into the scene
for (const tile of tileArray) { for (const tile of tileArray) {
await loadZoneTileTexture(scene, tile.key, tile.updatedAt) await loadTexture(scene, tile)
} }
} }
export async function loadZoneTileTexture(scene: Phaser.Scene, textureId: string, updatedAt: Date): Promise<boolean> {
const assetManager = useAssetManager
// Check if the texture is already loaded in Phaser
if (scene.textures.exists(textureId)) {
return true
}
let assetData = await assetManager.getAsset(textureId)
if (!assetData) {
await assetManager.downloadAsset(textureId, `/assets/tiles/${textureId}.png`, 'tiles', updatedAt)
assetData = await assetManager.getAsset(textureId)
}
if (assetData) {
return new Promise<boolean>((resolve) => {
scene.textures.addBase64(textureId, assetData.data)
scene.textures.once(`addtexture-${textureId}`, () => {
resolve(true)
})
})
}
return false
}
export async function loadZoneObjectTexture(scene: Phaser.Scene, textureId: string, updatedAt: Date): Promise<boolean> {
const assetManager = useAssetManager
// Check if the texture is already loaded in Phaser
if (scene.textures.exists(textureId)) {
return true
}
let assetData = await assetManager.getAsset(textureId)
if (!assetData) {
await assetManager.downloadAsset(textureId, `/assets/objects/${textureId}.png`, 'objects', updatedAt)
assetData = await assetManager.getAsset(textureId)
}
if (assetData) {
return new Promise<boolean>((resolve) => {
scene.textures.addBase64(textureId, assetData.data)
scene.textures.once(`addtexture-${textureId}`, () => {
resolve(true)
})
})
}
return false
}

View File

@ -1,81 +0,0 @@
import config from '@/config'
import Dexie from 'dexie'
class AssetManager extends Dexie {
assets!: Dexie.Table<
{
key: string
data: Blob
group: string
updatedAt: Date
frameCount?: number
frameWidth?: number
frameHeight?: number
},
string
>
constructor() {
super('Assets')
this.version(1).stores({
assets: 'key, group'
})
}
async downloadAsset(key: string, url: string, group: string, updatedAt: Date, frameCount?: number, frameWidth?: number, frameHeight?: number) {
try {
// Check if the asset already exists, then check if updatedAt is newer
const asset = await this.assets.get(key)
if (asset && asset.updatedAt > updatedAt) {
return
}
// Download the asset
const response = await fetch(config.server_endpoint + url)
const blob = await response.blob()
// Store the asset in the database
await this.assets.put({ key, data: blob, group, updatedAt, frameCount, frameWidth, frameHeight })
} catch (error) {
console.error(`Failed to add asset ${key}:`, error)
}
}
async getAsset(key: string) {
try {
const asset = await this.assets.get(key)
if (asset) {
return {
...asset,
data: URL.createObjectURL(asset.data)
}
}
} catch (error) {
console.error(`Failed to retrieve asset ${key}:`, error)
}
return null
}
async getAssetsByGroup(group: string) {
try {
const assets = await this.assets.where('group').equals(group).toArray()
return assets.map((asset) => ({
...asset,
data: URL.createObjectURL(asset.data)
}))
} catch (error) {
console.error(`Failed to retrieve assets for group ${group}:`, error)
return []
}
}
async deleteAsset(key: string) {
try {
await this.assets.delete(key)
} catch (error) {
console.error(`Failed to delete asset ${key}:`, error)
}
}
}
export const useAssetManager = new AssetManager()

View File

@ -114,6 +114,9 @@ gameStore.connection?.on('character:list', (data: any) => {
}) })
onMounted(async () => { onMounted(async () => {
const audio = new Audio('/assets/music/login.mp3')
await audio.play()
// wait 0.75 sec // wait 0.75 sec
setTimeout(() => { setTimeout(() => {
gameStore.connection?.emit('character:list') gameStore.connection?.emit('character:list')

View File

@ -32,10 +32,8 @@ import CharacterProfile from '@/components/gui/CharacterProfile.vue'
import Effects from '@/components/Effects.vue' import Effects from '@/components/Effects.vue'
import Minimap from '@/components/gui/Minimap.vue' import Minimap from '@/components/gui/Minimap.vue'
import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin' import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin'
import { useAssetManager } from '@/managers/assetManager'
const gameStore = useGameStore() const gameStore = useGameStore()
const assetManager = useAssetManager
const gameConfig = { const gameConfig = {
name: config.name, name: config.name,
@ -78,45 +76,7 @@ function preloadScene(scene: Phaser.Scene) {
*/ */
scene.load.image('blank_tile', '/assets/zone/blank_tile.png') scene.load.image('blank_tile', '/assets/zone/blank_tile.png')
scene.load.image('waypoint', '/assets/waypoint.png') scene.load.image('waypoint', '/assets/waypoint.png')
/**
* We're using rex-await-loader to load assets asynchronously
* Phaser does not support this out of the box, so we're using this plugin
*/
// scene.load.rexAwait(async function (successCallback) {
// await assetManager.getAssetsByGroup('tiles').then((assets) => {
// assets.forEach((asset) => {
// if (scene.load.textureManager.exists(asset.key)) return
// scene.textures.addBase64(asset.key, asset.data)
// })
// })
//
// // Load objects
// await assetManager.getAssetsByGroup('objects').then((assets) => {
// assets.forEach((asset) => {
// if (scene.load.textureManager.exists(asset.key)) return
// scene.textures.addBase64(asset.key, asset.data)
// })
// })
//
// successCallback()
// })
} }
function createScene(scene: Phaser.Scene) { function createScene(scene: Phaser.Scene) {}
/**
* Create sprite animations
* This is done here because phaser forces us to
*/
// assetManager.getAssetsByGroup('sprite_animations').then((assets) => {
// assets.forEach((asset) => {
// scene.anims.create({
// key: asset.key,
// frameRate: 7,
// frames: scene.anims.generateFrameNumbers(asset.key, { start: 0, end: asset.frameCount! - 1 }),
// repeat: -1
// })
// })
// })
}
</script> </script>

View File

@ -1,74 +1,25 @@
<template> <template>
<div class="flex flex-col justify-center items-center h-dvh relative"> <div class="flex flex-col justify-center items-center h-dvh relative">
<div v-if="!isLoaded" class="w-20 h-20 rounded-full border-4 border-solid border-gray-300 border-t-transparent animate-spin"></div> <button @click="continueBtnClick" class="w-32 h-12 rounded-full bg-gray-500 flex items-center justify-between px-4 hover:bg-gray-600 transition-colors">
<button v-else @click="continueBtnClick" class="w-20 h-20 rounded-full bg-gray-500 flex items-center justify-center hover:bg-gray-600 transition-colors"> <span class="text-white text-lg flex-1 text-center">Play</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</button> </button>
<div v-if="!isLoaded" class="text-center mt-6">
<h1 class="text-2xl font-bold">Loading...</h1>
<p class="text-gray-400">Please wait while we load the assets.</p>
</div>
</div> </div>
</template> </template>
<script setup lang="ts" async> <script setup lang="ts" async>
import { onMounted, ref } from 'vue'
import config from '@/config'
import type { AssetT as ServerAsset } from '@/types'
import { useAssetManager } from '@/managers/assetManager'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
/**
* This component downloads all assets from the server and
* stores them in the asset manager.
*/
const gameStore = useGameStore() const gameStore = useGameStore()
const assetManager = useAssetManager
const isLoaded = ref(false)
async function getAssets() {
return fetch(config.server_endpoint + '/assets/')
.then((response) => {
if (!response.ok) throw new Error('Failed to fetch assets')
console.log(response)
return response.json()
})
.catch((error) => {
console.error('Error fetching assets:', error)
return false
})
}
async function loadAssetsIntoAssetManager(assets: ServerAsset[]): Promise<void> {
for (const asset of assets) {
// Check if the asset is already loaded
const existingAsset = await assetManager.getAsset(asset.key)
// Check if the asset needs to be updated
if (!existingAsset || new Date(asset.updatedAt) > new Date(existingAsset.updatedAt)) {
// Check if the asset is already loaded, if so, delete it
if (existingAsset) {
await assetManager.deleteAsset(asset.key)
}
// Add the asset to the asset manager
await assetManager.downloadAsset(asset.key, asset.url, asset.group, asset.updatedAt, asset.frameCount, asset.frameWidth, asset.frameHeight)
}
}
}
function continueBtnClick() { function continueBtnClick() {
gameStore.isAssetsLoaded = true // Play music
} const audio = new Audio('/assets/music/login.mp3')
audio.play()
onMounted(async () => { // Set isLoaded to true
const assets = await getAssets() gameStore.game.isLoaded = true
if (assets) {
await loadAssetsIntoAssetManager(assets)
isLoaded.value = true
} }
})
</script> </script>

View File

@ -1,14 +1,15 @@
<template> <template>
<!-- @TODO this must be shown over the login screen -->
<div class="relative max-lg:h-dvh flex flex-row-reverse"> <div class="relative max-lg:h-dvh flex flex-row-reverse">
<ResetPassword :isModalOpen="isPasswordResetFormShown" /> <ResetPassword :isModalOpen="isPasswordResetFormShown" />
<div class="lg:bg-gradient-to-l bg-gradient-to-b from-gray-900 to-transparent w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 z-10"></div> <div class="lg:bg-gradient-to-l bg-gradient-to-b from-gray-900 to-transparent w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 z-10"></div>
<div class="bg-[url('/assets/login/login-bg.png')] w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 bg-no-repeat bg-cover bg-center"></div> <div class="bg-[url('/assets/login/login-bg.png')] w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 bg-no-repeat bg-cover bg-center"></div>
<div class="bg-gray-900 z-20 w-full lg:w-1/2 h-[65dvh] lg:h-dvh relative"> <div class="bg-gray-900 z-20 w-full lg:w-1/2 h-[65dvh] lg:h-dvh relative">
<div class="h-dvh flex items-center lg:justify-center flex-col px-8 max-lg:pt-20"> <div class="h-dvh flex items-center lg:justify-center flex-col px-8 max-lg:pt-20">
<img src="/assets/login/sq-logo-v1.svg" class="mb-10" /> <img src="/assets/login/sq-logo-v1.svg" class="mb-10" alt="Sylvan Quest logo" />
<div class="relative"> <div class="relative">
<img src="/assets/ui-elements/ui-box-outer.svg" class="absolute w-full h-full" /> <img src="/assets/ui-elements/ui-box-outer.svg" class="absolute w-full h-full" alt="UI box outer" />
<img src="/assets/ui-elements/ui-box-inner.svg" class="absolute left-2 top-2 w-[calc(100%_-_16px)] h-[calc(100%_-_16px)] max-lg:hidden" /> <img src="/assets/ui-elements/ui-box-inner.svg" class="absolute left-2 top-2 w-[calc(100%_-_16px)] h-[calc(100%_-_16px)] max-lg:hidden" alt="UI box inner" />
<!-- Login Form --> <!-- Login Form -->
<form v-show="switchForm === 'login'" @submit.prevent="loginFunc" class="relative px-6 py-11"> <form v-show="switchForm === 'login'" @submit.prevent="loginFunc" class="relative px-6 py-11">
@ -73,6 +74,7 @@ import { login, register } from '@/services/authentication'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { useCookies } from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
import ResetPassword from '@/components/utilities/ResetPassword.vue' import ResetPassword from '@/components/utilities/ResetPassword.vue'
import Loading from '@/screens/Loading.vue'
const props = defineProps(['isModalOpen']) const props = defineProps(['isModalOpen'])
const isPasswordResetFormShown = ref(props.isModalOpen) const isPasswordResetFormShown = ref(props.isModalOpen)

View File

@ -16,8 +16,8 @@ import { useGameStore } from '@/stores/gameStore'
import { useZoneEditorStore } from '@/stores/zoneEditorStore' import { useZoneEditorStore } from '@/stores/zoneEditorStore'
import ZoneEditor from '@/components/gameMaster/zoneEditor/ZoneEditor.vue' import ZoneEditor from '@/components/gameMaster/zoneEditor/ZoneEditor.vue'
import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin' import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin'
import { loadZoneTilesIntoScene, loadZoneTileTexture } from '@/composables/zoneComposable' import { loadTexture } from '@/composables/gameComposable'
import type { AssetT, Tile } from '@/types' import type { AssetDataT } from '@/types'
const gameStore = useGameStore() const gameStore = useGameStore()
const zoneEditorStore = useZoneEditorStore() const zoneEditorStore = useZoneEditorStore()
@ -65,6 +65,20 @@ const preloadScene = async (scene: Phaser.Scene) => {
scene.load.image('TELEPORT', '/assets/zone/tp_tile.png') scene.load.image('TELEPORT', '/assets/zone/tp_tile.png')
scene.load.image('blank_tile', '/assets/zone/blank_tile.png') scene.load.image('blank_tile', '/assets/zone/blank_tile.png')
scene.load.image('waypoint', '/assets/waypoint.png') scene.load.image('waypoint', '/assets/waypoint.png')
/**
* Because Phaser can't load tiles after the scene with map in it is created,
* we need to load and cache all the tiles first.
* Then load them into the scene.
*/
scene.load.rexAwait(async function (successCallback: any) {
const tiles: AssetDataT[] = await fetch(config.server_endpoint + '/assets/list_tiles').then((response) => response.json())
for await (const tile of tiles) {
await loadTexture(scene, tile)
}
successCallback()
})
} }
const createScene = async (scene: Phaser.Scene) => {} const createScene = async (scene: Phaser.Scene) => {}

View File

@ -1,6 +1,7 @@
import axios from 'axios' import axios from 'axios'
import config from '@/config' import config from '@/config'
import { useCookies } from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
import { getDomain } from '@/utilities'
export async function register(username: string, email: string, password: string) { export async function register(username: string, email: string, password: string) {
try { try {
@ -8,6 +9,9 @@ export async function register(username: string, email: string, password: string
useCookies().set('token', response.data.token as string) useCookies().set('token', response.data.token as string)
return { success: true, token: response.data.token } return { success: true, token: response.data.token }
} catch (error: any) { } catch (error: any) {
if (typeof error.response.data === 'undefined') {
return { error: 'Could not connect to server' }
}
return { error: error.response.data.message } return { error: error.response.data.message }
} }
} }
@ -16,9 +20,7 @@ export async function login(username: string, password: string) {
try { try {
const response = await axios.post(`${config.server_endpoint}/login`, { username, password }) const response = await axios.post(`${config.server_endpoint}/login`, { username, password })
useCookies().set('token', response.data.token as string, { useCookies().set('token', response.data.token as string, {
// for whole domain domain: getDomain()
// @TODO : #190
// domain: window.location.hostname.split('.').slice(-2).join('.')
}) })
return { success: true, token: response.data.token } return { success: true, token: response.data.token }
} catch (error: any) { } catch (error: any) {
@ -31,6 +33,9 @@ export async function resetPassword(email: string) {
const response = await axios.post(`${config.server_endpoint}/reset-password`, { email }) const response = await axios.post(`${config.server_endpoint}/reset-password`, { email })
return { success: true, token: response.data.token } return { success: true, token: response.data.token }
} catch (error: any) { } catch (error: any) {
if (typeof error.response.data === 'undefined') {
return { error: 'Could not connect to server' }
}
return { error: error.response.data.message } return { error: error.response.data.message }
} }
} }

View File

@ -0,0 +1,69 @@
import config from '@/config'
import Dexie from 'dexie'
import type { AssetDataT } from '@/types'
export class AssetStorage {
private db: Dexie
constructor() {
this.db = new Dexie('assets')
this.db.version(1).stores({
assets: 'key, group'
})
}
async download(asset: AssetDataT) {
try {
// Check if the asset already exists, then check if updatedAt is newer
const _asset = await this.db.table('assets').get(asset.key)
if (_asset && _asset.updatedAt > asset.updatedAt) {
return
}
// Download the asset
const response = await fetch(config.server_endpoint + asset.data)
const blob = await response.blob()
// Store the asset in the database
await this.db.table('assets').put({ key: asset.key, data: blob, group: asset.group, updatedAt: asset.updatedAt, frameCount: asset.frameCount, frameWidth: asset.frameWidth, frameHeight: asset.frameHeight })
} catch (error) {
console.error(`Failed to add asset ${asset.key}:`, error)
}
}
async get(key: string) {
try {
const asset = await this.db.table('assets').get(key)
if (asset) {
return {
...asset,
data: URL.createObjectURL(asset.data) // Convert blob to data URL
}
}
} catch (error) {
console.error(`Failed to retrieve asset ${key}:`, error)
}
return null
}
async getByGroup(group: string) {
try {
const assets = await this.db.table('assets').where('group').equals(group).toArray()
return assets.map((asset) => ({
...asset,
data: URL.createObjectURL(asset.data) // Convert blob to data URL
}))
} catch (error) {
console.error(`Failed to retrieve assets for group ${group}:`, error)
return []
}
}
async delete(key: string) {
try {
await this.db.table('assets').delete(key)
} catch (error) {
console.error(`Failed to delete asset ${key}:`, error)
}
}
}

View File

@ -1,27 +1,29 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { io, Socket } from 'socket.io-client' import { io, Socket } from 'socket.io-client'
import type { Asset, Character, Notification, User, WorldSettings } from '@/types' import type { AssetDataT, Character, Notification, User, WorldSettings } from '@/types'
import config from '@/config' import config from '@/config'
import { useCookies } from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
import { getDomain } from '@/utilities'
export const useGameStore = defineStore('game', { export const useGameStore = defineStore('game', {
state: () => { state: () => {
return { return {
notifications: [] as Notification[], notifications: [] as Notification[],
isAssetsLoaded: false,
loadedAssets: [] as string[],
token: '' as string | null, token: '' as string | null,
connection: null as Socket | null, connection: null as Socket | null,
user: null as User | null, user: null as User | null,
character: null as Character | null, character: null as Character | null,
isPlayerDraggingCamera: false,
world: { world: {
date: new Date(), date: new Date(),
isRainEnabled: false, isRainEnabled: false,
isFogEnabled: false, isFogEnabled: false,
fogDensity: 0.5 fogDensity: 0.5
} as WorldSettings, } as WorldSettings,
gameSettings: { game: {
isLoading: false,
isLoaded: false, // isLoaded is currently being used to determine if the player has interacted with the game
loadedAssets: [] as AssetDataT[],
isPlayerDraggingCamera: false,
isCameraFollowingCharacter: false isCameraFollowingCharacter: false
}, },
uiSettings: { uiSettings: {
@ -31,6 +33,17 @@ export const useGameStore = defineStore('game', {
} }
} }
}, },
getters: {
getLoadedAssets: (state) => {
return state.game.loadedAssets
},
getLoadedAsset: (state) => {
return (key: string) => state.game.loadedAssets.find((asset) => asset.key === key)
},
getLoadedAssetsByGroup: (state) => {
return (group: string) => state.game.loadedAssets.filter((asset) => asset.group === group)
}
},
actions: { actions: {
addNotification(notification: Notification) { addNotification(notification: Notification) {
if (!notification.id) { if (!notification.id) {
@ -53,17 +66,8 @@ export const useGameStore = defineStore('game', {
toggleGmPanel() { toggleGmPanel() {
this.uiSettings.isGmPanelOpen = !this.uiSettings.isGmPanelOpen this.uiSettings.isGmPanelOpen = !this.uiSettings.isGmPanelOpen
}, },
togglePlayerDraggingCamera() {
this.isPlayerDraggingCamera = !this.isPlayerDraggingCamera
},
setPlayerDraggingCamera(moving: boolean) { setPlayerDraggingCamera(moving: boolean) {
this.isPlayerDraggingCamera = moving this.game.isPlayerDraggingCamera = moving
},
toggleCameraFollowingCharacter() {
this.gameSettings.isCameraFollowingCharacter = !this.gameSettings.isCameraFollowingCharacter
},
setCameraFollowingCharacter(following: boolean) {
this.gameSettings.isCameraFollowingCharacter = following
}, },
toggleChat() { toggleChat() {
this.uiSettings.isChatOpen = !this.uiSettings.isChatOpen this.uiSettings.isChatOpen = !this.uiSettings.isChatOpen
@ -101,21 +105,22 @@ export const useGameStore = defineStore('game', {
this.connection?.disconnect() this.connection?.disconnect()
useCookies().remove('token', { useCookies().remove('token', {
// for whole domain domain: getDomain()
// @TODO : #190
// domain: window.location.hostname.split('.').slice(-2).join('.')
}) })
this.isAssetsLoaded = false
this.connection = null this.connection = null
this.token = null this.token = null
this.user = null this.user = null
this.character = null this.character = null
this.uiSettings.isGmPanelOpen = false
this.isPlayerDraggingCamera = false this.game.isLoaded = false
this.gameSettings.isCameraFollowingCharacter = false this.game.loadedAssets = []
this.game.isPlayerDraggingCamera = false
this.game.isCameraFollowingCharacter = false
this.uiSettings.isChatOpen = false this.uiSettings.isChatOpen = false
this.uiSettings.isCharacterProfileOpen = false this.uiSettings.isCharacterProfileOpen = false
this.uiSettings.isGmPanelOpen = false
this.world.date = new Date() this.world.date = new Date()
this.world.isRainEnabled = false this.world.isRainEnabled = false

View File

@ -4,11 +4,12 @@ export type Notification = {
message?: string message?: string
} }
export type AssetT = { export type AssetDataT = {
key: string key: string
url: string data: string
group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other' group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other'
updatedAt: Date updatedAt: Date
isAnimated?: boolean
frameCount?: number frameCount?: number
frameWidth?: number frameWidth?: number
frameHeight?: number frameHeight?: number

View File

@ -5,3 +5,21 @@ export function uuidv4() {
export function unduplicateArray(array: any[]) { export function unduplicateArray(array: any[]) {
return [...new Set(array.flat())] return [...new Set(array.flat())]
} }
export function getDomain() {
// Check if not localhost
if (window.location.hostname !== 'localhost') {
return window.location.hostname
}
// Check if not IP address
if (window.location.hostname.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
return window.location.hostname
}
if (window.location.hostname.split('.').length < 3) {
return window.location.hostname
}
return window.location.hostname.split('.').slice(-2).join('.')
}

View File

@ -2,12 +2,14 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import VueDevTools from 'vite-plugin-vue-devtools' import VueDevTools from 'vite-plugin-vue-devtools'
import viteCompression from 'vite-plugin-compression';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
vue(), vue(),
VueDevTools(), VueDevTools(),
viteCompression()
], ],
resolve: { resolve: {
alias: { alias: {