From 29675c7c0bea3a0f6c4ea4b0eddcde2417d666c8 Mon Sep 17 00:00:00 2001 From: Walidoux Date: Sat, 22 Jul 2023 13:34:09 +0100 Subject: [PATCH] =?UTF-8?q?init(=F0=9F=8E=89):=20new=20habbo=20renderer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 10 + .gitignore | 4 + .parcelrc | 8 + .vscode/settings.json | 10 + package.json | 47 + pnpm-lock.yaml | 4645 +++++++++++++++++ public/index.html | 31 + public/script.js | 61 + public/script2.js | 427 ++ public/script3.js | 443 ++ public/script4.js | 166 + src/Scuti.ts | 126 + src/enums/Direction.ts | 16 + src/enums/StairType.ts | 11 + src/enums/WallType.ts | 12 + src/index.ts | 8 + src/objects/avatars/Avatar.ts | 98 + src/objects/avatars/AvatarFigure.ts | 79 + src/objects/avatars/actions/AvatarAction.ts | 30 + .../avatars/actions/AvatarActionManager.ts | 48 + .../avatars/animations/AvatarAnimation.ts | 15 + .../animations/AvatarAnimationManager.ts | 33 + .../avatars/visualizations/AvatarBodyPart.ts | 231 + .../avatars/visualizations/AvatarLayer.ts | 110 + .../visualizations/AvatarVisualization.ts | 134 + src/objects/filters/WiredSelectionFilter.ts | 52 + src/objects/furnitures/FloorFurniture.ts | 65 + src/objects/furnitures/WallFurniture.ts | 64 + .../visualizations/FurnitureData.ts | 97 + .../visualizations/FurnitureLayer.ts | 202 + .../visualizations/FurnitureVisualization.ts | 283 + src/objects/interactions/EventManager.ts | 416 ++ src/objects/interactions/HitSprite.ts | 71 + src/objects/interactions/HitTexture.ts | 290 + src/objects/rooms/Room.ts | 333 ++ src/objects/rooms/RoomCamera.ts | 269 + src/objects/rooms/RoomTileMap.ts | 325 ++ src/objects/rooms/RoomVisualization.ts | 404 ++ src/objects/rooms/layers/RoomObjectLayer.ts | 72 + src/objects/rooms/layers/RoomPartLayer.ts | 104 + src/objects/rooms/materials/FloorMaterial.ts | 73 + src/objects/rooms/materials/Material.ts | 76 + src/objects/rooms/materials/WallMaterial.ts | 73 + src/objects/rooms/objects/RoomObject.ts | 547 ++ .../rooms/objects/RoomObjectVisualization.ts | 76 + src/objects/rooms/parts/Cursor.ts | 69 + src/objects/rooms/parts/RoomPart.ts | 228 + src/objects/rooms/parts/Stair.ts | 579 ++ src/objects/rooms/parts/Tile.ts | 219 + src/objects/rooms/parts/Wall.ts | 314 ++ src/objects/rooms/parts/index.ts | 4 + src/types/Avatar.d.ts | 93 + src/types/Configuration.d.ts | 14 + src/types/Dimension.d.ts | 15 + src/types/Figure.d.ts | 52 + src/types/Furniture.d.ts | 100 + src/types/Global.d.ts | 1 + src/types/Interaction.d.ts | 10 + src/types/Room.d.ts | 108 + src/types/RoomMaterial.d.ts | 54 + src/types/index.d.ts | 8 + src/utilities/AssetLoader.ts | 26 + src/utilities/Logger.ts | 107 + src/utilities/ZOrder.ts | 102 + tsconfig.json | 21 + 65 files changed, 12819 insertions(+) create mode 100755 .editorconfig create mode 100644 .gitignore create mode 100644 .parcelrc create mode 100644 .vscode/settings.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 public/index.html create mode 100644 public/script.js create mode 100644 public/script2.js create mode 100644 public/script3.js create mode 100644 public/script4.js create mode 100644 src/Scuti.ts create mode 100644 src/enums/Direction.ts create mode 100644 src/enums/StairType.ts create mode 100644 src/enums/WallType.ts create mode 100644 src/index.ts create mode 100644 src/objects/avatars/Avatar.ts create mode 100644 src/objects/avatars/AvatarFigure.ts create mode 100644 src/objects/avatars/actions/AvatarAction.ts create mode 100644 src/objects/avatars/actions/AvatarActionManager.ts create mode 100644 src/objects/avatars/animations/AvatarAnimation.ts create mode 100644 src/objects/avatars/animations/AvatarAnimationManager.ts create mode 100644 src/objects/avatars/visualizations/AvatarBodyPart.ts create mode 100644 src/objects/avatars/visualizations/AvatarLayer.ts create mode 100644 src/objects/avatars/visualizations/AvatarVisualization.ts create mode 100644 src/objects/filters/WiredSelectionFilter.ts create mode 100644 src/objects/furnitures/FloorFurniture.ts create mode 100644 src/objects/furnitures/WallFurniture.ts create mode 100644 src/objects/furnitures/visualizations/FurnitureData.ts create mode 100644 src/objects/furnitures/visualizations/FurnitureLayer.ts create mode 100644 src/objects/furnitures/visualizations/FurnitureVisualization.ts create mode 100644 src/objects/interactions/EventManager.ts create mode 100644 src/objects/interactions/HitSprite.ts create mode 100644 src/objects/interactions/HitTexture.ts create mode 100644 src/objects/rooms/Room.ts create mode 100644 src/objects/rooms/RoomCamera.ts create mode 100644 src/objects/rooms/RoomTileMap.ts create mode 100644 src/objects/rooms/RoomVisualization.ts create mode 100644 src/objects/rooms/layers/RoomObjectLayer.ts create mode 100644 src/objects/rooms/layers/RoomPartLayer.ts create mode 100644 src/objects/rooms/materials/FloorMaterial.ts create mode 100644 src/objects/rooms/materials/Material.ts create mode 100644 src/objects/rooms/materials/WallMaterial.ts create mode 100644 src/objects/rooms/objects/RoomObject.ts create mode 100644 src/objects/rooms/objects/RoomObjectVisualization.ts create mode 100644 src/objects/rooms/parts/Cursor.ts create mode 100644 src/objects/rooms/parts/RoomPart.ts create mode 100644 src/objects/rooms/parts/Stair.ts create mode 100644 src/objects/rooms/parts/Tile.ts create mode 100644 src/objects/rooms/parts/Wall.ts create mode 100644 src/objects/rooms/parts/index.ts create mode 100644 src/types/Avatar.d.ts create mode 100644 src/types/Configuration.d.ts create mode 100644 src/types/Dimension.d.ts create mode 100644 src/types/Figure.d.ts create mode 100644 src/types/Furniture.d.ts create mode 100644 src/types/Global.d.ts create mode 100644 src/types/Interaction.d.ts create mode 100644 src/types/Room.d.ts create mode 100644 src/types/RoomMaterial.d.ts create mode 100644 src/types/index.d.ts create mode 100644 src/utilities/AssetLoader.ts create mode 100644 src/utilities/Logger.ts create mode 100644 src/utilities/ZOrder.ts create mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..ae10a5c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed7dc6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +.parcel-cache +dist +.github diff --git a/.parcelrc b/.parcelrc new file mode 100644 index 0000000..d11e779 --- /dev/null +++ b/.parcelrc @@ -0,0 +1,8 @@ +{ + "extends": "@parcel/config-default", + "transformers": { + "*.ts": [ + "@parcel/transformer-typescript-tsc" + ] + } +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e19d258 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.tsdk": "node_modules/typescript/lib", + + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c148c6f --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "renderer", + "version": "0.0.0", + "main": "dist/index.js", + "types": "dist/types.d.ts", + "source": "src/index.ts", + "license": "MIT", + "scripts": { + "dev": "parcel public/index.html --no-cache --open", + "build": "parcelb", + "lint:typescript": "eslint \"**/*.ts\" --ignore-path \".gitignore\" && tsc --noemit", + "lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"" + }, + "dependencies": { + "@pixi/color": "7.2.4", + "@pixi/layers": "2.1.0", + "@pixi/utils": "^7.2.4", + "gsap": "^3.12.2", + "pixi.js": "^7.2.4" + }, + "devDependencies": { + "@parcel/config-default": "^2.9.3", + "@parcel/transformer-typescript-tsc": "^2.9.3", + "@types/node": "^20.4.3", + "@walidoux/eslint-config": "1.0.3", + "@walidoux/prettier-config": "1.0.3", + "parcel": "^2.9.3", + "process": "^0.11.10", + "punycode": "1.4.1", + "querystring-es3": "^0.2.1", + "timers-browserify": "^2.0.12", + "ts-node": "^10.9.1", + "ts-node-dev": "^2.0.0", + "eslint": "8.45.0", + "prettier": "2.8.8", + "typescript": "^5.1.6" + }, + "peerDependencies": { + "url": "0.11.1" + }, + "prettier": "@walidoux/prettier-config", + "eslintConfig": { + "extends": [ + "@walidoux/eslint-config" + ] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8b2468f --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4645 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@pixi/color': + specifier: 7.2.4 + version: 7.2.4 + '@pixi/layers': + specifier: 2.1.0 + version: 2.1.0(@pixi/canvas-renderer@7.2.4)(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/utils': + specifier: ^7.2.4 + version: 7.2.4 + gsap: + specifier: ^3.12.2 + version: 3.12.2 + pixi.js: + specifier: ^7.2.4 + version: 7.2.4(@pixi/utils@7.2.4) + url: + specifier: 0.11.1 + version: 0.11.1 + +devDependencies: + '@parcel/config-default': + specifier: ^2.9.3 + version: 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-typescript-tsc': + specifier: ^2.9.3 + version: 2.9.3(@parcel/core@2.9.3)(typescript@5.1.6) + '@types/node': + specifier: ^20.4.3 + version: 20.4.3 + '@walidoux/eslint-config': + specifier: 1.0.3 + version: 1.0.3(eslint@8.45.0)(prettier@2.8.8)(typescript@5.1.6) + '@walidoux/prettier-config': + specifier: 1.0.3 + version: 1.0.3(prettier@2.8.8) + eslint: + specifier: 8.45.0 + version: 8.45.0 + parcel: + specifier: ^2.9.3 + version: 2.9.3 + prettier: + specifier: 2.8.8 + version: 2.8.8 + process: + specifier: ^0.11.10 + version: 0.11.10 + punycode: + specifier: 1.4.1 + version: 1.4.1 + querystring-es3: + specifier: ^0.2.1 + version: 0.2.1 + timers-browserify: + specifier: ^2.0.12 + version: 2.0.12 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.4.3)(typescript@5.1.6) + ts-node-dev: + specifier: ^2.0.0 + version: 2.0.0(@types/node@20.4.3)(typescript@5.1.6) + typescript: + specifier: ^5.1.6 + version: 5.1.6 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@babel/code-frame@7.22.5: + resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.5 + dev: true + + /@babel/helper-validator-identifier@7.22.5: + resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.22.5: + resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.45.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.45.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@eslint-community/regexpp@4.6.0: + resolution: {integrity: sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.0: + resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.44.0: + resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.10: + resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@lezer/common@0.15.12: + resolution: {integrity: sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==} + dev: true + + /@lezer/lr@0.15.8: + resolution: {integrity: sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==} + dependencies: + '@lezer/common': 0.15.12 + dev: true + + /@lmdb/lmdb-darwin-arm64@2.7.11: + resolution: {integrity: sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@lmdb/lmdb-darwin-x64@2.7.11: + resolution: {integrity: sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@lmdb/lmdb-linux-arm64@2.7.11: + resolution: {integrity: sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@lmdb/lmdb-linux-arm@2.7.11: + resolution: {integrity: sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@lmdb/lmdb-linux-x64@2.7.11: + resolution: {integrity: sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@lmdb/lmdb-win32-x64@2.7.11: + resolution: {integrity: sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@mischnic/json-sourcemap@0.1.0: + resolution: {integrity: sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==} + engines: {node: '>=12.0.0'} + dependencies: + '@lezer/common': 0.15.12 + '@lezer/lr': 0.15.8 + json5: 2.2.3 + dev: true + + /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: + resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: + resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: + resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: + resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: + resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: + resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@parcel/bundler-default@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-JjJK8dq39/UO/MWI/4SCbB1t/qgpQRFnFDetAAAezQ8oN++b24u1fkMDa/xqQGjbuPmGeTds5zxGgYs7id7PYg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/cache@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/logger': 2.9.3 + '@parcel/utils': 2.9.3 + lmdb: 2.7.11 + dev: true + + /@parcel/codeframe@2.9.3: + resolution: {integrity: sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA==} + engines: {node: '>= 12.0.0'} + dependencies: + chalk: 4.1.2 + dev: true + + /@parcel/compressor-raw@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-jz3t4/ICMsHEqgiTmv5i1DJva2k5QRpZlBELVxfY+QElJTVe8edKJ0TiKcBxh2hx7sm4aUigGmp7JiqqHRRYmA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/config-default@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-tqN5tF7QnVABDZAu76co5E6N8mA9n8bxiWdK4xYyINYFIEHgX172oRTqXTnhEMjlMrdmASxvnGlbaPBaVnrCTw==} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/bundler-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/compressor-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/core': 2.9.3 + '@parcel/namer-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-htmlnano': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-image': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-svgo': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-swc': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-html': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-svg': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-dev-server': 2.9.3(@parcel/core@2.9.3) + '@parcel/resolver-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-browser-hmr': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-react-refresh': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-service-worker': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-babel': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-html': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-image': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-json': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-postcss': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-posthtml': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-react-refresh-wrap': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-svg': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - uncss + dev: true + + /@parcel/core@2.9.3: + resolution: {integrity: sha512-4KlM1Zr/jpsqWuMXr2zmGsaOUs1zMMFh9vfCNKRZkptf+uk8I3sugHbNdo+F5B+4e2yMuOEb1zgAmvJLeuH6ww==} + engines: {node: '>= 12.0.0'} + dependencies: + '@mischnic/json-sourcemap': 0.1.0 + '@parcel/cache': 2.9.3(@parcel/core@2.9.3) + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/profiler': 2.9.3 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + abortcontroller-polyfill: 1.7.5 + base-x: 3.0.9 + browserslist: 4.21.9 + clone: 2.1.2 + dotenv: 7.0.0 + dotenv-expand: 5.1.0 + json5: 2.2.3 + msgpackr: 1.9.5 + nullthrows: 1.1.1 + semver: 7.5.4 + dev: true + + /@parcel/diagnostic@2.9.3: + resolution: {integrity: sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA==} + engines: {node: '>= 12.0.0'} + dependencies: + '@mischnic/json-sourcemap': 0.1.0 + nullthrows: 1.1.1 + dev: true + + /@parcel/events@2.9.3: + resolution: {integrity: sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A==} + engines: {node: '>= 12.0.0'} + dev: true + + /@parcel/fs-search@2.9.3: + resolution: {integrity: sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q==} + engines: {node: '>= 12.0.0'} + dev: true + + /@parcel/fs@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs-search': 2.9.3 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/watcher': 2.2.0 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + dev: true + + /@parcel/graph@2.9.3: + resolution: {integrity: sha512-3LmRJmF8+OprAr6zJT3X2s8WAhLKkrhi6RsFlMWHifGU5ED1PFcJWFbOwJvSjcAhMQJP0fErcFIK1Ludv3Vm3g==} + engines: {node: '>= 12.0.0'} + dependencies: + nullthrows: 1.1.1 + dev: true + + /@parcel/hash@2.9.3: + resolution: {integrity: sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ==} + engines: {node: '>= 12.0.0'} + dependencies: + xxhash-wasm: 0.4.2 + dev: true + + /@parcel/logger@2.9.3: + resolution: {integrity: sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + dev: true + + /@parcel/markdown-ansi@2.9.3: + resolution: {integrity: sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ==} + engines: {node: '>= 12.0.0'} + dependencies: + chalk: 4.1.2 + dev: true + + /@parcel/namer-default@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-1ynFEcap48/Ngzwwn318eLYpLUwijuuZoXQPCsEQ21OOIOtfhFQJaPwXTsw6kRitshKq76P2aafE0BioGSqxcA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/node-resolver-core@3.0.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A==} + engines: {node: '>= 12.0.0'} + dependencies: + '@mischnic/json-sourcemap': 0.1.0 + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-css@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-RK1QwcSdWDNUsFvuLy0hgnYKtPQebzCb0vPPzqs6LhL+vqUu9utOyRycGaQffHCkHVQP6zGlN+KFssd7YtFGhA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.21.9 + lightningcss: 1.21.5 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-htmlnano@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-9g/KBck3c6DokmJfvJ5zpHFBiCSolaGrcsTGx8C3YPdCTVTI9P1TDCwUxvAr4LjpcIRSa82wlLCI+nF6sSgxKA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + htmlnano: 2.0.4(svgo@2.8.0) + nullthrows: 1.1.1 + posthtml: 0.16.6 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - uncss + dev: true + + /@parcel/optimizer-image@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-530YzthE7kmecnNhPbkAK+26yQNt69pfJrgE0Ev0BZaM1Wu2+33nki7o8qvkTkikhPrurEJLGIXt1qKmbKvCbA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + dev: true + + /@parcel/optimizer-svgo@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-ytQS0wY5JJhWU4mL0wfhYDUuHcfuw+Gy2+JcnTm1t1AZXHlOTbU6EzRWNqBShsgXjvdrQQXizAe3B6GFFlFJVQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-swc@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-GQINNeqtdpL1ombq/Cpwi6IBk02wKJ/JJbYbyfHtk8lxlq13soenpwOlzJ5T9D2fdG+FUhai9NxpN5Ss4lNoAg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + '@swc/core': 1.3.70 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' + dev: true + + /@parcel/package-manager@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/logger': 2.9.3 + '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + semver: 7.5.4 + dev: true + + /@parcel/packager-css@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-mePiWiYZOULY6e1RdAIJyRoYqXqGci0srOaVZYaP7mnrzvJgA63kaZFFsDiEWghunQpMUuUjM2x/vQVHzxmhKQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-html@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-0Ex+O0EaZf9APNERRNGgGto02hFJ6f5RQEvRWBK55WAV1rXeU+kpjC0c0qZvnUaUtXfpWMsEBkevJCwDkUMeMg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-js@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-V5xwkoE3zQ3R+WqAWhA1KGQ791FvJeW6KonOlMI1q76Djjgox68hhObqcLu66AmYNhR2R/wUpkP18hP2z8dSFw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + globals: 13.20.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-raw@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-oPQTNoYanQ2DdJyL61uPYK2py83rKOT8YVh2QWAx0zsSli6Kiy64U3+xOCYWgDVCrHw9+9NpQMuAdSiFg4cq8g==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-svg@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-p/Ya6UO9DAkaCUFxfFGyeHZDp9YPAlpdnh1OChuwqSFOXFjjeXuoK4KLT+ZRalVBo2Jo8xF70oKMZw4MVvaL7Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/plugin@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/profiler@2.9.3: + resolution: {integrity: sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + chrome-trace-event: 1.0.3 + dev: true + + /@parcel/reporter-cli@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-pZiEvQpuXFuQBafMHxkDmwH8CnnK9sWHwa3bSbsnt385aUahtE8dpY0LKt+K1zfB6degKoczN6aWVj9WycQuZQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + chalk: 4.1.2 + term-size: 2.2.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/reporter-dev-server@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-s6eboxdLEtRSvG52xi9IiNbcPKC0XMVmvTckieue2EqGDbDcaHQoHmmwkk0rNq0/Z/UxelGcQXoIYC/0xq3ykQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/reporter-tracer@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-9cXpKWk0m6d6d+4+TlAdOe8XIPaFEIKGWMWG+5SFAQE08u3olet4PSvd49F4+ZZo5ftRE7YI3j6xNbXvJT8KGw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + chrome-trace-event: 1.0.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/resolver-default@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-8ESJk1COKvDzkmOnppNXoDamNMlYVIvrKc2RuFPmp8nKVj47R6NwMgvwxEaatyPzvkmyTpq5RvG9I3HFc+r4Cw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-browser-hmr@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-EgiDIDrVAWpz7bOzWXqVinQkaFjLwT34wsonpXAbuI7f7r00d52vNAQC9AMu+pTijA3gyKoJ+Q4NWPMZf7ACDA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-js@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-EvIy+qXcKnB5qxHhe96zmJpSAViNVXHfQI5RSdZ2a7CPwORwhTI+zPNT9sb7xb/WwFw/WuTTgzT40b41DceU6Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-react-refresh@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-XBgryZQIyCmi6JwEfMUCmINB3l1TpTp9a2iFxmYNpzHlqj4Ve0saKaqWOVRLvC945ZovWIBzcSW2IYqWKGtbAA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + react-error-overlay: 6.0.9 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-service-worker@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-qLJLqv1mMdWL7gyh8aKBFFAuEiJkhUUgLKpdn6eSfH/R7kTtb76WnOwqUrhvEI9bZFUM/8Pa1bzJnPpqSOM+Sw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/source-map@2.1.1: + resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} + engines: {node: ^12.18.3 || >=14} + dependencies: + detect-libc: 1.0.3 + dev: true + + /@parcel/transformer-babel@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-pURtEsnsp3h6tOBDuzh9wRvVtw4PgIlqwAArIWdrG7iwqOUYv9D8ME4+ePWEu7MQWAp58hv9pTJtqWv4T+Sq8A==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.21.9 + json5: 2.2.3 + nullthrows: 1.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-css@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-duWMdbEBBPjg3fQdXF16iWIdThetDZvCs2TpUD7xOlXH6kR0V5BJy8ONFT15u1RCqIV9hSNGaS3v3I9YRNY5zQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.21.9 + lightningcss: 1.21.5 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-html@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-0NU4omcHzFXA1seqftAXA2KNZaMByoKaNdXnLgBgtCGDiYvOcL+6xGHgY6pw9LvOh5um10KI5TxSIMILoI7VtA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.5.4 + srcset: 4.0.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-image@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-7CEe35RaPadQzLIuxzTtIxnItvOoy46hcbXtOdDt6lmVa4omuOygZYRIya2lsGIP4JHvAaALMb5nt99a1uTwJg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + dev: true + + /@parcel/transformer-js@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-Z2MVVg5FYcPOfxlUwxqb5l9yjTMEqE3KI3zq2MBRUme6AV07KxLmCDF23b6glzZlHWQUE8MXzYCTAkOPCcPz+Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + '@swc/helpers': 0.5.1 + browserslist: 4.21.9 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.11 + semver: 7.5.4 + dev: true + + /@parcel/transformer-json@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-yNL27dbOLhkkrjaQjiQ7Im9VOxmkfuuSNSmS0rA3gEjVcm07SLKRzWkAaPnyx44Lb6bzyOTWwVrb9aMmxgADpA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + json5: 2.2.3 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-postcss@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-HoDvPqKzhpmvMmHqQhDnt8F1vH61m6plpGiYaYnYv2Om4HHi5ZIq9bO+9QLBnTKfaZ7ndYSefTKOxTYElg7wyw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + clone: 2.1.2 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + semver: 7.5.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-posthtml@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-2fQGgrzRmaqbWf3y2/T6xhqrNjzqMMKksqJzvc8TMfK6f2kg3Ddjv158eaSW2JdkV39aY7tvAOn5f1uzo74BMA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.5.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-raw@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-oqdPzMC9QzWRbY9J6TZEqltknjno+dY24QWqf8ondmdF2+W+/2mRDu59hhCzQrqUHgTq4FewowRZmSfpzHxwaQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-react-refresh-wrap@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-cb9NyU6oJlDblFIlzqIE8AkvRQVGl2IwJNKwD4PdE7Y6sq2okGEPG4hOw3k/Y9JVjM4/2pUORqvjSRhWwd9oVQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-svg@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-ypmE+dzB09IMCdEAkOsSxq1dEIm2A3h67nAFz4qbfHbwNgXBUuy/jB3ZMwXN/cO0f7SBh/Ap8Jhq6vmGqB5tWw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.5.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-typescript-tsc@2.9.3(@parcel/core@2.9.3)(typescript@5.1.6): + resolution: {integrity: sha512-kQUts/PVFhEb9JoyJBlKvaa5qR/3bEiyZSjn8qE3S+97CUOIVun8fNxGiDPwpaPd90BgPcgU43gSAQwlY9rIMQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + typescript: '>=3.0.0' + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/ts-utils': 2.9.3(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/ts-utils@2.9.3(typescript@5.1.6): + resolution: {integrity: sha512-MiQoXFV8I4IWZT/q5yolKN/gnEY5gZfGB2X7W9WHJbRgyjlT/A5cPERXzVBj6mc3/VM1GdZJz76w637GUcQhow==} + engines: {node: '>= 12.0.0'} + peerDependencies: + typescript: '>=3.0.0' + dependencies: + nullthrows: 1.1.1 + typescript: 5.1.6 + dev: true + + /@parcel/types@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA==} + dependencies: + '@parcel/cache': 2.9.3(@parcel/core@2.9.3) + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + utility-types: 3.10.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/utils@2.9.3: + resolution: {integrity: sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/codeframe': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/markdown-ansi': 2.9.3 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + nullthrows: 1.1.1 + dev: true + + /@parcel/watcher-android-arm64@2.2.0: + resolution: {integrity: sha512-nU2wh00CTQT9rr1TIKTjdQ9lAGYpmz6XuKw0nAwAN+S2A5YiD55BK1u+E5WMCT8YOIDe/n6gaj4o/Bi9294SSQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-darwin-arm64@2.2.0: + resolution: {integrity: sha512-cJl0UZDcodciy3TDMomoK/Huxpjlkkim3SyMgWzjovHGOZKNce9guLz2dzuFwfObBFCjfznbFMIvAZ5syXotYw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-darwin-x64@2.2.0: + resolution: {integrity: sha512-QI77zxaGrCV1StKcoRYfsUfmUmvPMPfQrubkBBy5XujV2fwaLgZivQOTQMBgp5K2+E19u1ufpspKXAPqSzpbyg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-arm-glibc@2.2.0: + resolution: {integrity: sha512-I2GPBcAXazPzabCmfsa3HRRW+MGlqxYd8g8RIueJU+a4o5nyNZDz0CR1cu0INT0QSQXEZV7w6UE8Hz9CF8u3Pg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-arm64-glibc@2.2.0: + resolution: {integrity: sha512-St5mlfp+2lS9AmgixUqfwJa/DwVmTCJxC1HcOubUTz6YFOKIlkHCeUa1Bxi4E/tR/HSez8+heXHL8HQkJ4Bd8g==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-arm64-musl@2.2.0: + resolution: {integrity: sha512-jS+qfhhoOBVWwMLP65MaG8xdInMK30pPW8wqTCg2AAuVJh5xepMbzkhHJ4zURqHiyY3EiIRuYu4ONJKCxt8iqA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-x64-glibc@2.2.0: + resolution: {integrity: sha512-xJvJ7R2wJdi47WZBFS691RDOWvP1j/IAs3EXaWVhDI8FFITbWrWaln7KoNcR0Y3T+ZwimFY/cfb0PNht1q895g==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-linux-x64-musl@2.2.0: + resolution: {integrity: sha512-D+NMpgr23a+RI5mu8ZPKWy7AqjBOkURFDgP5iIXXEf/K3hm0jJ3ogzi0Ed2237B/CdYREimCgXyeiAlE/FtwyA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-win32-arm64@2.2.0: + resolution: {integrity: sha512-z225cPn3aygJsyVUOWwfyW+fY0Tvk7N3XCOl66qUPFxpbuXeZuiuuJemmtm8vxyqa3Ur7peU/qJxrpC64aeI7Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher-win32-x64@2.2.0: + resolution: {integrity: sha512-JqGW0RJ61BkKx+yYzIURt9s53P7xMVbv0uxYPzAXLBINGaFmkIKSuUPyBVfy8TMbvp93lvF4SPBNDzVRJfvgOw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@parcel/watcher@2.2.0: + resolution: {integrity: sha512-71S4TF+IMyAn24PK4KSkdKtqJDR3zRzb0HE3yXpacItqTM7XfF2f5q9NEGLEVl0dAaBAGfNwDCjH120y25F6Tg==} + engines: {node: '>= 10.0.0'} + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.5 + node-addon-api: 7.0.0 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.2.0 + '@parcel/watcher-darwin-arm64': 2.2.0 + '@parcel/watcher-darwin-x64': 2.2.0 + '@parcel/watcher-linux-arm-glibc': 2.2.0 + '@parcel/watcher-linux-arm64-glibc': 2.2.0 + '@parcel/watcher-linux-arm64-musl': 2.2.0 + '@parcel/watcher-linux-x64-glibc': 2.2.0 + '@parcel/watcher-linux-x64-musl': 2.2.0 + '@parcel/watcher-win32-arm64': 2.2.0 + '@parcel/watcher-win32-x64': 2.2.0 + dev: true + + /@parcel/workers@2.9.3(@parcel/core@2.9.3): + resolution: {integrity: sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/profiler': 2.9.3 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + dev: true + + /@pixi/accessibility@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/events@7.2.4): + resolution: {integrity: sha512-EVjuqUqv9FeYFXCv0S0qj1hgCtbAMNBPCbOGEtiMogpM++/IySxBZvcOYg3rRgo9inwt2s4Bi7kUiqMPD8hItw==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/events': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/events': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/app@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-eJ2jpu5P28ip07nLItw6sETXn45P4KR/leMJ6zPHRlhT1m8t5zTsWr3jK4Uj8LF2E+6KlPNzLQh5Alf/unn/aQ==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/assets@7.2.4(@pixi/core@7.2.4)(@pixi/utils@7.2.4): + resolution: {integrity: sha512-7199re3wvMAlVqXLaCyAr8IkJSXqkeVAxcYyB2rBu4Id5m2hhlGX1dQsdMBiCXLwu6/LLVqDvJggSNVQBzL6ZQ==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/utils': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/utils': 7.2.4 + '@types/css-font-loading-module': 0.0.7 + dev: false + + /@pixi/canvas-renderer@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-H1E7HjU/5tpSS6F695HwiJiwf2M8YXrNsAkwBVNZRw7yVJ66LUMc8ofyIvlcGcZVFObtl7E+p4DaUjiibnx6fQ==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/color@7.2.4: + resolution: {integrity: sha512-B/+9JRcXe2uE8wQfsueFRPZVayF2VEMRB7XGeRAsWCryOX19nmWhv0Nt3nOU2rvzI0niz9XgugJXsB6vVmDFSg==} + dependencies: + colord: 2.9.3 + dev: false + + /@pixi/compressed-textures@7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4): + resolution: {integrity: sha512-atnWyw/ot/Wg69qhgskKiuTYCZx15IxV35sa0KyXMthyjyvDLCIvOn0nczM6wCBy9H96SjJbfgynVWhVrip6qw==} + peerDependencies: + '@pixi/assets': 7.2.4 + '@pixi/core': 7.2.4 + dependencies: + '@pixi/assets': 7.2.4(@pixi/core@7.2.4)(@pixi/utils@7.2.4) + '@pixi/core': 7.2.4 + dev: false + + /@pixi/constants@7.2.4: + resolution: {integrity: sha512-hKuHBWR6N4Q0Sf5MGF3/9l+POg/G5rqhueHfzofiuelnKg7aBs3BVjjZ+6hZbd6M++vOUmxYelEX/NEFBxrheA==} + dev: false + + /@pixi/core@7.2.4: + resolution: {integrity: sha512-0XtvrfxHlS2T+beBBSpo7GI8+QLyyTqMVQpNmPqB4woYxzrOEJ9JaUFBaBfCvycLeUkfVih1u6HAbtF+2d1EjQ==} + dependencies: + '@pixi/color': 7.2.4 + '@pixi/constants': 7.2.4 + '@pixi/extensions': 7.2.4 + '@pixi/math': 7.2.4 + '@pixi/runner': 7.2.4 + '@pixi/settings': 7.2.4 + '@pixi/ticker': 7.2.4 + '@pixi/utils': 7.2.4 + '@types/offscreencanvas': 2019.7.0 + dev: false + + /@pixi/display@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-w5tqb8cWEO5qIDaO9GEqRvxYhL0iMk0Wsngw23bbLm1gLEQmrFkB2tpJlRAqd7H82C3DrDDeWvkrrxW6+m4apg==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/events@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-/JtmoB98fzIU8giN9xvlRvmvOi6u4MaD2DnKNOMHkQ1MBraj3pmrXM9fZ0JbNzi+324GraAAY76QidgHjIYoYQ==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/extensions@7.2.4: + resolution: {integrity: sha512-Mnqv9scbL1ARD3QFKfOWs2aSVJJfP1dL8g5UiqGImYO3rZbz/9QCzXOeMVIZ5n3iaRyKMNhFFr84/zUja2H7Dw==} + dev: false + + /@pixi/extract@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-wlXZg+J2L/1jQhRi5nZQP/cXshovhjksjss91eAKMvY5aGxNAQovCP4xotJ/XJjfTvPMpeRzHPFYzm3PrOPQ7g==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-alpha@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-UTUMSGyktUr+I9vmigqJo9iUhb0nwGyqTTME2xBWZvVGCnl5z+/wHxvIBBCe5pNZ66IM15pGXQ4cDcfqCuP2kA==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-blur@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-aLyXIoxy14bTansCPtbY8x7Sdn2OrrqkF/pcKiRXHJGGhi7wPacvB/NcmYJdnI/n2ExQ6V5Njuj/nfrsejVwcA==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-color-matrix@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-DFtayybYXoUh73eHUFRK5REbi1t3FZuVUnaQTj+euHKF9L7EaYc3Q9wctpx1WPRcwkqEX50M4SNFhxpA7Pxtaw==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-displacement@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-Simq3IBJKt7+Gvk4kK7OFkfoeYUMhNhIyATCdeT+Jkdkq5WV7pYnH5hqO0YW7eAHrgjV13yn6t4H/GC4+6LhEA==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-fxaa@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-qzKjdL+Ih18uGTJLg8tT/H+YCsTeGkw2uF7lyKnw/lxGLJQhLWIhM95M9qSNgxbXyW1vp7SbG81a9aAEz2HAhA==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/filter-noise@7.2.4(@pixi/core@7.2.4): + resolution: {integrity: sha512-QAU9Ybj2ZQrWM9ZEjTTC0iLnQcuyNoZNRinxSbg1G0yacpmsSb9wvV5ltIZ66+hfY+90+u2Nudt/v9g6pvOdGg==} + peerDependencies: + '@pixi/core': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + dev: false + + /@pixi/graphics@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-3A2EumTjWJgXlDLOyuBrl9b6v1Za/E+/IjOGUIX843HH4NYaf1a2sfDfljx6r3oiDvy+VhuBFmgynRcV5IyA0Q==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/layers@2.1.0(@pixi/canvas-renderer@7.2.4)(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-e3p3xaHSkVF1jLTfiTEsFGNzGRPffTO0UkKbwA9pfBzRHIBOFM2mCbVqcdhgHSMXRFYr2FoOp1+zK9jJifEUug==} + peerDependencies: + '@pixi/canvas-renderer': ^7.0.0 + '@pixi/core': ^7.0.0 + '@pixi/display': ^7.0.0 + dependencies: + '@pixi/canvas-renderer': 7.2.4(@pixi/core@7.2.4) + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/math@7.2.4: + resolution: {integrity: sha512-LJB+mozyEPllxa0EssFZrKNfVwysfaBun4b2dJKQQInp0DafgbA0j7A+WVg0oe51KhFULTJMpDqbLn/ITFc41A==} + dev: false + + /@pixi/mesh-extras@7.2.4(@pixi/core@7.2.4)(@pixi/mesh@7.2.4): + resolution: {integrity: sha512-Lxqq/1E2EmDgjZX8KzjhBy3VvITIQ00arr2ikyHYF1d0XtQTKEYpr8VKzhchqZ5/9DuyTDbDMYGhcxoNXQmZrQ==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/mesh': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/mesh': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/mesh@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-wiALIqcRKib2BqeH9kOA5fOKWN352nqAspgbDa8gA7OyWzmNwqIedIlElixd0oLFOrIN5jOZAdzeKnoYQlt9Aw==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/mixin-cache-as-bitmap@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-95L/9nzfLHw6GoeqqRl/RjSloKvRt0xrc2inCmjMZvMsFUEtHN2F8IWd1k5vcv0S+83NCreFkJg6nJm1m5AZqg==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/mixin-get-child-by-name@7.2.4(@pixi/display@7.2.4): + resolution: {integrity: sha512-9g17KgSBEEhkinnKk4dqmxagzHOCPSTvGB6lOopBq4yyXmr/2WVv+QGjuzE0O+p80szQeBJjPBQxzrfBILaSRw==} + peerDependencies: + '@pixi/display': 7.2.4 + dependencies: + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/mixin-get-global-position@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-UrAUF2BXCeWtFgR2m+er41Ky7zShT7r228cZkB6ZfYwMeThhwqG5mH68UeCyP6p68JMpT1gjI2DPfeSRY3ecnA==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/particle-container@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-tpSzilZGFtAoi8XhzL0TecLPNRQAbY8nWV9XNGXJDw+nxXp18GCe8L6eEmnHLlAug67BRHl65DtrdvTknPX+4g==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/prepare@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/graphics@7.2.4)(@pixi/text@7.2.4): + resolution: {integrity: sha512-Yff5Sh4kTLdKc5VkkM44LW9gpj7Izw8ns3P1TzWxqeGjzPZ3folr/tQujGL+Qw+8A9VESp+hX9MSIHyw+jpyrg==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/graphics': 7.2.4 + '@pixi/text': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/graphics': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/text': 7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4) + dev: false + + /@pixi/runner@7.2.4: + resolution: {integrity: sha512-YtyqPk1LA+0guEFKSFx6t/YSvbEQwajFwi4Ft8iDhioa6VK2MmTir1GjWwy7JQYLcDmYSAcQjnmFtVTZohyYSw==} + dev: false + + /@pixi/settings@7.2.4: + resolution: {integrity: sha512-ZPKRar9EwibijGmH8EViu4Greq1I/O7V/xQx2rNqN23XA7g09Qo6yfaeQpufu5xl8+/lZrjuHtQSnuY7OgG1CA==} + dependencies: + '@pixi/constants': 7.2.4 + '@types/css-font-loading-module': 0.0.7 + ismobilejs: 1.1.1 + dev: false + + /@pixi/sprite-animated@7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-9eRriPSC0QVS7U9zQlrG3uEI5+h3fi+mqofXy+yjk1sGCmXSIJME5p2wg2mzxoJk3qkSMagQA9QHtL26Fti8Iw==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/sprite-tiling@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-nGfxQoACRx49dUN0oW1vFm3141M+7gkAbzoNJym2Pljd2dpLME9fb5E6Lyahu0yWMaPRhhGorn6z9VIGmTF3Jw==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/sprite@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4): + resolution: {integrity: sha512-DhR1B+/d0eXpxHIesJMXcVPrKFwQ+zRA1LvEIFfzewqfaRN3X6PMIuoKX8SIb6tl+Hq8Ba9Pe28zI7d2rmRzrA==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + dev: false + + /@pixi/spritesheet@7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4): + resolution: {integrity: sha512-LNmlavyiMQeCF0U4S+yhzxUYmPmat6EpLjLnkGukQTZV5CZkxDCVgXM9uKoRF2DvNydj4yuwZ6+JjK8QssHI8Q==} + peerDependencies: + '@pixi/assets': 7.2.4 + '@pixi/core': 7.2.4 + dependencies: + '@pixi/assets': 7.2.4(@pixi/core@7.2.4)(@pixi/utils@7.2.4) + '@pixi/core': 7.2.4 + dev: false + + /@pixi/text-bitmap@7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/mesh@7.2.4)(@pixi/text@7.2.4): + resolution: {integrity: sha512-3u2CP4VN+muCaq/jtj7gn0hb3DET/X2S04zTBcgc2WVGufJc62yz+UDzS9jC+ellotVdt9c8U74++vpz3zJGfw==} + peerDependencies: + '@pixi/assets': 7.2.4 + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/mesh': 7.2.4 + '@pixi/text': 7.2.4 + dependencies: + '@pixi/assets': 7.2.4(@pixi/core@7.2.4)(@pixi/utils@7.2.4) + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/mesh': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/text': 7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4) + dev: false + + /@pixi/text-html@7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4)(@pixi/text@7.2.4): + resolution: {integrity: sha512-0NfLAE/w51ZtatxVqLvDS62iO0VLKsSdctqTAVv4Zlgdk9TKJmX1WUucHJboTvbm2SbDjNDGfZ6qXM5nAslIDQ==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4 + '@pixi/sprite': 7.2.4 + '@pixi/text': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/text': 7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4) + dev: false + + /@pixi/text@7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4): + resolution: {integrity: sha512-DGu7ktpe+zHhqR2sG9NsJt4mgvSObv5EqXTtUxD4Z0li1gmqF7uktpLyn5I6vSg1TTEL4TECClRDClVDGiykWw==} + peerDependencies: + '@pixi/core': 7.2.4 + '@pixi/sprite': 7.2.4 + dependencies: + '@pixi/core': 7.2.4 + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + dev: false + + /@pixi/ticker@7.2.4: + resolution: {integrity: sha512-hQQHIHvGeFsP4GNezZqjzuhUgNQEVgCH9+qU05UX1Mc5UHC9l6OJnY4VTVhhcHxZjA6RnyaY+1zBxCnoXuazpg==} + dependencies: + '@pixi/extensions': 7.2.4 + '@pixi/settings': 7.2.4 + '@pixi/utils': 7.2.4 + dev: false + + /@pixi/utils@7.2.4: + resolution: {integrity: sha512-VUGQHBOINIS4ePzoqafwxaGPVRTa3oM/mEutIIHbNGI3b+QvSO+1Dnk40M0zcH6Bo+MxQZbOZK5X/wO9oU5+LQ==} + dependencies: + '@pixi/color': 7.2.4 + '@pixi/constants': 7.2.4 + '@pixi/settings': 7.2.4 + '@types/earcut': 2.1.1 + earcut: 2.2.4 + eventemitter3: 4.0.7 + url: 0.11.1 + dev: false + + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + fast-glob: 3.3.1 + is-glob: 4.0.3 + open: 9.1.0 + picocolors: 1.0.0 + tslib: 2.6.0 + dev: true + + /@swc/core-darwin-arm64@1.3.70: + resolution: {integrity: sha512-31+mcl0dgdRHvZRjhLOK9V6B+qJ7nxDZYINr9pBlqGWxknz37Vld5KK19Kpr79r0dXUZvaaelLjCnJk9dA2PcQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.3.70: + resolution: {integrity: sha512-GMFJ65E18zQC80t0os+TZvI+8lbRuitncWVge/RXmXbVLPRcdykP4EJ87cqzcG5Ah0z18/E0T+ixD6jHRisrYQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.3.70: + resolution: {integrity: sha512-wjhCwS8LCiAq2VedF1b4Bryyw68xZnfMED4pLRazAl8BaUlDFANfRBORNunxlfHQj4V3x39IaiLgCZRHMdzXBg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.3.70: + resolution: {integrity: sha512-9D/Rx67cAOnMiexvCqARxvhj7coRajTp5HlJHuf+rfwMqI2hLhpO9/pBMQxBUAWxODO/ksQ/OF+GJRjmtWw/2A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.3.70: + resolution: {integrity: sha512-gkjxBio7XD+1GlQVVyPP/qeFkLu83VhRHXaUrkNYpr5UZG9zZurBERT9nkS6Y+ouYh+Q9xmw57aIyd2KvD2zqQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.3.70: + resolution: {integrity: sha512-/nCly+V4xfMVwfEUoLLAukxUSot/RcSzsf6GdsGTjFcrp5sZIntAjokYRytm3VT1c2TK321AfBorsi9R5w8Y7Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.3.70: + resolution: {integrity: sha512-HoOsPJbt361KGKaivAK0qIiYARkhzlxeAfvF5NlnKxkIMOZpQ46Lwj3tR0VWohKbrhS+cYKFlVuDi5XnDkx0XA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.3.70: + resolution: {integrity: sha512-hm4IBK/IaRil+aj1cWU6f0GyAdHpw/Jr5nyFYLM2c/tt7w2t5hgb8NjzM2iM84lOClrig1fG6edj2vCF1dFzNQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.3.70: + resolution: {integrity: sha512-5cgKUKIT/9Fp5fCA+zIjYCQ4dSvjFYOeWGZR3QiTXGkC4bGa1Ji9SEPyeIAX0iruUnKjYaZB9RvHK2tNn7RLrQ==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.3.70: + resolution: {integrity: sha512-LE8lW46+TQBzVkn2mHBlk8DIElPIZ2dO5P8AbJiARNBAnlqQWu67l9gWM89UiZ2l33J2cI37pHzON3tKnT8f9g==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.3.70: + resolution: {integrity: sha512-LWVWlEDLlOD25PvA2NEz41UzdwXnlDyBiZbe69s3zM0DfCPwZXLUm79uSqH9ItsOjTrXSL5/1+XUL6C/BZwChA==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.70 + '@swc/core-darwin-x64': 1.3.70 + '@swc/core-linux-arm-gnueabihf': 1.3.70 + '@swc/core-linux-arm64-gnu': 1.3.70 + '@swc/core-linux-arm64-musl': 1.3.70 + '@swc/core-linux-x64-gnu': 1.3.70 + '@swc/core-linux-x64-musl': 1.3.70 + '@swc/core-win32-arm64-msvc': 1.3.70 + '@swc/core-win32-ia32-msvc': 1.3.70 + '@swc/core-win32-x64-msvc': 1.3.70 + dev: true + + /@swc/helpers@0.5.1: + resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} + dependencies: + tslib: 2.6.0 + dev: true + + /@trysound/sax@0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/css-font-loading-module@0.0.7: + resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==} + dev: false + + /@types/earcut@2.1.1: + resolution: {integrity: sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==} + dev: false + + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node@20.4.3: + resolution: {integrity: sha512-Yu3+r4Mn/iY6Mf0aihncZQ1qOjOUrCiodbHHY1hds5O+7BbKp9t+Li7zLO13zO8j9L2C6euz8xsYQP0rjGvVXw==} + dev: true + + /@types/normalize-package-data@2.4.1: + resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + dev: true + + /@types/offscreencanvas@2019.7.0: + resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==} + dev: false + + /@types/semver@7.5.0: + resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + dev: true + + /@types/strip-bom@3.0.0: + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + dev: true + + /@types/strip-json-comments@0.0.30: + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + dev: true + + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.45.0)(typescript@5.1.6): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.6.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.45.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.62.0(eslint@8.45.0)(typescript@5.1.6): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.45.0 + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + dev: true + + /@typescript-eslint/type-utils@5.62.0(eslint@8.45.0)(typescript@5.1.6): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + '@typescript-eslint/utils': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.45.0 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.1.6): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.62.0(eslint@8.45.0)(typescript@5.1.6): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) + eslint: 8.45.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@walidoux/eslint-config@1.0.3(eslint@8.45.0)(prettier@2.8.8)(typescript@5.1.6): + resolution: {integrity: sha512-iaUwqAYDl0RPOqWUow/E8tBB3mS1hD+QGkn3E7TyPwvfm71RS8KZsJk3ZTYiw7aVQObhUxXPxpB3QSgphc7T+A==} + peerDependencies: + eslint: '>=8.34.0' + prettier: '>=2.8.4' + dependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + eslint: 8.45.0 + eslint-config-conventions: 7.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-promise@6.1.1)(eslint-plugin-unicorn@45.0.2)(eslint@8.45.0) + eslint-config-prettier: 8.8.0(eslint@8.45.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.45.0)(prettier@2.8.8) + eslint-plugin-promise: 6.1.1(eslint@8.45.0) + eslint-plugin-unicorn: 45.0.2(eslint@8.45.0) + prettier: 2.8.8 + transitivePeerDependencies: + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + - typescript + dev: true + + /@walidoux/prettier-config@1.0.3(prettier@2.8.8): + resolution: {integrity: sha512-sJpPplmG3xdoEEPXseRheNtoq4yRTmwLlN0dIjS+kcT+h9m8wmOTocdV+oAdrJrpHk8wwGrG2Q7PYcM6d4KmzA==} + peerDependencies: + prettier: '>=2.8.7' + dependencies: + prettier: 2.8.8 + dev: true + + /abortcontroller-polyfill@1.7.5: + resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base-x@3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + dependencies: + big-integer: 1.6.51 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.21.9: + resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001517 + electron-to-chromium: 1.4.468 + node-releases: 2.0.13 + update-browserslist-db: 1.0.11(browserslist@4.21.9) + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + dependencies: + run-applescript: 5.0.0 + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /caniuse-lite@1.0.30001517: + resolution: {integrity: sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /chrome-trace-event@1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + dev: true + + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + + /clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + dev: false + + /commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cosmiconfig@8.2.0: + resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + dev: true + + /css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: true + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + + /csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + dependencies: + css-tree: 1.1.3 + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + dev: true + + /default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.1.1 + titleize: 3.0.0 + dev: true + + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: true + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: true + + /domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: true + + /dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: true + + /dotenv@7.0.0: + resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} + engines: {node: '>=6'} + dev: true + + /dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + dependencies: + xtend: 4.0.2 + dev: true + + /earcut@2.2.4: + resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} + dev: false + + /electron-to-chromium@1.4.468: + resolution: {integrity: sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag==} + dev: true + + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.11 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-conventions@7.0.0(eslint-plugin-import@2.27.5)(eslint-plugin-promise@6.1.1)(eslint-plugin-unicorn@45.0.2)(eslint@8.45.0): + resolution: {integrity: sha512-tkFeBkqj5z+LR24h+6I68x67CkJwoThK4zBbce9cSsuRvd8QAxrrVY2m7R2KM17e6TLLW6VfNt1P5YW7MMfV+A==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + eslint: ^8.33.0 + eslint-plugin-import: ^2.27.5 + eslint-plugin-promise: ^6.1.1 + eslint-plugin-unicorn: ^45.0.2 + dependencies: + eslint: 8.45.0 + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-promise: 6.1.1(eslint@8.45.0) + eslint-plugin-unicorn: 45.0.2(eslint@8.45.0) + dev: true + + /eslint-config-prettier@8.8.0(eslint@8.45.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.45.0 + dev: true + + /eslint-import-resolver-node@0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + dependencies: + debug: 3.2.7 + is-core-module: 2.12.1 + resolve: 1.22.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0): + resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.4 + enhanced-resolve: 5.15.0 + eslint: 8.45.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + get-tsconfig: 4.6.2 + globby: 13.2.2 + is-core-module: 2.12.1 + is-glob: 4.0.3 + synckit: 0.8.5 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + debug: 3.2.7 + eslint: 8.45.0 + eslint-import-resolver-node: 0.3.7 + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.45.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + has: 1.0.3 + is-core-module: 2.12.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.2 + semver: 6.3.1 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.45.0)(prettier@2.8.8): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.45.0 + eslint-config-prettier: 8.8.0(eslint@8.45.0) + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-plugin-promise@6.1.1(eslint@8.45.0): + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.45.0 + dev: true + + /eslint-plugin-unicorn@45.0.2(eslint@8.45.0): + resolution: {integrity: sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw==} + engines: {node: '>=14.18'} + peerDependencies: + eslint: '>=8.28.0' + dependencies: + '@babel/helper-validator-identifier': 7.22.5 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) + ci-info: 3.8.0 + clean-regexp: 1.0.0 + eslint: 8.45.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.27 + regjsparser: 0.9.1 + safe-regex: 2.1.1 + semver: 7.5.4 + strip-indent: 3.0.0 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.1: + resolution: {integrity: sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.1: + resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.45.0: + resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) + '@eslint-community/regexpp': 4.6.0 + '@eslint/eslintrc': 2.1.0 + '@eslint/js': 8.44.0 + '@humanwhocodes/config-array': 0.11.10 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.1 + eslint-visitor-keys: 3.4.1 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.1 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + + /get-port@4.2.0: + resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} + engines: {node: '>=6'} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + + /get-tsconfig@4.6.2: + resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 4.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /gsap@3.12.2: + resolution: {integrity: sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ==} + dev: false + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /htmlnano@2.0.4(svgo@2.8.0): + resolution: {integrity: sha512-WGCkyGFwjKW1GeCBsPYacMvaMnZtFJ0zIRnC2NCddkA+IOEhTqskXrS7lep+3yYZw/nQ3dW1UAX4yA/GJyR8BA==} + peerDependencies: + cssnano: ^6.0.0 + postcss: ^8.3.11 + purgecss: ^5.0.0 + relateurl: ^0.2.7 + srcset: 4.0.0 + svgo: ^3.0.2 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true + dependencies: + cosmiconfig: 8.2.0 + posthtml: 0.16.6 + svgo: 2.8.0 + timsort: 0.3.0 + dev: true + + /htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.12.1: + resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: true + + /is-json@2.0.1: + resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.11 + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /ismobilejs@1.1.1: + resolution: {integrity: sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==} + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lightningcss-darwin-arm64@1.21.5: + resolution: {integrity: sha512-z05hyLX85WY0UfhkFUOrWEFqD69lpVAmgl3aDzMKlIZJGygbhbegqb4PV8qfUrKKNBauut/qVNPKZglhTaDDxA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-darwin-x64@1.21.5: + resolution: {integrity: sha512-MSJhmej/U9MrdPxDk7+FWhO8+UqVoZUHG4VvKT5RQ4RJtqtANTiWiI97LvoVNMtdMnHaKs1Pkji6wHUFxjJsHQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm-gnueabihf@1.21.5: + resolution: {integrity: sha512-xN6+5/JsMrbZHL1lPl+MiNJ3Xza12ueBKPepiyDCFQzlhFRTj7D0LG+cfNTzPBTO8KcYQynLpl1iBB8LGp3Xtw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-gnu@1.21.5: + resolution: {integrity: sha512-KfzFNhC4XTbmG3ma/xcTs/IhCwieW89XALIusKmnV0N618ZDXEB0XjWOYQRCXeK9mfqPdbTBpurEHV/XZtkniQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-arm64-musl@1.21.5: + resolution: {integrity: sha512-bc0GytQO5Mn9QM6szaZ+31fQHNdidgpM1sSCwzPItz8hg3wOvKl8039rU0veMJV3ZgC9z0ypNRceLrSHeRHmXw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-gnu@1.21.5: + resolution: {integrity: sha512-JwMbgypPQgc2kW2av3OwzZ8cbrEuIiDiXPJdXRE6aVxu67yHauJawQLqJKTGUhiAhy6iLDG8Wg0a3/ziL+m+Kw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-linux-x64-musl@1.21.5: + resolution: {integrity: sha512-Ib8b6IQ/OR/VrPU6YBgy4T3QnuHY7DUa95O+nz+cwrTkMSN6fuHcTcIaz4t8TJ6HI5pl3uxUOZjmtls2pyQWow==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /lightningcss-win32-x64-msvc@1.21.5: + resolution: {integrity: sha512-A8cSi8lUpBeVmoF+DqqW7cd0FemDbCuKr490IXdjyeI+KL8adpSKUs8tcqO0OXPh1EoDqK7JNkD/dELmd4Iz5g==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /lightningcss@1.21.5: + resolution: {integrity: sha512-/pEUPeih2EwIx9n4T82aOG6CInN83tl/mWlw6B5gWLf36UplQi1L+5p3FUHsdt4fXVfOkkh9KIaM3owoq7ss8A==} + engines: {node: '>= 12.0.0'} + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.21.5 + lightningcss-darwin-x64: 1.21.5 + lightningcss-linux-arm-gnueabihf: 1.21.5 + lightningcss-linux-arm64-gnu: 1.21.5 + lightningcss-linux-arm64-musl: 1.21.5 + lightningcss-linux-x64-gnu: 1.21.5 + lightningcss-linux-x64-musl: 1.21.5 + lightningcss-win32-x64-msvc: 1.21.5 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /lmdb@2.7.11: + resolution: {integrity: sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw==} + hasBin: true + requiresBuild: true + dependencies: + msgpackr: 1.8.5 + node-addon-api: 4.3.0 + node-gyp-build-optional-packages: 5.0.6 + ordered-binary: 1.4.1 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 2.7.11 + '@lmdb/lmdb-darwin-x64': 2.7.11 + '@lmdb/lmdb-linux-arm': 2.7.11 + '@lmdb/lmdb-linux-arm64': 2.7.11 + '@lmdb/lmdb-linux-x64': 2.7.11 + '@lmdb/lmdb-win32-x64': 2.7.11 + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /msgpackr-extract@3.0.2: + resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.0.7 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 + dev: true + optional: true + + /msgpackr@1.8.5: + resolution: {integrity: sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg==} + optionalDependencies: + msgpackr-extract: 3.0.2 + dev: true + + /msgpackr@1.9.5: + resolution: {integrity: sha512-/IJ3cFSN6Ci3eG2wLhbFEL6GT63yEaoN/R5My2QkV6zro+OJaVRLPlwvxY7EtHYSmDlQpk8stvOQTL2qJFkDRg==} + optionalDependencies: + msgpackr-extract: 3.0.2 + dev: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-addon-api@4.3.0: + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + dev: true + + /node-addon-api@7.0.0: + resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==} + dev: true + + /node-gyp-build-optional-packages@5.0.6: + resolution: {integrity: sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw==} + hasBin: true + dev: true + + /node-gyp-build-optional-packages@5.0.7: + resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} + hasBin: true + dev: true + optional: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.2 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: true + + /nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /ordered-binary@1.4.1: + resolution: {integrity: sha512-9LtiGlPy982CsgxZvJGNNp2/NnrgEr6EAyN3iIEP3/8vd3YLgAZQHbQ75ZrkfBRGrNg37Dk3U6tuVb+B4Xfslg==} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parcel@2.9.3: + resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==} + engines: {node: '>= 12.0.0'} + hasBin: true + peerDependenciesMeta: + '@parcel/core': + optional: true + dependencies: + '@parcel/config-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/logger': 2.9.3 + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-cli': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-dev-server': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-tracer': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + chalk: 4.1.2 + commander: 7.2.0 + get-port: 4.2.0 + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - uncss + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pixi.js@7.2.4(@pixi/utils@7.2.4): + resolution: {integrity: sha512-nBH60meoLnHxoMFz17HoMxXS4uJpG5jwIdL+Gx2S11TzWgP3iKF+/WLOTrkSdyuQoQSdIBxVqpnYii0Wiox15A==} + dependencies: + '@pixi/accessibility': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/events@7.2.4) + '@pixi/app': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/assets': 7.2.4(@pixi/core@7.2.4)(@pixi/utils@7.2.4) + '@pixi/compressed-textures': 7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4) + '@pixi/core': 7.2.4 + '@pixi/display': 7.2.4(@pixi/core@7.2.4) + '@pixi/events': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/extensions': 7.2.4 + '@pixi/extract': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-alpha': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-blur': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-color-matrix': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-displacement': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-fxaa': 7.2.4(@pixi/core@7.2.4) + '@pixi/filter-noise': 7.2.4(@pixi/core@7.2.4) + '@pixi/graphics': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/mesh': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/mesh-extras': 7.2.4(@pixi/core@7.2.4)(@pixi/mesh@7.2.4) + '@pixi/mixin-cache-as-bitmap': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/mixin-get-child-by-name': 7.2.4(@pixi/display@7.2.4) + '@pixi/mixin-get-global-position': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/particle-container': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/prepare': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/graphics@7.2.4)(@pixi/text@7.2.4) + '@pixi/sprite': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4) + '@pixi/sprite-animated': 7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/sprite-tiling': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/spritesheet': 7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4) + '@pixi/text': 7.2.4(@pixi/core@7.2.4)(@pixi/sprite@7.2.4) + '@pixi/text-bitmap': 7.2.4(@pixi/assets@7.2.4)(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/mesh@7.2.4)(@pixi/text@7.2.4) + '@pixi/text-html': 7.2.4(@pixi/core@7.2.4)(@pixi/display@7.2.4)(@pixi/sprite@7.2.4)(@pixi/text@7.2.4) + transitivePeerDependencies: + - '@pixi/utils' + dev: false + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /posthtml-parser@0.10.2: + resolution: {integrity: sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==} + engines: {node: '>=12'} + dependencies: + htmlparser2: 7.2.0 + dev: true + + /posthtml-parser@0.11.0: + resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} + engines: {node: '>=12'} + dependencies: + htmlparser2: 7.2.0 + dev: true + + /posthtml-render@3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + dependencies: + is-json: 2.0.1 + dev: true + + /posthtml@0.16.6: + resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} + engines: {node: '>=12.0.0'} + dependencies: + posthtml-parser: 0.11.0 + posthtml-render: 3.0.0 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: true + + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-error-overlay@6.0.9: + resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + dev: true + + /react-refresh@0.9.0: + resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} + engines: {node: '>=0.10.0'} + dev: true + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.1 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: true + + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: true + + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regjsparser@0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true + dependencies: + is-core-module: 2.12.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-regex: 1.1.4 + dev: true + + /safe-regex@2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + dependencies: + regexp-tree: 0.1.27 + dev: true + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.13 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.13 + dev: true + + /spdx-license-ids@3.0.13: + resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + dev: true + + /srcset@4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + dev: true + + /stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + dev: true + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.0 + stable: 0.1.8 + dev: true + + /synckit@0.8.5: + resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.4.2 + tslib: 2.6.0 + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + dependencies: + setimmediate: 1.0.5 + dev: true + + /timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + dev: true + + /titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /ts-node-dev@2.0.0(@types/node@20.4.3)(typescript@5.1.6): + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + chokidar: 3.5.3 + dynamic-dedupe: 0.3.0 + minimist: 1.2.8 + mkdirp: 1.0.4 + resolve: 1.22.2 + rimraf: 2.7.1 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + ts-node: 10.9.1(@types/node@20.4.3)(typescript@5.1.6) + tsconfig: 7.0.0 + typescript: 5.1.6 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + dev: true + + /ts-node@10.9.1(@types/node@20.4.3)(typescript@5.1.6): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.4.3 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + dependencies: + '@types/strip-bom': 3.0.0 + '@types/strip-json-comments': 0.0.30 + strip-bom: 3.0.0 + strip-json-comments: 2.0.1 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.0: + resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + dev: true + + /tsutils@3.21.0(typescript@5.1.6): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.1.6 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + + /update-browserslist-db@1.0.11(browserslist@4.21.9): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.9 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /url@0.11.1: + resolution: {integrity: sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==} + dependencies: + punycode: 1.4.1 + qs: 6.11.2 + dev: false + + /utility-types@3.10.0: + resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} + engines: {node: '>= 4'} + dev: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /xxhash-wasm@0.4.2: + resolution: {integrity: sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..dface68 --- /dev/null +++ b/public/index.html @@ -0,0 +1,31 @@ + + + + + Scuti Renderer + + + + +
+ + + + diff --git a/public/script.js b/public/script.js new file mode 100644 index 0000000..ffd4750 --- /dev/null +++ b/public/script.js @@ -0,0 +1,61 @@ +import { Scuti } from '../src/Scuti'; +import { Room } from '../src/objects/rooms/Room'; +import { FloorMaterial } from '../src/objects/rooms/materials/FloorMaterial'; +import { WallMaterial } from '../src/objects/rooms/materials/WallMaterial'; +import { Avatar } from '../src/objects/avatars/Avatar'; +import { AvatarAction } from '../src/objects/avatars/actions/AvatarAction'; + +(async () => { + const renderer = new Scuti({ + canvas: document.getElementById('app'), + width: window.innerWidth, + height: window.innerHeight, + resources: './resources' + }); + await renderer.loadResources(); + + const tileMap = + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + '00000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n'; + + const room = new Room(renderer, { + tileMap: tileMap, + /*floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1501)*/ + //floorMaterial: new FloorMaterial(renderer, 307), + floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1601) + }); + avatar(room, 2, 1, 0, 0); + avatar(room, 4, 1, 0, 1); + avatar(room, 6, 1, 0, 2); + avatar(room, 8, 1, 0, 3); + avatar(room, 10, 1, 0, 4); + avatar(room, 12, 1, 0, 5); + avatar(room, 14, 1, 0, 6); + avatar(room, 16, 1, 0, 7); +})(); + +function avatar(room, x, y, z, direction) { + const avatar = new Avatar({ + figure: 'hr-100-61.hd-180-7.ch-210-66.lg-270-82.sh-290-80', + position: { + x: x, + y: y, + z: z + }, + bodyDirection: direction, + headDirection: direction, + actions: [AvatarAction.Talk, AvatarAction.Wave, AvatarAction.Walk, AvatarAction.CarryItem], + handItem: 55 + }); + room.objects.add(avatar); +} diff --git a/public/script2.js b/public/script2.js new file mode 100644 index 0000000..0dc12e2 --- /dev/null +++ b/public/script2.js @@ -0,0 +1,427 @@ +import { Scuti } from '../src/Scuti'; +import { Room } from '../src/objects/rooms/Room'; +import { FloorMaterial } from '../src/objects/rooms/materials/FloorMaterial'; +import { WallMaterial } from '../src/objects/rooms/materials/WallMaterial'; +import { FloorFurniture } from '../src/objects/furnitures/FloorFurniture'; +import { WallFurniture } from '../src/objects/furnitures/WallFurniture'; +import { Avatar } from '../src/objects/avatars/Avatar'; +import { AvatarAction } from '../src/objects/avatars/actions/AvatarAction'; + +(async () => { + const renderer = new Scuti({ + canvas: document.getElementById('app'), + width: window.innerWidth, + height: window.innerHeight, + resources: './resources' + }); + await renderer.loadResources(); + + const tileMap = + 'xxxxxxxxxxxxxxxxxxxx\n' + + 'x222221111111111111x\n' + + 'x222221111111111111x\n' + + '2222221111111111111x\n' + + 'x222221111111111111x\n' + + 'x222221111111111111x\n' + + 'x222221111111111111x\n' + + 'xxxxxxxx1111xxxxxxxx\n' + + 'xxxxxxxx0000xxxxxxxx\n' + + 'x000000x0000x000000x\n' + + 'x000000x0000x000000x\n' + + 'x00000000000x000000x\n' + + 'x00000000000x000000x\n' + + 'x000000000000000000x\n' + + 'x000000000000000000x\n' + + 'xxxxxxxx00000000000x\n' + + 'x000000x00000000000x\n' + + 'x000000x0000xxxxxxxx\n' + + 'x00000000000x000000x\n' + + 'x00000000000x000000x\n' + + 'x00000000000x000000x\n' + + 'x00000000000x000000x\n' + + 'xxxxxxxx0000x000000x\n' + + 'x000000x0000x000000x\n' + + 'x000000x0000x000000x\n' + + 'x000000000000000000x\n' + + 'x000000000000000000x\n' + + 'x000000000000000000x\n' + + 'x000000000000000000x\n' + + 'xxxxxxxxxxxxxxxxxxxx\n'; + + const tileMap1 = + 'xxxxxx\n' + + 'x4444432110011111x\n' + + 'x444443211001xx00x\n' + + '0000000011001xx00x\n' + + 'x0000000000000000x\n' + + 'x0001000000000000x\n' + + 'x0000000000000000x\n' + + 'x000000000xx00000x\n' + + 'x000000000x000000x\n' + + '00000000000001000x\n' + + 'x0000432100011110x\n' + + 'x0000000000001000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000100000000x\n' + + 'x0000001110000000x\n' + + 'x0000011211000000x\n' + + 'x0000012221000000x\n' + + 'x000111232111100x\n' + + 'x0001112321111000x\n' + + 'x0000012321000000x\n' + + 'x0000012221000000x\n' + + 'x0000011211000000x\n' + + 'x00000011100xxxxxx\n' + + 'x00000001000xxxxxx\n' + + 'x00000000000xxxxxx\n' + + 'x00000000000xxxxxx\n'; + + const tileMap2 = + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + '00000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n'; + + const tileMap3 = + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'x222222222222222222222222222x\n' + + 'x222222222222222222222222222x\n' + + '2222222222222222222222222222x\n' + + 'x222222222222222222222222222x\n' + + 'x2222xxxxxx222222xxxxxxx2222x\n' + + 'x2222xxxxxx111111xxxxxxx2222x\n' + + 'x2222xx111111111111111xx2222x\n' + + 'x2222xx111111111111111xx2222x\n' + + 'x2222xx11xxx1111xxxx11xx2222x\n' + + 'x2222xx11xxx0000xxxx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x22222111x00000000xx11xx2222x\n' + + 'x2222xx11xxxxxxxxxxx11xx2222x\n' + + 'x2222xx11xxxxxxxxxxx11xx2222x\n' + + 'x2222xx111111111111111xx2222x\n' + + 'x2222xx111111111111111xx2222x\n' + + 'x2222xxxxxxxxxxxxxxxxxxx2222x\n' + + 'x2222xxxxxxxxxxxxxxxxxxx2222x\n' + + 'x222222222222222222222222222x\n' + + 'x222222222222222222222222222x\n' + + 'x222222222222222222222222222x\n' + + 'x222222222222222222222222222x\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + + const tileMap4 = + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xeeeeeeeeeeeeeeeedcba9888888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xdxxxxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xcxxxxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xbxxxxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xaxxxxxxxxxxxxxxxxxxxxx88888888888\n' + + 'aaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaa98766666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx5xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx4xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx3xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx9xxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx8xxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx7xxx3333333333xxxx\n' + + 'xxx777777777xxxx6xxx3333333333xxxx\n' + + 'xxx777777777xxxx5xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx4xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx3xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx2xxxxxxxxxxxxxxxxx\n' + + 'xfffffffffxxxxxx1xxxxxxxxxxxxxxxxx\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xxxxxxxxxxxxxxxx111111111111111111\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + + const tileMap5 = + 'xxxxxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'x2222xx1111111111xx11111111\n' + + 'x2222xx1111111111xx11111111\n' + + '222222111111111111111111111\n' + + 'x22222111111111111111111111\n' + + 'x22222111111111111111111111\n' + + 'x22222111111111111111111111\n' + + 'x2222xx1111111111xx11111111\n' + + 'x2222xx1111111111xx11111111\n' + + 'x2222xx1111111111xxxx1111xx\n' + + 'x2222xx1111111111xxxx0000xx\n' + + 'xxxxxxx1111111111xx00000000\n' + + 'xxxxxxx1111111111xx00000000\n' + + 'x22222111111111111000000000\n' + + 'x22222111111111111000000000\n' + + 'x22222111111111111000000000\n' + + 'x22222111111111111000000000\n' + + 'x2222xx1111111111xx00000000\n' + + 'x2222xx1111111111xx00000000\n' + + 'x2222xxxx1111xxxxxxxxxxxxxx\n' + + 'x2222xxxx0000xxxxxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx\n' + + 'x2222x0000000000xxxxxxxxxxx'; + + const room = new Room(renderer, { + tileMap: tileMap5, + /*floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1501)*/ + //floorMaterial: new FloorMaterial(renderer, 307), + floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1601) + }); + /*setTimeout(() => { + room.wallMaterial = new WallMaterial(renderer, 1701); + room.floorMaterial = new FloorMaterial(renderer, 301); + room.wallThickness = 8; + room.floorThickness = 8; + room.wallHeight = 6; + }, 5000);*/ + const furniture = new FloorFurniture({ + id: 1619, + position: { + x: 7, + y: 5, + z: 0 + }, + direction: 4, + state: 1 + }); + const furniture2 = new FloorFurniture({ + id: 3895, + position: { + x: 7, + y: 5, + z: 0 + }, + direction: 4, + state: 1 + }); + const wallFurniture = new WallFurniture({ + position: { + x: 1, + y: 0, + offsetX: 8, + offsetY: 36 + }, + state: 0, + id: 4625, + direction: 2 + }); + const wallFurniture2 = new WallFurniture({ + position: { + x: 1, + y: 3, + offsetX: 8, + offsetY: 36 + }, + state: 0, + id: 4625, + direction: 2 + }); + const wallFurniture3 = new WallFurniture({ + position: { + x: 4, + y: 0, + offsetX: 14, + offsetY: 41 + }, + id: 4066, + direction: 4, + state: 3 + }); + const avatar = new Avatar({ + figure: 'hr-100-61.hd-180-7.ch-210-66.lg-270-82.sh-290-80', + position: { + x: 4, + y: 4, + z: 0 + }, + bodyDirection: 2, + headDirection: 2, + actions: [ + //AvatarAction.Idle, + //AvatarAction.Walk, + AvatarAction.Talk, + AvatarAction.Wave, + AvatarAction.Walk, + AvatarAction.CarryItem + ], + handItem: 55 + }); + + let hd = [180, 185, 190, 195, 200, 205]; + let hr = [100, 105, 110, 115, 125, 135, 145, 155, 165, 170]; + let ch = [210, 215, 220, 225, 230, 235, 240, 245, 250, 255]; + let sh = [290, 295, 300, 305, 725, 730, 735, 740, 905, 906, 907, 908]; + let ha = [ + 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, + 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027 + ]; + let lg = [270, 275, 280, 285, 281, 695, 696, 716, 700, 705, 710, 715, 720, 827]; + let wa = [2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012]; + let ea = [1401, 1402, 1403, 1404, 1405, 1406]; + let color = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 + ]; + let color2 = [ + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61 + ]; + let color3 = [ + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110 + ]; + let actions = [ + AvatarAction.Default, + AvatarAction.Walk, + AvatarAction.GestureSmile, + AvatarAction.Wave, + AvatarAction.GestureAngry, + AvatarAction.GestureSurprised, + AvatarAction.Respect, + AvatarAction.CarryItem, + AvatarAction.UseItem + ]; + + for (let y = 0; y < 2; y++) { + for (let x = 0; x < 2; x++) { + let figure = + 'hr-' + + hr[Math.floor(Math.random() * hr.length)] + + '-' + + color2[Math.floor(Math.random() * color2.length)] + + '.hd-' + + hd[Math.floor(Math.random() * hd.length)] + + '-' + + color[Math.floor(Math.random() * color.length)] + + '.ch-' + + ch[Math.floor(Math.random() * ch.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)] + + '.lg-' + + lg[Math.floor(Math.random() * lg.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)] + + '.sh-' + + sh[Math.floor(Math.random() * sh.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)] + + '.ha-' + + ha[Math.floor(Math.random() * ha.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)] + + '.wa-' + + wa[Math.floor(Math.random() * wa.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)] + + '.ea-' + + ea[Math.floor(Math.random() * ea.length)] + + '-' + + color3[Math.floor(Math.random() * color3.length)]; + let randomAvatar = new Avatar({ + position: { + x: x + 1, + y: y, + z: 0 + }, + bodyDirection: 2, + headDirection: 2, + figure: figure, + actions: [actions[Math.floor(Math.random() * actions.length)]] + }); + //room.objects.add(randomAvatar); + } + } + + room.objects.add(avatar); + avatar.onPointerDown = (event) => { + console.log('down', event); + }; + avatar.onPointerUp = (event) => { + console.log('up', event); + }; + avatar.onPointerMove = (event) => { + console.log('move', event); + }; + avatar.onPointerOut = (event) => { + console.log('out', event); + }; + avatar.onPointerOver = (event) => { + console.log('over', event); + }; + avatar.onDoubleClick = (event) => { + console.log('doubleclick', event); + }; + //room.objects.add(furniture2); + room.visualization.onTileClick = (position) => { + console.log('click', position); + /*if(furniture.direction === 4) { + furniture.direction = 2 + } else { + furniture.direction = 4; + }*/ + avatar.roomPosition = { + x: position.x, + y: position.y, + z: position.z + }; + wallFurniture.pos = { + x: 1, + y: Math.floor(Math.random() * (10 - 1 + 1) + 1), + offsetX: 8, + offsetY: 36 + }; + }; + room.visualization.onTileOver = (position) => { + console.log('over', position); + }; + room.visualization.onTileOut = (position) => { + console.log('out', position); + }; + room.objects.add(furniture); + furniture.onDoubleClick = (event) => { + console.log('dblclick furni', event); + }; + //room.addRoomObject(wallFurniture); + //room.addRoomObject(wallFurniture2); + //room.addRoomObject(wallFurniture3); +})(); diff --git a/public/script3.js b/public/script3.js new file mode 100644 index 0000000..35af7c0 --- /dev/null +++ b/public/script3.js @@ -0,0 +1,443 @@ +import { Scuti } from '../src/Scuti'; +import { Room } from '../src/objects/rooms/Room'; +import { FloorMaterial } from '../src/objects/rooms/materials/FloorMaterial'; +import { WallMaterial } from '../src/objects/rooms/materials/WallMaterial'; +import { FloorFurniture } from '../src/objects/furnitures/FloorFurniture'; +import { WallFurniture } from '../src/objects/furnitures/WallFurniture'; +import { Avatar } from '../src/objects/avatars/Avatar'; +import { AvatarAction } from '../src/objects/avatars/actions/AvatarAction'; + +(async () => { + const renderer = new Scuti({ + canvas: document.getElementById('app'), + width: window.innerWidth, + height: window.innerHeight, + resources: 'http://localhost:8081/' + }); + await renderer.loadResources('http://localhost:8081/'); + + const tileMap = + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xeeeeeeeeeeeeeeeedcba9888888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xeeeeeeeeeeeeeeeexxxxxx88888888888\n' + + 'xdxxxxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xcaaaxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xbaaaxxxxxxxxxxxxxxxxxx88888888888\n' + + 'xaaaaxxxxxxxxxxxxxxxxxx88888888888\n' + + 'aaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxxxxxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxx6666666666666\n' + + 'xaaaaaaaaaaaaaaaa98766666666666666\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx5xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx4xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxxxxxxxxxxx3xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xaaaaaaaaaaaaaaaaxxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx9xxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx8xxx3333333333xxxx\n' + + 'xxxxxxxxxxxxxxxx7xxx3333333333xxxx\n' + + 'xxx777777777xxxx6xxx3333333333xxxx\n' + + 'xxx777777777xxxx5xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx4xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx3xxxxxxxxxxxxxxxxx\n' + + 'xxx777777777xxxx2xxxxxxxxxxxxxxxxx\n' + + 'xfffffffffxxxxxx1xxxxxxxxxxxxxxxxx\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xfffffffffxxxxxx111111111111111111\n' + + 'xxxxxxxxxxxxxxxx111111111111111111\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + + const tileMapUpdated = + 'x6543210000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + '00000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n' + + 'x0000000000000000x\n'; + + const room = new Room(renderer, { + tileMap: + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxxxxxxxxxxxxxxxxxxxxx\n' + + 'xxxxx00xxxxxxxxxxxxxxxxx\n' + + 'xxxxx00xxxxxxxxxxxxxxxxx\n' + + 'xxxxx00xxxxx00xxxxxxxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxx000000000000x\n' + + 'xxxxxxxxxxx000000000000x\n' + + 'xxxxxxxxxxx000000000000x\n' + + 'xxxxxxxxxx0000000000000x\n' + + 'xxxxxxxxxxx0000000000000\n' + + 'xxxxxxxxxxx0000000000000\n' + + 'x66666xxxxx000000000000x\n' + + 'x66666xxxxx000000000000x\n' + + 'x6666xxxxxx000000000000x\n' + + 'x6666xxxxxx000000000000x\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'x6666xxxxxx00000000xxxxx\n' + + 'xxxxxxxxxxxx00xxxxxxxxxx', + /*floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1501)*/ + //floorMaterial: new FloorMaterial(renderer, 307), + floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 1601) + }); + setTimeout(() => { + //room.tileMap = tileMapUpdated; + //room.camera._centerCamera(); + }, 2000); + const avatar = new Avatar({ + //figure: "hr-100-61.hd-180-7.ch-210-66.lg-270-82.sh-290-80", + // police figure: "hr-892-46.hd-209-8.ch-225-81.lg-270-64.sh-300-64.ca-1804-64.wa-2012", + figure: 'hd-209-14.ch-3688-1408.lg-280-1408.sh-290-1408.ha-1008.ea-3578.ca-1806-82.cc-3360-1408', + //figure: "hd-180-1.ch-255-66.lg-280-110.sh-305-62.ha-1012-110.hr-828-61", + position: { + x: 4, + y: 4, + z: 0 + }, + bodyDirection: 6, + headDirection: 6, + actions: [ + //AvatarAction.Idle, + //AvatarAction.Walk, + AvatarAction.Talk, + AvatarAction.Wave, + //AvatarAction.Walk, + AvatarAction.CarryItem, + AvatarAction.Sit + ], + handItem: 55 + }); + setTimeout(() => { + avatar.addAction(AvatarAction.Walk); + }, 5000); + setTimeout(() => { + avatar.removeAction(AvatarAction.Walk); + }, 7000); + setTimeout(() => { + avatar.addAction(AvatarAction.Walk); + }, 9000); + room.objects.add(avatar); + room.tiles.onPointerDown = (position) => { + console.log('click', position); + avatar.roomPosition = position.position; + }; + room.tiles.onDoubleClick = (position) => { + console.log('dblclick', position); + }; + room.tiles.onPointerOver = (event) => {}; + dice(room, 5, 5, 2); + dice(room, 5, 6, 1); + dice(room, 6, 5, 2); + dice(room, 7, 5, 2); + dice(room, 7, 6, 1); + const wallFurniture = new WallFurniture({ + id: 4054, + position: { + x: 0, + y: 0, + offsetX: 0, + offsetY: 0 + }, + direction: 4, + state: 0 + }); + const wallFurniture2 = new WallFurniture({ + position: { + x: 1, + y: 0, + offsetX: 8, + offsetY: 36 + }, + state: 0, + id: 4625, + direction: 2 + }); + const furniture = new FloorFurniture({ + id: 1619, + position: { + x: 8, + y: 5, + z: 0 + }, + direction: 2, + state: 1 + }); + setTimeout(() => {}, 2000); + document.body.addEventListener('keyup', function (event) { + event.preventDefault(); + if (event.keyCode === 13) { + room.camera.centerCamera(furniture); + } + }); + const furniture2 = new FloorFurniture({ + id: 8916, + position: { + x: 8, + y: 10, + z: 0 + }, + direction: 2, + state: 1 + }); + const furniture3 = new FloorFurniture({ + id: 8916, + position: { + x: 10, + y: 10, + z: 0 + }, + direction: 2, + state: 0 + }); + furniture.onPointerDown = () => { + if (furniture.selected) { + furniture.selected = false; + } else { + furniture.selected = true; + } + }; + const guildGate = new FloorFurniture({ + id: 4389, + position: { + x: 11, + y: 5, + z: 0 + }, + direction: 2, + state: 1 + }); + const background1 = new FloorFurniture({ + id: 3996, + position: { + x: 20, + y: 19, + z: 0 + }, + direction: 1, + state: 0 + }); + const background2 = new FloorFurniture({ + id: 3996, + position: { + x: 12, + y: 11, + z: 1 + }, + direction: 1, + state: 0 + }); + const background3 = new FloorFurniture({ + id: 3996, + position: { + x: 12, + y: 11, + z: 0 + }, + direction: 1, + state: 0 + }); + const background4 = new FloorFurniture({ + id: 3996, + position: { + x: 11, + y: 10, + z: 0 + }, + direction: 1, + state: 0 + }); + const background5 = new FloorFurniture({ + id: 3996, + position: { + x: 8, + y: 5, + z: 0 + }, + direction: 1, + state: 1 + }); + setTimeout(() => { + console.log(furniture.position); + /*furniture.move({ + x: 10, + y: 6, + z: 0 + }, 0);*/ + // + furniture.rotate(4); + //furniture.direction = 4; + furniture.state = 0; + guildGate.state = 101; + guildGate.visualization.secondaryColor = 0xffff00; + guildGate.visualization.primaryColor = 0x00ffff; + }, 5000); + const wallFurniture3 = new WallFurniture({ + position: { + x: 4, + y: 0, + offsetX: 14, + offsetY: 41 + }, + id: 4066, + direction: 4, + state: 3 + }); + background1.onLoad = () => { + background1.move( + { + x: 20, + y: 19, + z: 0 + }, + 0 + ); + background1.visualization.offsetX = -720; + background1.visualization.offsetY = 190; + background1.visualization.offsetZ = 8700; + background1.visualization.imageUrl = '/images/room_ads/wl15/wl15_a.png'; + }; + room.objects.add(background1); + background2.onLoad = () => { + background2.move( + { + x: 12, + y: 11, + z: 1 + }, + 0 + ); + background2.visualization.offsetX = -253; + background2.visualization.offsetY = 446; + background2.visualization.offsetZ = 8700; + background2.visualization.imageUrl = '/images/room_ads/wl15/wl15_d.png'; + }; + room.objects.add(background2); + background3.onLoad = () => { + background3.move( + { + x: 12, + y: 11, + z: 0 + }, + 0 + ); + background3.visualization.offsetX = -704; + background3.visualization.offsetY = 155; + background3.visualization.offsetZ = 8700; + background3.visualization.imageUrl = '/images/room_ads/wl15/wl15_c.png'; + }; + room.objects.add(background3); + background4.onLoad = () => { + background4.move( + { + x: 11, + y: 10, + z: 0 + }, + 0 + ); + background4.visualization.offsetX = -253; + background4.visualization.offsetY = 187; + background4.visualization.offsetZ = 9995; + background4.visualization.imageUrl = '/images/room_ads/wl15/wl15_b.png'; + }; + room.objects.add(background4); + //background.visualization.imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/2367px-Vue.js_Logo_2.svg.png"; + room.objects.add(wallFurniture); + setInterval(() => { + wallFurniture3.state += 1; + wallFurniture3.destroy(); + }, 3000); + room.objects.add(wallFurniture3); + room.objects.add(furniture); + room.objects.add(furniture2); + room.objects.add(furniture3); + room.objects.add(guildGate); + + /*const furniture = new FloorFurniture({ + id: 1619, + position: { + x: 7, + y: 5, + z: 0 + }, + direction: 4, + state: 1 + });*/ +})(); + +function dice(room, x, y, z) { + let furni5 = new FloorFurniture({ + position: { + x: x, + y: y, + z: z + }, + //direction: randomRotation[Math.floor(Math.random() * randomRotation.length)], + direction: 0, + //id: furniId[Math.floor(Math.random() * furniId.length)], + id: 284, + state: 1 + }); + room.objects.add(furni5); + let timeout = undefined; + furni5.onDoubleClick = (event) => { + console.log(event); + //if(furni5.infos.logic === "furniture_dice") { + console.log('clicked furni5', event); + if (event.tag === 'activate') { + clearTimeout(timeout); + furni5.state = -1; + timeout = setTimeout(() => { + furni5.state = Math.floor(Math.random() * 6) + 1; + }, 1000); + /*setTimeout(() => { + furni5.state = 0 + }, 2000);*/ + } else { + clearTimeout(timeout); + furni5.state = 0; + } + //x@} + }; +} diff --git a/public/script4.js b/public/script4.js new file mode 100644 index 0000000..425655c --- /dev/null +++ b/public/script4.js @@ -0,0 +1,166 @@ +import { Scuti } from '../src/Scuti'; +import { Room } from '../src/objects/rooms/Room'; +import { FloorMaterial } from '../src/objects/rooms/materials/FloorMaterial'; +import { WallMaterial } from '../src/objects/rooms/materials/WallMaterial'; +import { FloorFurniture } from '../src/objects/furnitures/FloorFurniture'; +import {WiredSelectionFilter} from "../src/objects/filters/WiredSelectionFilter"; +import {WallFurniture} from "../src"; + +(async () => { + const renderer = new Scuti({ + canvas: document.getElementById('app'), + width: window.innerWidth, + height: window.innerHeight, + resources: 'https://kozennnn.github.io/scuti-resources/' + }); + await renderer.loadResources('https://kozennnn.github.io/scuti-resources/'); + + const tileMap = 'x1110001\n' + 'x0000000\n' + '00000000\n' + 'x0000000\n' + 'x0000000\n'; + + const room = new Room(renderer, { + tileMap: tileMap, + floorMaterial: new FloorMaterial(renderer, 110), + wallMaterial: new WallMaterial(renderer, 2301) + }); + const furniture = new FloorFurniture({ + //id: 4950, + //id: 1619, + id: 4967, + position: { + x: 5, + y: 4, + z: 0 + }, + direction: 2, + state: 1 + }); + room.objects.add(furniture); + furniture.onPointerDown = () => { + console.log('clicked'); + }; + const furniture3 = new FloorFurniture({ + id: 8916, + position: { + x: 10, + y: 10, + z: 0 + }, + direction: 2, + state: 1 + }); + const furniture2 = new FloorFurniture({ + id: 8916, + position: { + x: 8, + y: 10, + z: 0 + }, + direction: 2, + state: 1 + }); + const wallFurniture = new WallFurniture({ + id: 4625, + position: { + x: -1, + y: 2, + offsetX: 2, + offsetY: -25 + }, + direction: 2, + state: 2 + }); + const wallFurniture2 = new WallFurniture({ + id: 4032, + position: { + x: 3, + y: -1, + offsetX: 4, + offsetY: -30 + }, + direction: 4, + state: 1 + }); + room.objects.add(furniture3); + room.objects.add(furniture2); + room.objects.add(wallFurniture); + room.objects.add(wallFurniture2); + setTimeout(() => wallFurniture.move({ + x: -1, + y: 3, + offsetX: 2, + offsetY: -25 + }), 3000); + setTimeout(() => wallFurniture.move({ + x: -1, + y: 5, + offsetX: 2, + offsetY: -25 + }), 5000); + //setTimeout(() => room.objects.add(furniture), 6000); + furniture3.onLoadComplete = () => { + console.log('loaded!'); + }; + room.tiles.onPointerDown = (event) => { + furniture.move(event.position); + //room.tileMap = tileMap; + }; + //dice(room, 5, 5, 2); + document.onkeydown = (e) => { + e = e || window.event; + + if (e.keyCode == '38') { + if (room.camera.zoomLevel <= 1) { + room.camera.zoomLevel = room.camera.zoomLevel * 2; + } else { + room.camera.zoomLevel += 1; + } + } else if (e.keyCode == '40') { + if (room.camera.zoomLevel <= 1) { + room.camera.zoomLevel = room.camera.zoomLevel / 2; + } else { + room.camera.zoomLevel -= 1; + } + } else if (e.keyCode == '37') { + furniture.rotate(4); + } else if (e.keyCode == '39') { + const filter = new WiredSelectionFilter(0xffffff, 0x999999); + furniture.addFilter(filter); + } + }; +})(); + +function dice(room, x, y, z) { + let furni5 = new FloorFurniture({ + position: { + x: x, + y: y, + z: z + }, + //direction: randomRotation[Math.floor(Math.random() * randomRotation.length)], + direction: 0, + //id: furniId[Math.floor(Math.random() * furniId.length)], + id: 284, + state: 1 + }); + room.objects.add(furni5); + let timeout = undefined; + furni5.onDoubleClick = (event) => { + console.log(event); + //if(furni5.infos.logic === "furniture_dice") { + console.log('clicked furni5', event); + if (event.tag === 'activate') { + clearTimeout(timeout); + furni5.state = -1; + timeout = setTimeout(() => { + furni5.state = Math.floor(Math.random() * 6) + 1; + }, 1000); + /*setTimeout(() => { + furni5.state = 0 + }, 2000);*/ + } else { + clearTimeout(timeout); + furni5.state = 0; + } + //x@} + }; +} diff --git a/src/Scuti.ts b/src/Scuti.ts new file mode 100644 index 0000000..fd57297 --- /dev/null +++ b/src/Scuti.ts @@ -0,0 +1,126 @@ +import { Application, BaseTexture, Container, SCALE_MODES, settings } from 'pixi.js' +import { PixiPlugin } from 'gsap/PixiPlugin' +import { gsap } from 'gsap' +import { Stage } from '@pixi/layers' + +import { Logger } from './utilities/Logger' +import type { IRendererConfiguration } from './types/Configuration' +import { AssetLoader } from './utilities/AssetLoader' + +/** + * Convenience class to create a new Scuti renderer. + * + * This class automatically load all the needed resources and initialise the PixiJS application. + * @example + * import { Scuti } from 'scuti-renderer'; + * + * // Create the renderer + * const renderer = new Scuti({ + * canvas: document.getElementById("app"), + * width: window.innerWidth, + * height: window.innerHeight, + * resources: './resources' + * }); + * await renderer.loadResources(); + * + * @class + * @memberof Scuti + */ +export class Scuti { + /** + * The canvas that will be used to render the PixiJS canvas. + * + * @member {HTMLElement} + * @private + */ + private readonly _canvas: HTMLElement + + /** + * The PixiJS application instance that will be used to render everything. + * + * @member {Application} + * @private + */ + private readonly _application: Application + + /** + * The renderer logger instance. + * + * @member {Logger} + * @private + */ + private readonly _logger: Logger = new Logger('Scuti') + + /** + * @param {IRendererConfiguration} [configuration] - The renderer configuration. + * @param {HTMLElement} [configuration.canvas] - The canvas that will be used to render everything. + * @param {number} [configuration.width] - The width of the render part. + * @param {number} [configuration.height] - The height of the render part. + * @param {string} [configuration.resources] - The URL of the resource server. + **/ + constructor(configuration: IRendererConfiguration) { + this._logger.info('⚡ Scuti Renderer - v1.0.0') + + /** Change the PixiJS settings and default settings */ + settings.RESOLUTION = 1 + Container.defaultSortableChildren = true + BaseTexture.defaultOptions.scaleMode = SCALE_MODES.NEAREST + + /** Register the plugins */ + gsap.registerPlugin(PixiPlugin) + + /** Create the PixiJS application */ + this._application = new Application({ + width: configuration.width, + height: configuration.height, + resolution: 1, + antialias: false + }) + + /** Support for PIXI.js dev-tool */ + if (process.env.NODE_ENV === 'development') (globalThis as any).__PIXI_APP__ = this._application + this._application.stage = new Stage() + this._canvas = configuration.canvas + + /** Append it to the canvas */ + this._canvas.append(this._application.view as unknown as Node) + } + + /** + * It loads all the resources that are essentials for rendering rooms and objects. + * It's necessary to call this method just after the instanciation of this class. + * + * @member {Promise} + * @public + */ + public async loadResources(domain: string = 'http://127.0.0.1:8081/'): Promise { + AssetLoader.domain = domain + + /** And now load them */ + await Promise.all([ + AssetLoader.load('room/materials', 'generic/room/room_data.json'), + AssetLoader.load('room/room', 'generic/room/room.json'), + AssetLoader.load('room/cursors', 'generic/tile_cursor/tile_cursor.json'), + AssetLoader.load('furnitures/floor/placeholder', 'generic/place_holder/place_holder_furniture.json'), + AssetLoader.load('furnitures/wall/placeholder', 'generic/place_holder/place_holder_wall_item.json'), + AssetLoader.load('furnitures/furnidata', 'gamedata/furnidata.json'), + AssetLoader.load('figures/figuredata', 'gamedata/figuredata.json'), + AssetLoader.load('figures/figuremap', 'gamedata/figuremap.json'), + AssetLoader.load('figures/draworder', 'gamedata/draworder.json'), + AssetLoader.load('figures/actions', 'generic/HabboAvatarActions.json'), + AssetLoader.load('figures/partsets', 'generic/HabboAvatarPartSets.json'), + AssetLoader.load('figures/animations', 'generic/HabboAvatarAnimations.json') + ]) + } + + /** + * Reference to the PixiJS application instance. + * + * @member {Application} + * @readonly + * @public + */ + public get application(): Application { + return this._application + } +} diff --git a/src/enums/Direction.ts b/src/enums/Direction.ts new file mode 100644 index 0000000..c31fdd6 --- /dev/null +++ b/src/enums/Direction.ts @@ -0,0 +1,16 @@ +/** + * Direction enum regroup the 8 directions that an avatar can handle on Habbo. + * + * @enum + * @memberof Scuti + */ +export enum Direction { + NORTH /** Direction 0 */, + NORTH_EAST /** Direction 1 */, + EAST /** Direction 2 */, + SOUTH_EAST /** Direction 3 */, + SOUTH /** Direction 4 */, + SOUTH_WEST /** Direction 5 */, + WEST /** Direction 6 */, + NORTH_WEST /** Direction 7 */ +} diff --git a/src/enums/StairType.ts b/src/enums/StairType.ts new file mode 100644 index 0000000..3b98ba2 --- /dev/null +++ b/src/enums/StairType.ts @@ -0,0 +1,11 @@ +/** + * StairType enum regroup the 3 differents type of stairs available on Habbo. + * + * @enum + * @memberof Scuti + */ +export enum StairType { + INNER_CORNER_STAIR, + OUTER_CORNER_STAIR, + STAIR +} diff --git a/src/enums/WallType.ts b/src/enums/WallType.ts new file mode 100644 index 0000000..82baea9 --- /dev/null +++ b/src/enums/WallType.ts @@ -0,0 +1,12 @@ +/** + * WallType enum regroup the 3 differents type of walls available on Habbo. + * + * @enum + * @memberof Scuti + */ +export enum WallType { + CORNER_WALL, + LEFT_WALL, + RIGHT_WALL, + DOOR_WALL +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..5ea5a9b --- /dev/null +++ b/src/index.ts @@ -0,0 +1,8 @@ +export { Scuti } from './Scuti'; +export { Room } from './objects/rooms/Room'; +export { FloorMaterial } from './objects/rooms/materials/FloorMaterial'; +export { FloorFurniture } from './objects/furnitures/FloorFurniture'; +export { WallMaterial } from './objects/rooms/materials/WallMaterial'; +export { WallFurniture } from './objects/furnitures/WallFurniture'; +export { Avatar } from './objects/avatars/Avatar'; +export { AvatarAction } from './objects/avatars/actions/AvatarAction'; diff --git a/src/objects/avatars/Avatar.ts b/src/objects/avatars/Avatar.ts new file mode 100644 index 0000000..cf1f920 --- /dev/null +++ b/src/objects/avatars/Avatar.ts @@ -0,0 +1,98 @@ +import { gsap } from 'gsap'; + +import type { Direction } from '../../enums/Direction'; +import { AvatarAction } from './actions/AvatarAction'; +import { AvatarActionManager } from './actions/AvatarActionManager'; +import { AvatarAnimationManager } from './animations/AvatarAnimationManager'; +import type { AvatarBodyPart } from './visualizations/AvatarBodyPart'; +import type { IAvatarConfig, IAvatarPosition } from '../../types/Avatar'; +import { RoomObject } from '../rooms/objects/RoomObject'; +import { AvatarVisualization } from './visualizations/AvatarVisualization'; +import { AvatarFigure } from './AvatarFigure'; + +export class Avatar extends RoomObject { + private readonly _figure: AvatarFigure; + private _bodyDirection: Direction; + private _headDirection: Direction; + private _actions: AvatarAction[]; + private readonly _actionManager: AvatarActionManager; + private readonly _animationManager: AvatarAnimationManager; + private readonly _bodyParts: AvatarBodyPart[] = []; + + constructor(config: IAvatarConfig) { + super(config); + + this._headDirection = config.headDirection; + this._bodyDirection = config.bodyDirection; + this._actions = config.actions; + + this._figure = new AvatarFigure(this, config.figure); + this._visualization = new AvatarVisualization(this); + this._actionManager = new AvatarActionManager(AvatarAction.Default); + this._animationManager = new AvatarAnimationManager(config.actions); + } + + public addAction(action: AvatarAction): void { + if (!Boolean(this._animationManager.getAnimation(action))) this._animationManager.registerAnimation(action); + if (!this._actions.includes(action)) this._actions.push(action); + } + + public removeAction(action: AvatarAction): void { + this._actions = this._actions.filter((fAction) => { + return fAction !== action; + }); + } + + move(position: IAvatarPosition, duration: number = 0.5): void { + if (this._visualization === undefined) return; + gsap.to(this.position, { + x: 32 * position.x - 32 * position.y, + y: 16 * position.x + 16 * position.y - 32 * position.z, + duration, + ease: 'linear', + onUpdate: () => this._visualization.updatePosition() + }); + } + + public get actions(): AvatarAction[] { + return this._actions; + } + + public set actions(actions: AvatarAction[]) { + actions.forEach((action) => this._animationManager.registerAnimation(action)); + actions.forEach((action) => this.actions.push(action)); + this._actions = actions; + } + + public get headDirection(): Direction { + return this._headDirection; + } + + public set headDirection(direction: Direction) { + this._headDirection = direction; + } + + public get bodyDirection(): Direction { + return this._bodyDirection; + } + + public set bodyDirection(direction: Direction) { + this._bodyDirection = direction; + } + + public get actionManager(): AvatarActionManager { + return this._actionManager; + } + + public get bodyParts(): AvatarBodyPart[] { + return this._bodyParts; + } + + public get animationManager(): AvatarAnimationManager { + return this._animationManager; + } + + public get figure(): AvatarFigure { + return this._figure; + } +} diff --git a/src/objects/avatars/AvatarFigure.ts b/src/objects/avatars/AvatarFigure.ts new file mode 100644 index 0000000..dbb3982 --- /dev/null +++ b/src/objects/avatars/AvatarFigure.ts @@ -0,0 +1,79 @@ +import { Assets } from 'pixi.js'; + +import type { Figure, IAvatarPart, IFigureData, IFigureMap } from '../../types'; +import type { Avatar } from './Avatar'; +import { AvatarBodyPart } from './visualizations/AvatarBodyPart'; + +export class AvatarFigure { + private readonly _avatar: Avatar; + private _figure!: Figure; + + constructor(avatar: Avatar, figure: string) { + this._avatar = avatar; + + this._parseFigure(figure); + this._parseBodyParts(); + } + + private _parseFigure(figure: string): void { + // todo!(): Handle split errors for the string + + this._figure = new Map( + figure.split('.').map((part) => { + const data = part.split('-'); + return [ + data[0], + { + setId: Number(data[1]), + colors: data.splice(2, 2).map((color) => Number(color)) + } + ] as const; + }) + ); + } + + private _parseBodyParts(): void { + return this._figure.forEach((set, type) => { + const parts = this._getParts(type, set.setId); + + return this._avatar.bodyParts.push( + new AvatarBodyPart(this._avatar, { + type, + setId: set.setId, + colors: set.colors, + parts, + actions: this._avatar.actions + }) + ); + }); + } + + private _getParts(type: string, setId: number): IAvatarPart[] { + const figureData = Assets.get('figures/figuredata'); + const figureMap = Assets.get('figures/figuremap'); + let parts: IAvatarPart[] = []; + + if (!Boolean(figureData.settype[type]?.set[setId])) return parts; + + const hiddenLayers = figureData.settype[type].set[setId].hiddenLayers; + const set = figureData.settype[type].set[setId]; + + set?.parts.forEach((part) => { + const libId = figureMap.parts[part.type][String(part.id)]; + const lib = figureMap.libs[libId]; + //console.log(part.type, libId); + part.lib = lib; + parts.push(part); + }); + + if (hiddenLayers !== undefined) { + parts = parts.filter((part) => !hiddenLayers.includes(part.type)); + } + + return parts; + } + + public get figure(): Figure { + return this._figure; + } +} diff --git a/src/objects/avatars/actions/AvatarAction.ts b/src/objects/avatars/actions/AvatarAction.ts new file mode 100644 index 0000000..88c9a3e --- /dev/null +++ b/src/objects/avatars/actions/AvatarAction.ts @@ -0,0 +1,30 @@ +export enum AvatarAction { + Default = 'Default', + Walk = 'Move', + Sleep = 'Sleep', + GestureSurprised = 'GestureSurprised', + GestureAngry = 'GestureAngry', + GestureSad = 'GestureSad', + GestureSmile = 'GestureSmile', + Gesture = 'Gesture', + Talk = 'Talk', + CarryItem = 'CarryItem', + UseItem = 'UseItem', + Dance = 'Dance', + AvatarEffect = 'AvatarEffect', + Idle = 'Idle', + Laugh = 'Laugh', + Blow = 'Blow', + Sign = 'Sign', + Wave = 'Wave', + Respect = 'Respect', + RideJump = 'RideJump', + SnowboardSquat = 'SnowboardSquat', + SnowboardUp = 'SnowboardUp', + SnowboardOllie = 'SnowboardOllie', + Snowboard360 = 'Snowboard360', + Sit = 'Sit', + Swim = 'Swim', + Float = 'Float', + Lay = 'Lay' +} diff --git a/src/objects/avatars/actions/AvatarActionManager.ts b/src/objects/avatars/actions/AvatarActionManager.ts new file mode 100644 index 0000000..370fb7b --- /dev/null +++ b/src/objects/avatars/actions/AvatarActionManager.ts @@ -0,0 +1,48 @@ +import { Assets } from 'pixi.js'; + +import type { AvatarAction } from './AvatarAction'; +import type { IActionDefinition, IAvatarPartSets } from '../../../types/Avatar'; + +export class AvatarActionManager { + private readonly _avatarActionsLib: IActionDefinition[] = Assets.get('figures/actions'); + private readonly _avatarPartSetsLib: IAvatarPartSets = Assets.get('figures/partsets'); + + constructor(private readonly _defaultAction: AvatarAction) {} + + public filterActions(actions: AvatarAction[], partType: string): AvatarAction[] { + return actions.filter((action: AvatarAction) => { + const actionDefinition: IActionDefinition = this._avatarActionsLib[action]; + return ( + actionDefinition !== undefined && + this._avatarPartSetsLib.activePartSets[actionDefinition.activepartset].includes(partType) + ); + }); + } + + public sortActions(actions: AvatarAction[]): AvatarAction[] { + if (actions.length === 0) return [this._defaultAction]; + return actions.sort((a: AvatarAction, b: AvatarAction) => { + const actionDefinitionA: IActionDefinition = this._avatarActionsLib[a]; + const actionDefinitionB: IActionDefinition = this._avatarActionsLib[b]; + if (Number(actionDefinitionA.precedence) < Number(actionDefinitionB.precedence)) return -1; + if (Number(actionDefinitionA.precedence) > Number(actionDefinitionB.precedence)) return 1; + return 0; + }); + } + + public getActionDefinition(action: AvatarAction): IActionDefinition { + return this._avatarActionsLib[action]; + } + + public get defaultAction(): AvatarAction { + return this._defaultAction; + } + + public get definitions(): IActionDefinition[] { + return this._avatarActionsLib; + } + + public get partSets(): IAvatarPartSets { + return this._avatarPartSetsLib; + } +} diff --git a/src/objects/avatars/animations/AvatarAnimation.ts b/src/objects/avatars/animations/AvatarAnimation.ts new file mode 100644 index 0000000..43b8e3d --- /dev/null +++ b/src/objects/avatars/animations/AvatarAnimation.ts @@ -0,0 +1,15 @@ +import type { IAnimationDefinition, IAnimationFrameData } from '../../../types/Avatar'; +import type { AvatarAction } from '../actions/AvatarAction'; + +export class AvatarAnimation { + constructor(_action: AvatarAction, private readonly _definition: IAnimationDefinition) {} + + public getFrame(frame: number, type: string): IAnimationFrameData { + // @ts-expect-error + return this._definition.frames[frame].bodyparts[type]; + } + + public getFrameCount(): number { + return this._definition.frames.length; + } +} diff --git a/src/objects/avatars/animations/AvatarAnimationManager.ts b/src/objects/avatars/animations/AvatarAnimationManager.ts new file mode 100644 index 0000000..4d185a8 --- /dev/null +++ b/src/objects/avatars/animations/AvatarAnimationManager.ts @@ -0,0 +1,33 @@ +import { Assets } from 'pixi.js'; + +import type { IAvatarPartSets, IAnimationFrameData } from '../../../types/Avatar'; +import type { AvatarAction } from '../actions/AvatarAction'; +import { AvatarAnimation } from './AvatarAnimation'; + +export class AvatarAnimationManager { + private readonly _animations = new Map(); + private readonly _avatarAnimationsLib = Assets.get('figures/animations'); + + constructor(actions: AvatarAction[]) { + actions.forEach((action) => this.registerAnimation(action)); + } + + public registerAnimation(action: AvatarAction): void { + if (this._avatarAnimationsLib[action] === undefined) return; + this._animations.set(action, new AvatarAnimation(action, this._avatarAnimationsLib[action])); + } + + public getAnimation(action: AvatarAction): AvatarAnimation { + return this._animations.get(action); + } + + public getLayerData(action: AvatarAction, frame: number, type: string): IAnimationFrameData { + const animation = this.getAnimation(action); + if (animation === undefined) return; + return animation.getFrame(frame, type); + } + + public get animations(): IAvatarPartSets { + return this._avatarAnimationsLib; + } +} diff --git a/src/objects/avatars/visualizations/AvatarBodyPart.ts b/src/objects/avatars/visualizations/AvatarBodyPart.ts new file mode 100644 index 0000000..1cfa3d1 --- /dev/null +++ b/src/objects/avatars/visualizations/AvatarBodyPart.ts @@ -0,0 +1,231 @@ +import { Assets } from 'pixi.js'; + +import type { IAvatarPart, IBodyPartConfiguration } from '../../../types/Avatar'; +import { AssetLoader } from '../../../utilities/AssetLoader'; +import { ZOrder } from '../../../utilities/ZOrder'; +import { AvatarAction } from '../actions/AvatarAction'; +import type { Avatar } from '../Avatar'; +import { AvatarLayer } from './AvatarLayer'; + +export class AvatarBodyPart { + private readonly _avatar: Avatar; + + private readonly _type: string; // hr - hd - ch - lg - sh + + private readonly _setId: number; + + private readonly _colors: number[]; + + private readonly _parts: IAvatarPart[]; // lib.id, etc... + + private _actions: AvatarAction[]; + + private readonly _frames: Map> = + new Map(); + + private areAllAssetsLoaded = false; + + constructor(avatar: Avatar, config: IBodyPartConfiguration) { + this._avatar = avatar; + this._type = config.type; + this._setId = config.setId; + this._colors = config.colors; + this._parts = config.parts; + this._actions = config.actions; + + const assets: Array> = []; + + this._parts.forEach((part: IAvatarPart) => { + if (part.lib != null) + assets.push(AssetLoader.load('figures/' + part.lib.id, 'figure/' + part.lib.id + '/' + part.lib.id + '.json')); + }); + + Promise.all(assets) + .then(() => (this.areAllAssetsLoaded = true)) + .catch((error) => this._avatar.logger.error(error)); + } + + private _draw(): void { + if (!this.areAllAssetsLoaded) return; + this._parts.forEach((part) => this._createPart(part)); + } + + private _createPart(part: IAvatarPart): void { + if (!this._frames.has(part.id)) this._frames.set(part.id, new Map()); + + if (part.lib == null) return; + + const spritesheet = Assets.get('figures/' + part.lib.id); + + Object.keys(spritesheet.data.partsType).forEach((type) => { + // We register the part type if it's not already registered + if (!this._frames.get(part.id).has(type)) + this._frames.get(part.id).set(type, { + action: AvatarAction.Default, + frame: 0, + repeat: 0 + }); + let direction = this._avatar.bodyDirection; + + // We get the actions, check if it's valid and if the action is included in the active part set + const sortedActions = this._avatar.actionManager.filterActions(this._actions, type); + + let finalAction: AvatarAction = this._avatar.actionManager.sortActions(sortedActions)[0]; + + // If this part type is in the head part set, we put the direction equal to the head direction + if (this._isHeadPart(type)) direction = this._avatar.headDirection; + + // We get the animation gesture and frame + const frameData = this._avatar.animationManager.getLayerData( + finalAction, + this._frames.get(part.id).get(type).frame, + type + ); + let gesture: string = this._avatar.actionManager.getActionDefinition(finalAction).assetpartdefinition; + let frame: number = 0; + let flip: boolean = false; + if (frameData !== undefined) { + this._frames.get(part.id).get(type).action = finalAction; + frame = frameData.frame; + gesture = frameData.assetpartdefinition; + } + + if ([4, 5, 6].includes(direction)) { + flip = true; + } + + // const zDirection = direction; + + let tempDirection: number = direction; + if ([4, 5, 6].includes(tempDirection)) tempDirection = 6 - tempDirection; + + // If the texture don't exist we reinitalise the gesture and the final action + if ( + spritesheet.textures[ + // Skipping type checking because we cannot convert + part.lib.id + '_h_' + gesture + '_' + type + '_' + part.id + '_' + tempDirection + '_' + frame + ] === undefined + ) { + gesture = 'std'; + finalAction = AvatarAction.Default; + this._frames.get(part.id).get(type).action = finalAction; + } + + if ( + ((gesture === 'wav' && (type === 'lh' || type === 'ls' || type === 'lc' || type === 'lcs')) || + (gesture === 'drk' && (type === 'rh' || type === 'rs' || type === 'rcs')) || + (gesture === 'blw' && type === 'rh') || + (gesture === 'sig' && type === 'lh') || + (gesture === 'respect' && type === 'lh')) && + [4, 5, 6].includes(this._avatar.bodyDirection) + ) { + flip = !flip; + } + + if ( + (gesture === 'crr' || gesture === 'respect' || gesture === 'sig') && + [4, 5, 6].includes(this._avatar.bodyDirection) + ) { + if ( + this._avatar.actionManager.partSets.partSets[type] !== undefined && + this._avatar.actionManager.partSets.partSets[type]['flipped-set-type'] !== undefined && + [4, 5, 6, 7].includes(direction) + ) { + type = this._avatar.actionManager.partSets.partSets[type]['flipped-set-type']; + } + } + + const zOrder = ZOrder.avatar(this._avatar.position, this._getDrawOrder(type, gesture, direction)); + + // We create the layer + if ( + spritesheet.textures[ + // Skipping type checking because we cannot convert + part.lib.id + '_h_' + gesture + '_' + type + '_' + part.id + '_' + tempDirection + '_' + frame + ] !== undefined + ) { + const layer = new AvatarLayer(this._avatar, { + type, + part, + gesture, + tint: + part.colorable === 1 && type !== 'ey' ? this._getColor(this._type, this._colors[part.index]) : undefined, + z: zOrder, + flip, + direction, + frame + }); + /*let tempType: string = type; + if(this._avatar.actionManager.partSets.partSets[type] !== undefined && this._avatar.actionManager.partSets.partSets[type]["flipped-set-type"] !== undefined && [4, 5, 6, 7].includes(direction)) { + tempType = this._avatar.actionManager.partSets.partSets[type]["flipped-set-type"]; + if(spritesheet.data.frames[part.lib.id + "_h_std_" + tempType + "_" + part.id + "_" + tempDirection + "_0"] !== undefined) { + layer.x = spritesheet.data.frames[part.lib.id + "_h_std_" + tempType + "_" + part.id + "_" + tempDirection + "_0"].spriteSourceSize.x; + layer.y = spritesheet.data.frames[part.lib.id + "_h_std_" + tempType + "_" + part.id + "_" + tempDirection + "_0"].spriteSourceSize.y; + } + }*/ + this._avatar.addChild(layer); + } + }); + } + + private _isHeadPart(type: string): boolean { + return this._avatar.actionManager.partSets.activePartSets.head.includes(type); + } + + // types must be changed here + private _getColor(type: string, colorId: number): number { + const figureData: [] = Assets.get('figures/figuredata'); + const paletteId = figureData.settype[type].paletteid; + const palette = figureData.palette[String(paletteId)]; + + if (palette[String(colorId)] === undefined) return Number('0xFFFFFF'); + + return Number('0x' + String(palette[String(colorId)].color)); + } + + private _getDrawOrder(type: string, action: string, direction: number): number { + const drawOrder: [] = Assets.get('figures/draworder'); + const drawOrderList = Object.entries( + drawOrder[drawOrder[action] !== undefined ? action : 'std'][direction] ?? drawOrder.std[direction] + ).find((entry) => { + return entry[1] === type; + }); + return drawOrderList !== undefined ? Number(drawOrderList[0]) : 0; + } + + public updateParts(): void { + this._frames.forEach((types, partId) => { + types.forEach((value, type) => { + const animation = this._avatar.animationManager.getAnimation(value.action); + const frameData = this._avatar.animationManager.getLayerData(value.action, value.frame, type); + if (frameData !== undefined) { + const currentFrame = this._frames.get(partId).get(type); + if (frameData.repeats !== undefined) { + if (currentFrame.repeat >= Number(frameData.repeats)) { + currentFrame.repeat = 0; + currentFrame.frame = currentFrame.frame >= animation.getFrameCount() - 1 ? 0 : currentFrame.frame + 1; + } else { + currentFrame.repeat += 1; + } + } else { + currentFrame.frame = currentFrame.frame >= animation.getFrameCount() - 1 ? 0 : currentFrame.frame + 1; + } + } + }); + }); + + return this._draw(); + } + + public get actions(): AvatarAction[] { + return this._actions; + } + + public set actions(actions: AvatarAction[]) { + this._actions = actions; + } + + public get frames(): Map> { + return this._frames; + } +} diff --git a/src/objects/avatars/visualizations/AvatarLayer.ts b/src/objects/avatars/visualizations/AvatarLayer.ts new file mode 100644 index 0000000..50e65a1 --- /dev/null +++ b/src/objects/avatars/visualizations/AvatarLayer.ts @@ -0,0 +1,110 @@ +import { Assets, Container } from 'pixi.js'; +import { Color } from '@pixi/color'; + +import type { Avatar } from '../Avatar'; +import type { IAvatarLayerConfiguration, IAvatarPart } from '../../../types/Avatar'; +import { HitSprite } from '../../interactions/HitSprite'; +import type { Direction } from '../../../enums/Direction'; + +export class AvatarLayer extends Container { + private readonly _avatar: Avatar; + + private readonly _type: string; + private readonly _part: IAvatarPart; + private readonly _gesture: string; + + /** + * The layer tint + * @private + */ + private readonly _tint: number; + + /** + * The layer z + * @private + */ + private readonly _z: number; + + private readonly _direction: Direction; + + /** + * Is the layer flipped + * @private + */ + private readonly _flip: boolean; + + private readonly _frame: number; + + private readonly _alpha: number; + + constructor(avatar: Avatar, configuration: IAvatarLayerConfiguration) { + super(); + + this._avatar = avatar; + this._type = configuration.type; + this._part = configuration.part; + this._gesture = configuration.gesture; + this._tint = configuration.tint; + this._z = configuration.z; + this._flip = configuration.flip; + this._direction = configuration.direction; + this._frame = configuration.frame; + this._alpha = configuration.alpha; + + this._draw(); + } + + private _draw(): void { + let tempDirection = this._direction; + if (this._flip && [4, 5, 6].includes(tempDirection)) this.scale.x = -1; + if (this._flip && [4, 5, 6].includes(tempDirection)) this.x = 64; + if ([4, 5, 6].includes(tempDirection) && this._flip) { + tempDirection = 6 - tempDirection; + } + // const avatarActions: IActionDefinition[] = Assets.get('figures/actions') + // if(this._type === "ls" || this._type === "lh" || this._type === "lc") console.log(2, this._part.lib.id + "_h_" + this._gesture + "_" + this._type + "_" + this._part.id + "_" + tempDirection + "_" + this._frame); + const sprite = new HitSprite( + Assets.get('figures/' + this._part.lib.id).textures[ + this._part.lib.id + + '_h_' + + this._gesture + + '_' + + this._type + + '_' + + String(this._part.id) + + '_' + + String(tempDirection) + + '_' + + String(this._frame) + ] + ); + if (this._tint !== undefined) sprite.tint = new Color(this._tint).premultiply(1).toNumber(); + if (this._avatar.room !== undefined) this.parentLayer = this._avatar.room.objects.layer; + if (this._z !== undefined) this.zOrder = this._z; + if (this._alpha !== undefined) sprite.alpha = this._alpha; + //sprite.animationSpeed = 0.167; + //sprite.play(); + sprite.interactive = true; + sprite.on('pointerdown', (event) => { + return this._avatar.eventManager.handlePointerDown({ event }); + }); + sprite.on('pointerup', (event) => { + return this._avatar.eventManager.handlePointerUp({ event }); + }); + sprite.on('pointermove', (event) => { + return this._avatar.eventManager.handlePointerMove({ event }); + }); + sprite.on('pointerout', (event) => { + return this._avatar.eventManager.handlePointerOut({ event }); + }); + sprite.on('pointerover', (event) => { + return this._avatar.eventManager.handlePointerOver({ event }); + }); + + this._avatar.addChild(sprite); + } + + public get avatar(): Avatar { + return this._avatar; + } +} diff --git a/src/objects/avatars/visualizations/AvatarVisualization.ts b/src/objects/avatars/visualizations/AvatarVisualization.ts new file mode 100644 index 0000000..acbca4f --- /dev/null +++ b/src/objects/avatars/visualizations/AvatarVisualization.ts @@ -0,0 +1,134 @@ +import type { IAvatarPosition, Nullable } from '../../../types'; +import { AssetLoader } from '../../../utilities/AssetLoader'; +import { RoomObjectVisualization } from '../../rooms/objects/RoomObjectVisualization'; +import { AvatarAction } from '../actions/AvatarAction'; +import type { Avatar } from '../Avatar'; +import { AvatarLayer } from './AvatarLayer'; + +export class AvatarVisualization extends RoomObjectVisualization { + private readonly _avatar: Avatar; + + private readonly _handItem: Nullable; + + constructor(avatar: Avatar) { + super(); + + this._avatar = avatar; + this._loadAssets(); + + this._avatar.onRoomAdded = (room) => { + if (this.loaded) room.visualization.animationTicker.add(() => this.render()); + }; + } + + private _loadAssets(): void { + const assets: Array> = []; + + if (this._avatar.onLoad !== undefined) this._avatar.onLoad(); + + if (this._hasHandItem()) + assets.push(AssetLoader.load('figures/hh_human_item', 'figure/hh_human_item/hh_human_item.json')); + assets.push(AssetLoader.load('figures/hh_human_body', 'figure/hh_human_body/hh_human_body.json')); + + Promise.all(assets) + .then(() => { + if (this._avatar.onLoadComplete !== undefined) this._avatar.onLoadComplete(); + this.loaded = true; + + if (this.placeholder !== undefined) this.placeholder.destroy(); + if (this._avatar.room != null) this._avatar.room.visualization.animationTicker.add(() => this.render()); + }) + .catch(() => { + this.logger.error('Unable to load the assets'); + }); + } + + render(): void { + this.destroy(); + + if (!this.loaded) return; + if (this._hasHandItem()) this._createHandItem(); + + this._createShadow(); + + this._avatar.bodyParts.forEach((bodyPart) => { + bodyPart.actions = this._avatar.actions; + bodyPart.updateParts(); + }); + + const position = this._avatar.position as IAvatarPosition; + + this._avatar.x = 32 * position.x - 32 * position.y; + this._avatar.y = 16 * position.x + 16 * position.y - 32 * position.z; + } + + // todo!(): destroy avatar's bodyparts + destroy(): void { + // if (this.placeholder.parent !== null) this.placeholder.destroy(); + } + + // todo!(): add figure placeholder + renderPlaceholder(): void { + // const position = this._avatar.position as IAvatarPosition; + // this.placeholder = new Sprite(Assets.get('furnitures/floor/placeholder').textures['place_holder_furniture_64.png']); + // if (this._avatar.room != null) this._avatar.addChild(this.placeholder); + // this.placeholder.x = 32 + 32 * position.x - 32 * position.y - 32; + // this.placeholder.y = 16 * position.x + 16 * position.y - 32 * position.z - 50; + } + + updatePosition(): void {} + + private _createShadow(): void { + this._avatar.addChild( + new AvatarLayer(this._avatar, { + type: 'sd', + part: { id: 1, lib: { id: 'hh_human_body' } }, + gesture: 'std', + tint: undefined, + z: 0, + flip: true, + direction: 0, + frame: 0, + alpha: 0.1 + }) + ); + } + + private _createHandItem(): void { + const item = this._handItem; + const handItemCarryId = this._avatar.actionManager.getActionDefinition(AvatarAction.CarryItem).params[String(item)]; + const handItemUseId = this._avatar.actionManager.getActionDefinition(AvatarAction.UseItem).params[String(item)]; + + if (this._avatar.actions.includes(AvatarAction.UseItem) && handItemUseId !== undefined) { + this._avatar.addChild( + new AvatarLayer(this._avatar, { + type: 'ri', + part: { id: handItemUseId, lib: { id: 'hh_human_item' } }, + gesture: 'drk', + tint: undefined, + z: 1000, + flip: false, + direction: this._avatar.bodyDirection, + frame: 0 + }) + ); + } else if (this._avatar.actions.includes(AvatarAction.CarryItem) && handItemCarryId !== undefined) { + this._avatar.addChild( + new AvatarLayer(this._avatar, { + type: 'ri', + part: { id: handItemCarryId, lib: { id: 'hh_human_item' } }, + gesture: 'crr', + tint: undefined, + z: 1000, + flip: false, + direction: this._avatar.bodyDirection, + frame: 0 + }) + ); + } + } + + private _hasHandItem(): boolean { + return this._handItem !== 0 || this._handItem !== undefined; + } +} diff --git a/src/objects/filters/WiredSelectionFilter.ts b/src/objects/filters/WiredSelectionFilter.ts new file mode 100644 index 0000000..4d8a7a8 --- /dev/null +++ b/src/objects/filters/WiredSelectionFilter.ts @@ -0,0 +1,52 @@ +import { Color, Filter } from 'pixi.js'; + +/** The shader vertex */ +const vertex = ` + attribute vec2 aVertexPosition; + attribute vec2 aTextureCoord; + uniform mat3 projectionMatrix; + varying vec2 vTextureCoord; + void main(void) + { + gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + vTextureCoord = aTextureCoord; + } +`; + +/** The shader fragment */ +const fragment = ` + varying vec2 vTextureCoord; + uniform sampler2D uSampler; + uniform vec3 lineColor; + uniform vec3 backgroundColor; + void main(void) { + vec4 currentColor = texture2D(uSampler, vTextureCoord); + vec3 colorLine = lineColor * currentColor.a; + vec3 colorOverlay = backgroundColor * currentColor.a; + if(currentColor.r == 0.0 && currentColor.g == 0.0 && currentColor.b == 0.0 && currentColor.a > 0.0) { + gl_FragColor = vec4(colorLine.r, colorLine.g, colorLine.b, currentColor.a); + } else if(currentColor.a > 0.0) { + gl_FragColor = vec4(colorOverlay.r, colorOverlay.g, colorOverlay.b, currentColor.a); + } + } +`; + +/** + * WiredSelectionFilter class that aim to reproduce the wired selection effect. + * + * @class + * @memberof Scuti + */ +export class WiredSelectionFilter extends Filter { + /** + * @param {number} [lineColor] - The color of the furniture border when selected. + * @param {number} [backgroundColor] - The main color of the furniture when selected. + **/ + constructor(lineColor: number, backgroundColor: number) { + super(vertex, fragment); + + /** Set the colors */ + this.uniforms.lineColor = new Color(lineColor).toRgbArray(); + this.uniforms.backgroundColor = new Color(backgroundColor).toRgbArray(); + } +} diff --git a/src/objects/furnitures/FloorFurniture.ts b/src/objects/furnitures/FloorFurniture.ts new file mode 100644 index 0000000..796c424 --- /dev/null +++ b/src/objects/furnitures/FloorFurniture.ts @@ -0,0 +1,65 @@ +import { gsap } from 'gsap'; + +import type { IFloorFurnitureConfiguration, IFloorPosition } from '../../types/Furniture'; +import { FurnitureData } from './visualizations/FurnitureData'; +import { FurnitureVisualization } from './visualizations/FurnitureVisualization'; +import { RoomObject } from '../rooms/objects/RoomObject'; + +/** + * FloorFurniture class that aim to reproduce the floor furnitures on Habbo. + * + * @class + * @memberof Scuti + */ +export class FloorFurniture extends RoomObject { + /** + * The furniture id that represent the one in furnidata. + * + * @member {number} + * @private + */ + private readonly _id: number; + + /** + * @param {IFloorFurnitureConfiguration} [config] - The furniture configuration. + */ + constructor(config: IFloorFurnitureConfiguration) { + super(config); + + this._id = config.id; + this._state = config.state ?? 0; + this._data = new FurnitureData(this); + this._visualization = new FurnitureVisualization(this); + } + + /** + * Reference to the furniture id from the furni data. + * + * @member {number} + * @readonly + * @public + */ + public get id(): number { + return this._id; + } + + /** + * Move the furniture at the given position and in time. + * + * @param {IFloorPosition} [position] - The position where we want to move the furniture. + * @param {number} [duration] - The time to move the furniture to the given position. + * @return {void} + * @public + */ + move(position: IFloorPosition, duration: number = 0.5): void { + if (this._visualization === undefined) return; + gsap.to(this.position, { + x: position.x, + y: position.y, + z: position.z, + duration, + ease: 'linear', + onUpdate: () => this._visualization.updatePosition() + }); + } +} diff --git a/src/objects/furnitures/WallFurniture.ts b/src/objects/furnitures/WallFurniture.ts new file mode 100644 index 0000000..9c273c6 --- /dev/null +++ b/src/objects/furnitures/WallFurniture.ts @@ -0,0 +1,64 @@ +import { gsap } from 'gsap'; + +import type { IWallFurniConfig, IWallPosition } from '../../types/Furniture'; +import { RoomObject } from '../rooms/objects/RoomObject'; +import { FurnitureData } from './visualizations/FurnitureData'; +import { FurnitureVisualization } from './visualizations/FurnitureVisualization'; + +/** + * WallFurniture class that aim to reproduce the wall furnitures on Habbo. + * + * @class + * @memberof Scuti + */ +export class WallFurniture extends RoomObject { + /** + * The furniture's id that represent the one in furnidata. + * + * @member {number} + * @private + */ + private readonly _id: number; + + /** + * @param {IWallFurniConfig} [config] - The furniture configuration. + */ + constructor(config: IWallFurniConfig) { + super(config); + + this._id = config.id; + this._state = config.state ?? 0; + this._data = new FurnitureData(this); + this._visualization = new FurnitureVisualization(this); + } + + /** + * Reference to the furniture id from the furni data. + * + * @member {number} + * @readonly + * @public + */ + public get id(): number { + return this._id; + } + + /** + * Move the furniture at the given position and in time. + * + * @param {IWallPosition} [position] - The position where we want to move the furniture. + * @param {number} [duration] - The time to move the furniture to the given position. + * @return {void} + * @public + */ + move(position: IWallPosition, duration: number = 0.5): void { + if (this._visualization === undefined) return; + gsap.to(this.position, { + x: position.x, + y: position.y, + duration, + ease: 'linear', + onUpdate: () => this._visualization.updatePosition() + }); + } +} diff --git a/src/objects/furnitures/visualizations/FurnitureData.ts b/src/objects/furnitures/visualizations/FurnitureData.ts new file mode 100644 index 0000000..7486555 --- /dev/null +++ b/src/objects/furnitures/visualizations/FurnitureData.ts @@ -0,0 +1,97 @@ +import { Assets } from 'pixi.js'; + +import type { ISharedFurniData } from '../../../types/Furniture'; +import { FloorFurniture } from '../FloorFurniture'; +import type { WallFurniture } from '../WallFurniture'; + +/** + * FurnitureData class that manage the data of a furniture. + * + * @class + * @memberof Scuti + */ +export class FurnitureData { + /** + * The furniture instance that we want to retrieve data. + * + * @member {FloorFurniture | WallFurniture} + * @private + */ + private readonly _furniture: FloorFurniture | WallFurniture; + + /** + * The furniture data. + * + * @member {ISharedFurniData} + * @private + */ + private _data!: ISharedFurniData; + + /** + * @param {FloorFurniture | WallFurniture} [furniture] - The furniture instance. + */ + constructor(furniture: FloorFurniture | WallFurniture) { + this._furniture = furniture; + + this._load(); + } + + /** + * Load the furniture data. + * + * @return {void} + * @private + */ + private _load(): void { + const furniType = this._furniture instanceof FloorFurniture ? 'floorItems' : 'wallItems'; + this._data = Assets.get('furnitures/furnidata')[furniType].find((item: ISharedFurniData) => { + return item.id === this._furniture.id; + }); + } + + /** + * Reference to the furniture id. + * + * @member {number} + * @readonly + * @public + */ + public get id(): number { + return this._data.id; + } + + /** + * Reference to the furniture class name. + * + * @member {string} + * @readonly + * @public + */ + public get className(): string { + return this._data.className; + } + + /** + * Reference to the furniture base name. + * + * @member {string} + * @readonly + * @public + */ + public get baseName(): string { + if (!Boolean(this._data.className.includes('*'))) return this._data.className; + return this._data.className.split('*')[0]; + } + + /** + * Reference to the furniture color id. + * + * @member {number} + * @readonly + * @public + */ + public get color(): number | null { + if (!Boolean(this._data.className.includes('*'))) return null; + return Number(this._data.className.split('*')[1]); + } +} diff --git a/src/objects/furnitures/visualizations/FurnitureLayer.ts b/src/objects/furnitures/visualizations/FurnitureLayer.ts new file mode 100644 index 0000000..b12ceb7 --- /dev/null +++ b/src/objects/furnitures/visualizations/FurnitureLayer.ts @@ -0,0 +1,202 @@ +import type { BLEND_MODES } from 'pixi.js'; +import { Assets } from 'pixi.js'; +import { Color } from '@pixi/color'; + +import type { FloorFurniture } from '../FloorFurniture'; +import type { IFurnitureLayerConfiguration } from '../../../types/Furniture'; +import { HitSprite } from '../../interactions/HitSprite'; +import type { WallFurniture } from '../WallFurniture'; +import type { Direction } from '../../../enums/Direction'; +import { WiredSelectionFilter } from '../../filters/WiredSelectionFilter'; + +/** The wired selection filter */ +const WIRED_SELECTION_FILTER = new WiredSelectionFilter(0xffffff, 0x999999); + +/** + * FurnitureLayer class. + * + * @class + * @memberof Scuti + */ +export class FurnitureLayer extends HitSprite { + /** + * The furniture instance. + * + * @member {FloorFurniture | WallFurniture} + * @private + */ + private readonly _furniture: FloorFurniture | WallFurniture; + + /** + * The layer id. + * + * @member {number | string} + * @private + */ + private readonly _layer: number; + + /** + * The layer alpha. + * + * @member {number} + * @private + */ + private readonly _alpha: number; + + /** + * The layer tint. + * + * @member {number} + * @private + */ + private readonly _tint: number; + + /** + * The layer z index. + * + * @member {number} + * @private + */ + private readonly _z: number; + + /** + * The layer blend mode. + * + * @member {BLEND_MODES} + * @private + */ + private readonly _blendMode: BLEND_MODES; + + /** + * Is the layer flipped. + * + * @member {boolean} + * @private + */ + private readonly _flip: boolean; + + /** + * The layer frame id. + * + * @member {number} + * @private + */ + private readonly _frame: number; + + /** + * Is the layer interactive. + * + * @member {boolean} + * @private + */ + private readonly _ignoreMouse: boolean; + + /** + * The layer direction. + * + * @member {Direction} + * @private + */ + private readonly _direction: Direction; + + /** + * The layer tag. + * + * @member {string} + * @private + */ + private readonly _tag: string; + + /** + * @param {FloorFurniture | WallFurniture} [furniture] - The furniture instance. + * @param {IFurnitureLayerConfiguration} [config] - The layer configuration. + */ + constructor(furniture: FloorFurniture | WallFurniture, config: IFurnitureLayerConfiguration) { + super(undefined); + + this._furniture = furniture; + this._layer = config.layer; + this._alpha = config.alpha; + this._tint = config.tint; + this._z = config.z; + this._blendMode = config.blendMode; + this._flip = config.flip; + this._frame = config.frame; + this._ignoreMouse = config.ignoreMouse; + this._direction = config.direction; + this._tag = config.tag; + + this._draw(); + } + + /** + * Draw the part. + * + * @return {void} + * @private + */ + private _draw(): void { + this.filters = []; + this.texture = Assets.get('furnitures/' + this._furniture.data.baseName).textures[ + this._furniture.data.baseName + + '_' + + this._furniture.data.baseName + + '_64_' + + String.fromCharCode(97 + Number(this._layer)) + + '_' + + String(this._direction) + + '_' + + String(this._frame) + ]; + /*console.log( + this._furniture.data.baseName + + '_' + + this._furniture.data.baseName + + '_64_' + + String.fromCharCode(97 + Number(this._layer)) + + '_' + + String(this._direction) + + '_' + + String(this._frame) + );*/ + if (this._tint !== undefined) this.tint = new Color(this._tint).premultiply(1).toNumber(); + if (this._blendMode !== undefined) this.blendMode = this._blendMode; + if (this._alpha !== undefined) this.alpha = this._alpha; + if (this._flip) this.scale.x = -1; + //if (this._furniture.room !== undefined) this.parentLayer = this._furniture.room.objects.layer; + if (this._z !== undefined) this.zIndex = this._z; + if (this._ignoreMouse !== null && !this._ignoreMouse) this.interactive = true; + if (this._furniture.selected) this.filters.push(WIRED_SELECTION_FILTER); + + this.on('pointerdown', (event) => { + return this._furniture.eventManager.handlePointerDown({ event, tag: this._tag }); + }); + this.on('pointerup', (event) => { + return this._furniture.eventManager.handlePointerUp({ event, tag: this._tag }); + }); + this.on('pointermove', (event) => { + return this._furniture.eventManager.handlePointerMove({ event, tag: this._tag }); + }); + this.on('pointerout', (event) => { + return this._furniture.eventManager.handlePointerOut({ event, tag: this._tag }); + }); + this.on('pointerover', (event) => { + return this._furniture.eventManager.handlePointerOver({ event, tag: this._tag }); + }); + } + + /** + * Reference to the furniture instance. + * + * @member {FloorFurniture | WallFurniture} + * @readonly + * @public + */ + public get furniture(): FloorFurniture | WallFurniture { + return this._furniture; + } + + public get layer(): number { + return this._layer; + } +} diff --git a/src/objects/furnitures/visualizations/FurnitureVisualization.ts b/src/objects/furnitures/visualizations/FurnitureVisualization.ts new file mode 100644 index 0000000..158284c --- /dev/null +++ b/src/objects/furnitures/visualizations/FurnitureVisualization.ts @@ -0,0 +1,283 @@ +import type { Spritesheet } from 'pixi.js'; +import { Assets, BLEND_MODES, Sprite } from 'pixi.js'; + +import { Direction } from '../../../enums/Direction'; +import { ZOrder } from '../../../utilities/ZOrder'; +import { AssetLoader } from '../../../utilities/AssetLoader'; +import { FurnitureLayer } from './FurnitureLayer'; +import { RoomObjectVisualization } from '../../rooms/objects/RoomObjectVisualization'; +import type { + IFloorPosition, + IFurnitureLayerData, + IFurnitureProperty, + IFurnitureVisualization, + IWallPosition +} from '../../../types/Furniture'; +import { WallFurniture } from '../WallFurniture'; +import { FloorFurniture } from '../FloorFurniture'; + +export class FurnitureVisualization extends RoomObjectVisualization { + // updateLayerPosition and layerData private + private readonly _frames: Map = new Map(); + + public _layers = new Map(); + + public _spritesheet!: Spritesheet; + + public _properties!: IFurnitureProperty; + + public _furniture: FloorFurniture | WallFurniture; + + constructor(furniture: FloorFurniture | WallFurniture) { + super(); + + this._furniture = furniture; + this._loadAssets(); + + this._furniture.onRoomAdded = (room) => { + if (this.loaded) room.visualization.animationTicker.add(() => this._update()); + }; + } + + private _loadAssets(): void { + const name = this._furniture.data.baseName; + + if (this._furniture.onLoad !== undefined) this._furniture.onLoad(); + AssetLoader.load('furnitures/' + name, 'furniture/' + name + '/' + name + '.json') + .then(() => { + if (this._furniture.onLoadComplete !== undefined) this._furniture.onLoadComplete(); + + this._spritesheet = Assets.get('furnitures/' + name); + // @ts-expect-error + this._properties = this._spritesheet.data.furniProperty; + this.loaded = true; + + if (this.placeholder !== undefined) this.placeholder.destroy(); + if (this._furniture.room != null) this._furniture.room.visualization.animationTicker.add(() => this._update()); + }) + .catch(() => { + this.logger.error( + 'Unable to load the assets "' + + name + + '". It can be an invalid file, an invalid json format or just it don\t exist!' + ); + }); + } + + private _update(): void { + const visualization = this._properties.visualization; + + for (let i = 0; i < this._properties.visualization.layerCount; i++) { + const frame = visualization.animation[String(this._furniture.state)]; + + if (frame !== undefined && frame[i] !== undefined) { + const frameSequence = frame[i].frameSequence; + const currentFrame = this._frames.get(i) ?? 0; + + if (frameSequence.length > 1) { + if (frameSequence.length - 1 > currentFrame) { + this._frames.set(i, currentFrame + 1); + } else this._frames.set(i, 0); + + this._renderLayer(i, this._frames.get(i) ?? 0); + } else { + if (this._layers.get(i) == null) this._renderLayer(i, 0); + } + } else { + if (this._layers.get(i) == null) this._renderLayer(i, 0); + } + } + } + + private _renderLayer(layer: number, frame: number): void { + const visualization = this._properties.visualization; + const furnitureLayer = this._layers.get(layer); + const frames = visualization.animation[this._furniture.state]; + + if (furnitureLayer != null) furnitureLayer.destroy(); + + if (this._properties.infos.visualization === 'furniture_animated' && frames === undefined) { + this._furniture.state = Number(Object.keys(visualization.animation)[0]); + return; + } + + const layerData = this.layerData(layer, frame); + + if (!visualization.directions.includes(this._furniture.direction)) { + this._furniture.rotate(visualization.directions[0], 0); + } + + if (frames !== undefined && frames[layer] !== undefined && frames[layer].frameSequence.length > 1) + layerData.frame = frame; + + if (frames !== undefined && frames[layer] !== undefined) + layerData.frame = frames[layer].frameSequence[layerData.frame] ?? 0; + + const layerContainer = new FurnitureLayer(this._furniture, layerData); + + layerContainer.zIndex = layerData.z; + this._layers.set(layer, layerContainer); + + if (this._furniture.room != null) { + // @ts-expect-error + this._furniture.addChild(layerContainer); + this.updateLayerPosition(layer); + } + + layerContainer.filters = this._furniture.filters; // TODO: Move this to global + } + + destroy(): void { + if (this.placeholder.parent !== null) this.placeholder.destroy(); + this._layers.forEach((layer) => layer.destroy()); + this._layers.clear(); + } + + render(): void { + this.destroy(); + this.directions = this._properties.visualization.directions; + + for (let i = 0; i < this._properties.visualization.layerCount; i++) { + this._renderLayer(i, this._frames.get(i) ?? 0); + } + } + + updatePosition(): void { + if (this._furniture instanceof FloorFurniture) { + const position = this._furniture.position as IFloorPosition; + + return this._layers.forEach((layer) => { + layer.x = layer.texture.orig.x + 32 + 32 * position.x - 32 * position.y; + layer.y = layer.texture.orig.y + 16 * position.x + 16 * position.y - 32 * position.z; + }); + } else { + const position = this._furniture.position as IWallPosition; + + // TODO: Refactor wall items + return this._layers.forEach((layer) => { + if (this._furniture.direction === Direction.EAST) { + layer.x = layer.texture.orig.x + 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 1; + layer.y = layer.texture.orig.y + 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31; + } else if (this._furniture.direction === Direction.SOUTH) { + layer.x = layer.texture.orig.x + 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 32; + layer.y = layer.texture.orig.x + 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31; + } + }); + } + } + + updateLayerPosition(layer: number): void { + const furnitureLayer = this._layers.get(layer); + if (furnitureLayer == null) return; + if (this._furniture instanceof FloorFurniture) { + const position = this._furniture.position as IFloorPosition; + + furnitureLayer.x = furnitureLayer.texture.orig.x + 32 + 32 * position.x - 32 * position.y; + furnitureLayer.y = furnitureLayer.texture.orig.y + 16 * position.x + 16 * position.y - 32 * position.z; + } else { + const position = this._furniture.position as IWallPosition; + + if (this._furniture.direction === Direction.EAST) { + furnitureLayer.x = + furnitureLayer.texture.orig.x + 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 1; + furnitureLayer.y = + furnitureLayer.texture.orig.y + 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31; + } else if (this._furniture.direction === Direction.SOUTH) { + furnitureLayer.x = + furnitureLayer.texture.orig.x + 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 32; + furnitureLayer.y = + furnitureLayer.texture.orig.x + 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31; + } + } + } + + renderPlaceholder(): void { + if (this._furniture instanceof FloorFurniture) { + const position = this._furniture.position as IFloorPosition; + + this.placeholder = new Sprite( + Assets.get('furnitures/floor/placeholder').textures['place_holder_furniture_64.png'] + ); + + if (this._furniture.room != null) this._furniture.addChild(this.placeholder); + + this.placeholder.x = 32 + 32 * position.x - 32 * position.y - 32; + this.placeholder.y = 16 * position.x + 16 * position.y - 32 * position.z - 50; + } else { + const position = this._furniture.position as IWallPosition; + + this.placeholder = new Sprite( + Assets.get('furnitures/wall/placeholder').textures['place_holder_wall_item_64.png'] + ); + + if (this._furniture.room != null) this._furniture.addChild(this.placeholder); + if (this._furniture.direction === Direction.EAST) { + this.placeholder.x = 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 1; + this.placeholder.y = 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31 - 50; + } else if (this._furniture.direction === Direction.SOUTH) { + this.placeholder.scale.x = -1; + this.placeholder.x = 32 + 32 * position.x - 32 * position.y + position.offsetX * 2 - 32; + this.placeholder.y = 16 * position.x + 16 * position.y - 32 + position.offsetY * 2 + 31 - 50; + } + } + } + + layerData(layer: number, frame: number = 0): IFurnitureLayerData { + const spritesheet = Assets.get('furnitures/' + this._furniture.data.baseName); + // @ts-expect-error + const visualization = spritesheet.data.furniProperty.visualization as IFurnitureVisualization; + const layerData: IFurnitureLayerData = { + layer, + alpha: 1, + z: 0, + blendMode: BLEND_MODES.NORMAL, + flip: false, + frame: 0, + ignoreMouse: false, + direction: Direction.NORTH + }; + + if (!visualization.directions.includes(this._furniture.direction)) + this._furniture.rotate(visualization.directions[0], 0); + + layerData.direction = this._furniture.direction; + + if ( + this._furniture.data.color !== null && + visualization.colors[this._furniture.data.color] !== undefined && + visualization.colors[this._furniture.data.color][layer] !== undefined + ) + layerData.tint = Number('0x' + String(visualization.colors[this._furniture.data.color][layer])); + + const visualizationLayerData = visualization.layers[layer]; + + if (visualizationLayerData !== undefined) { + if (visualizationLayerData.z !== undefined) layerData.z = visualizationLayerData.z; + if (visualizationLayerData.alpha !== undefined) layerData.alpha = visualizationLayerData.alpha / 255; + if (visualizationLayerData.ink !== undefined) layerData.blendMode = BLEND_MODES[visualizationLayerData.ink]; + if (visualization.layers[layer].ignoreMouse !== undefined) + layerData.ignoreMouse = visualizationLayerData.ignoreMouse; + if (visualization.layers[layer].tag !== undefined) layerData.tag = visualizationLayerData.tag; + } + + const name = [ + this._furniture.data.baseName, + this._furniture.data.baseName, + 64, + String.fromCharCode(97 + Number(layer)), + this._furniture.direction, + frame + ].join('_'); + + if (this._furniture instanceof FloorFurniture) + layerData.z = ZOrder.floorItem(this._furniture.position as IFloorPosition, layerData.z); + + if (this._furniture instanceof WallFurniture) + layerData.z = ZOrder.wallItem(this._furniture.position as IWallPosition, layerData.z); + + // @ts-expect-error + if (spritesheet.data.frames[name] !== undefined) layerData.flip = spritesheet.data.frames[name].flipH; + + return layerData; + } +} diff --git a/src/objects/interactions/EventManager.ts b/src/objects/interactions/EventManager.ts new file mode 100644 index 0000000..3104bb9 --- /dev/null +++ b/src/objects/interactions/EventManager.ts @@ -0,0 +1,416 @@ +import type { IInteractionEvent } from '../../types/Interaction'; +import type { Room } from '../rooms/Room'; + +/** + * InteractionManager class for interaction handling. + * + * @class + * @memberof Scuti + */ +export class EventManager { + /** + * A boolean indicating if the user have clicked at least one time, indicating that the second click is a double click. + * + * @member {boolean} + * @private + */ + private _isDoubleClicking = false; + + /** + * The double click timeout that set the _isDoubleClicking boolean value to false after 350ms. + * + * @member {number} + * @private + */ + private _doubleClickTimeout!: number; + + /** + * The pointer down event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onPointerDown!: (event: IInteractionEvent) => void; + + /** + * The pointer up event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onPointerUp!: (event: IInteractionEvent) => void; + + /** + * The pointer move event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onPointerMove!: (event: IInteractionEvent) => void; + + /** + * The pointer out event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onPointerOut!: (event: IInteractionEvent) => void; + + /** + * The pointer ouver event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onPointerOver!: (event: IInteractionEvent) => void; + + /** + * The pointer double click event. + * + * @member {(event: IInteractionEvent) => void} + * @private + */ + private _onDoubleClick!: (event: IInteractionEvent) => void; + + /** + * The assets starting load event. + * + * @member {() => void} + * @private + */ + private _onLoad!: () => void; + + /** + * The assets ending load event. + * + * @member {() => void} + * @private + */ + private _onLoadComplete!: () => void; + + /** + * The room add event. + * + * @member {(room: Room) => void} + * @private + */ + private _onRoomAdded!: (room: Room) => void; + + /** + * The room remove event. + * + * @member {(room: Room) => void} + * @private + */ + private _onRoomRemoved!: (room: Room) => void; + + /** + * Reference to the pointer down event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerDown(): (event: IInteractionEvent) => void { + return this._onPointerDown; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerDown(value: (event: IInteractionEvent) => void) { + this._onPointerDown = value; + } + + /** + * Reference to the pointer up event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerUp(): (event: IInteractionEvent) => void { + return this._onPointerUp; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerUp(value: (event: IInteractionEvent) => void) { + this._onPointerUp = value; + } + + /** + * Reference to the pointer move event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerMove(): (event: IInteractionEvent) => void { + return this._onPointerMove; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerMove(value: (event: IInteractionEvent) => void) { + this._onPointerMove = value; + } + + /** + * Reference to the pointer out event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerOut(): (event: IInteractionEvent) => void { + return this._onPointerOut; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerOut(value: (event: IInteractionEvent) => void) { + this._onPointerOut = value; + } + + /** + * Reference to the pointer over event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerOver(): (event: IInteractionEvent) => void { + return this._onPointerOver; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerOver(value: (event: IInteractionEvent) => void) { + this._onPointerOver = value; + } + + /** + * Reference to the pointer double click event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onDoubleClick(): (event: IInteractionEvent) => void { + return this._onDoubleClick; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onDoubleClick(value: (event: IInteractionEvent) => void) { + this._onDoubleClick = value; + } + + /** + * Reference to the assets starting load event. + * + * @member {() => void} + * @readonly + * @public + */ + public get onLoad(): () => void { + return this._onLoad; + } + + /** + * Update the event function that will be executed. + * + * @param {() => void} [value] - The event function that will be executed. + * @public + */ + public set onLoad(value: () => void) { + this._onLoad = value; + } + + /** + * Reference to the assets ending load event. + * + * @member {() => void} + * @readonly + * @public + */ + public get onLoadComplete(): () => void { + return this._onLoadComplete; + } + + /** + * Update the event function that will be executed. + * + * @param {() => void} [value] - The event function that will be executed. + * @public + */ + public set onLoadComplete(value: () => void) { + this._onLoadComplete = value; + } + + /** + * Reference to the room add event. + * + * @member {(room: Room) => void} + * @readonly + * @public + */ + public get onRoomAdded(): (room: Room) => void { + return this._onRoomAdded; + } + + /** + * Update the event function that will be executed. + * + * @param {(room: Room) => void} [value] - The event function that will be executed. + * @public + */ + public set onRoomAdded(value: (room: Room) => void) { + this._onRoomAdded = value; + } + + /** + * Reference to the room remove event. + * + * @member {(room: Room) => void} + * @readonly + * @public + */ + public get onRoomRemoved(): (room: Room) => void { + return this._onRoomRemoved; + } + + /** + * Update the event function that will be executed. + * + * @param {(room: Room) => void} [value] - The event function that will be executed. + * @public + */ + public set onRoomRemoved(value: (room: Room) => void) { + this._onRoomRemoved = value; + } + + /** + * Handle the pointer down event. + * + * @return {void} + * @public + */ + public handlePointerDown(event: IInteractionEvent): void { + if (!this._isDoubleClicking) { + if (this.onPointerDown !== undefined) this._onPointerDown(event); + this._isDoubleClicking = true; + this._doubleClickTimeout = window.setTimeout(() => { + return (this._isDoubleClicking = false); + }, 350); + } else { + if (this.onDoubleClick !== undefined) this._onDoubleClick(event); + this._isDoubleClicking = false; + window.clearTimeout(this._doubleClickTimeout); + } + } + + /** + * Handle the pointer up event. + * + * @return {void} + * @public + */ + public handlePointerUp(event: IInteractionEvent): void { + if (this.onPointerUp !== undefined) this._onPointerUp(event); + } + + /** + * Handle the pointer move event. + * + * @return {void} + * @public + */ + public handlePointerMove(event: IInteractionEvent): void { + if (this.onPointerMove !== undefined) this._onPointerMove(event); + } + + /** + * Handle the pointer out event. + * + * @return {void} + * @public + */ + public handlePointerOut(event: IInteractionEvent): void { + if (this.onPointerOut !== undefined) this._onPointerOut(event); + } + + /** + * Handle the pointer over event. + * + * @return {void} + * @public + */ + public handlePointerOver(event: IInteractionEvent): void { + if (this.onPointerOver !== undefined) this._onPointerOver(event); + } + + /** + * Handle the assets load start event. + * + * @return {void} + * @public + */ + public handleLoad(): void { + if (this.onLoad !== undefined) this._onLoad(); + } + + /** + * Handle the assets load end event. + * + * @return {void} + * @public + */ + public handleLoadComplete(): void { + if (this.onLoadComplete !== undefined) this._onLoadComplete(); + } + + /** + * Handle the room add event. + * + * @return {void} + * @public + */ + public handleRoomAdded(room: Room): void { + if (this.onRoomAdded !== undefined) this._onRoomAdded(room); + } + + /** + * Handle the room remove event. + * + * @return {void} + * @public + */ + public handleRoomRemoved(room: Room): void { + if (this.onRoomRemoved !== undefined) this._onRoomRemoved(room); + } +} diff --git a/src/objects/interactions/HitSprite.ts b/src/objects/interactions/HitSprite.ts new file mode 100644 index 0000000..c8920b1 --- /dev/null +++ b/src/objects/interactions/HitSprite.ts @@ -0,0 +1,71 @@ +import type { IPointData } from 'pixi.js'; +import { Sprite } from 'pixi.js'; + +import { HitTexture } from './HitTexture'; + +/** + * HitSprite class that manage the interactions with sprite transparency. + * + * @class + * @memberof Scuti + */ +export class HitSprite extends Sprite { + /** + * The sprite interactivity. + * + * @member {boolean} + * @public + */ + public interactive!: boolean; + + /** + * The global sprite position in the canvas. + * + * @member {{ x: number, y: number }} + * @public + */ + public getGlobalPosition: () => { x: number; y: number }; + + /** + * The hit texture that contains the hit map data. + * + * @member {HitTexture} + * @private + */ + private _hitTexture!: HitTexture; + + /** + * Return a boolean indicating if the pointer is on the sprite. + * + * @return {boolean} + * @public + */ + public containsPoint(point: IPointData): boolean { + /** The sprite is not interactive, so we stop here */ + if (!this.interactive) return false; + + if (this.texture.trim === undefined) return false; + + const width = this.texture.orig.width; + const height = this.texture.orig.height; + + const x1 = this.getGlobalPosition().x + this.texture.trim.x; + let y1 = 0; + let flag = false; + + /** Check if the pointer is out of bound of the sprite */ + if (point.x >= x1 && point.x < x1 + width) { + y1 = this.getGlobalPosition().y + this.texture.trim.y; + if (point.y >= y1 && point.y < y1 + height) flag = true; + } + + /** Return false if the pointer is out of bound */ + if (!flag) return false; + + /** Create the hit texture */ + if (this._hitTexture == null) this._hitTexture = new HitTexture(this); + + /** Check the hit map of the hit texture if the pointer is on a transparent pixel or not */ + return this._hitTexture.hit(point.x - x1, point.y - y1, this.scale.x === -1); + } +} diff --git a/src/objects/interactions/HitTexture.ts b/src/objects/interactions/HitTexture.ts new file mode 100644 index 0000000..a2eb08c --- /dev/null +++ b/src/objects/interactions/HitTexture.ts @@ -0,0 +1,290 @@ +import type { BaseTexture, DisplayObject, ICanvas, IRenderer, Resource } from 'pixi.js'; +import { BaseImageResource, Rectangle, RenderTexture, Sprite, Texture } from 'pixi.js'; +import { CanvasRenderTarget } from '@pixi/utils'; + +import type { HitSprite } from './HitSprite'; +import { FurnitureLayer } from '../furnitures/visualizations/FurnitureLayer'; +import { AvatarLayer } from '../avatars/visualizations/AvatarLayer'; + +/** + * HitTexture class create an hit map from a texture to manage interactions. + * + * @class + * @memberof Scuti + */ +export class HitTexture { + /** + * The hit sprite of the hit texture. + * + * @member {HitSprite} + * @readonly + * @private + */ + private readonly _sprite: HitSprite; + + /** + * The hit texture base texture. + * + * @member {Texture} + * @private + */ + private readonly _texture: Texture; + + /** + * The hit map array. + * + * @member {Uint32Array} + * @private + */ + private _hitMap!: Uint32Array; + + /** + * @param {HitSprite} [sprite] - The hit sprite that we want to retrieve the texture. + */ + constructor(sprite: HitSprite) { + this._sprite = sprite; + this._texture = this._generateTexture(); + } + + /** + * Return the cached hit map of the sprite. + * + * @return {Uint32Array} - A Uint32Array that is the cached hit map of the sprite. + * @private + */ + private _getHitMap(): Uint32Array { + if (!Boolean(this._hitMap)) this._hitMap = this._generateHitMap(this._texture.baseTexture); + return this._hitMap; + } + + /** + * Generate the hit map from a specified base texture. + * + * @param {BaseTexture} baseTexture - The base texture that we want to have the hit map. + * @return {Uint32Array} - An Uint32Array that is the hit map of the specified baseTexture. + * @private + */ + private _generateHitMap(baseTexture: BaseTexture): Uint32Array { + const { height: imageHeight, width: imageWidth } = baseTexture.resource; + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d') as CanvasRenderingContext2D; + + canvas.width = imageWidth; + canvas.height = imageHeight; + // @ts-expect-error + context.drawImage(baseTexture.resource.source, 0, 0); + + let w = canvas.width; + let h = canvas.height; + if (w > canvas.width) w = canvas.width; + if (h > canvas.height) h = canvas.height; + + if (w === 0) return new Uint32Array(); + + const imageData = context.getImageData(0, 0, w, h); + const threshold = 255; + const hitmap = new Uint32Array(Math.ceil((w * h) / 32)); + for (let i = 0; i < w * h; i++) { + const ind1 = i % 32; + const ind2 = (i / 32) | 0; + if (imageData.data[i * 4 + 3] >= threshold) { + hitmap[ind2] = hitmap[ind2] | (1 << ind1); + } + } + + return hitmap; + } + + /** + * Generate a sprite texture and not the texture of the entire spritesheet. + * + * @return {Texture} - A texture of the sprite (not all the spritesheet). + * @private + */ + private _generateTexture(): Texture { + const sprite = new Sprite(this._sprite.texture.clone()); + let renderTexture = {} as RenderTexture; + + sprite.x = this._sprite.getGlobalPosition().x + this._sprite.texture.trim.x; + sprite.y = this._sprite.getGlobalPosition().y + this._sprite.texture.trim.y; + sprite.texture.trim.x = 0; + sprite.texture.trim.y = 0; + + if (this._sprite instanceof FurnitureLayer) { + this._sprite.furniture.room.engine.application.stage.addChild(sprite); + renderTexture = this._sprite.furniture.room.engine.application.renderer.generateTexture(sprite); + } else if (this._sprite.parent instanceof AvatarLayer) { + this._sprite.parent.avatar.room.engine.application.stage.addChild(sprite); + renderTexture = this._sprite.parent.avatar.room.engine.application.renderer.generateTexture(sprite); + } + + const image = this._image(renderTexture); + const baseTexture = renderTexture.baseTexture; + + renderTexture.baseTexture.resource = new BaseImageResource(image); + renderTexture.destroy(); + sprite.destroy(); + + return new Texture(baseTexture); + } + + /** + * Will return a boolean that indicate if we hit the sprite. + * + * @param {number} x - The x coordinate of the hit point + * @param {number} y - The y coordinate of the hit point + * @param {boolean} flip - If the sprite is flipped + * @return {boolean} - A boolean indicating if we hit the sprite. + * @public + */ + public hit(x: number, y: number, flip: boolean): boolean { + const hitmap = this._getHitMap(); + const dx = flip + ? this._texture.baseTexture.realWidth - Math.round(x * this._texture.baseTexture.resolution) + : Math.round(x * this._texture.baseTexture.resolution); + const dy = Math.round(y * this._texture.baseTexture.resolution); + const ind = dx + dy * this._texture.baseTexture.realWidth; + const ind1 = ind % 32; + const ind2 = (ind / 32) | 0; + + return (hitmap[ind2] & (1 << ind1)) !== 0; + } + + /** + * Will return a HTML Image of the target. + * + * @param {DisplayObject|RenderTexture} target - A displayObject or renderTexture + * to convert. If left empty will use the main renderer + * @param {string} [format] - Image format, e.g. "image/jpeg" or "image/webp". + * @param {number} [quality] - JPEG or Webp compression from 0 to 1. Default is 0.92. + * @return {HTMLImageElement} HTML Image of the target. + * @private + */ + private _image(target: DisplayObject | RenderTexture, format?: string, quality?: number): HTMLImageElement { + const image = new Image(); + image.src = this._base64(target, format, quality); + return image; + } + + /** + * Will return a a base64 encoded string of this target. It works by calling + * `Extract.getCanvas` and then running toDataURL on that. + * + * @param {DisplayObject|RenderTexture} target - A displayObject or renderTexture + * to convert. If left empty will use the main renderer + * @param {string} [format] - Image format, e.g. "image/jpeg" or "image/webp". + * @param {number} [quality] - JPEG or Webp compression from 0 to 1. Default is 0.92. + * @return {string} A base64 encoded string of the texture. + * @private + */ + private _base64(target: DisplayObject | RenderTexture, format?: string, quality?: number): string { + return this._canvas(target).toDataURL(format, quality); + } + + /** + * Creates a Canvas element, renders this target to it and then returns it. + * + * @param {DisplayObject|RenderTexture} target - A displayObject or renderTexture + * to convert. If left empty will use the main renderer + * @return {HTMLCanvasElement} A Canvas element with the texture rendered on. + * @private + */ + private _canvas(target: DisplayObject | RenderTexture): ICanvas { + const TEMP_RECT = new Rectangle(); + const BYTES_PER_PIXEL = 4; + let renderer = {} as IRenderer; + + if (this._sprite instanceof FurnitureLayer) { + renderer = this._sprite.furniture.room.engine.application.renderer; + } else if (this._sprite.parent instanceof AvatarLayer) { + renderer = this._sprite.parent.avatar.room.engine.application.renderer; + } + + let resolution: number; + let frame: Rectangle; + let flipY: boolean; + let renderTexture: RenderTexture | undefined; + let generated = false; + + if (Boolean(target)) { + if (target instanceof RenderTexture) { + renderTexture = target; + } else { + renderTexture = renderer.generateTexture(target); + generated = true; + } + } + + if (Boolean(renderTexture)) { + resolution = renderTexture.baseTexture.resolution; + frame = renderTexture.frame; + flipY = false; + renderer.renderTexture.bind(renderTexture); + } else { + resolution = renderer.resolution; + + flipY = true; + + frame = TEMP_RECT; + frame.width = renderer.width; + frame.height = renderer.height; + renderer.renderTexture.bind(null); + } + + const width = Math.floor(frame.width * resolution + 1e-4); + const height = Math.floor(frame.height * resolution + 1e-4); + const webglPixels = new Uint8Array(BYTES_PER_PIXEL * width * height); + let canvasBuffer = new CanvasRenderTarget(width, height, 1); + + /** Read pixels to the array */ + const gl = renderer.gl; + gl.readPixels(frame.x * resolution, frame.y * resolution, width, height, gl.RGBA, gl.UNSIGNED_BYTE, webglPixels); + + /** Add the pixels to the canvas */ + const canvasData = canvasBuffer.context.getImageData(0, 0, width, height); + this.arrayPostDivide(webglPixels, canvasData.data); + canvasBuffer.context.putImageData(canvasData, 0, 0); + + /** Pulling pixels */ + if (flipY) { + const target = new CanvasRenderTarget(canvasBuffer.width, canvasBuffer.height, 1); + target.context.scale(1, -1); + + /** We can't render to itself because we should be empty before render. */ + target.context.drawImage(canvasBuffer.canvas, 0, -height); + + canvasBuffer.destroy(); + canvasBuffer = target; + } + + if (generated) renderTexture.destroy(true); + + /** Send the canvas back... */ + return canvasBuffer.canvas; + } + + /** + * Takes premultiplied pixel data and produces regular pixel data + * @private + * @param pixels - array of pixel data + * @param out - output array + */ + private arrayPostDivide( + pixels: number[] | Uint8Array | Uint8ClampedArray, + out: number[] | Uint8Array | Uint8ClampedArray + ): void { + for (let i = 0; i < pixels.length; i += 4) { + const alpha = (out[i + 3] = pixels[i + 3]); + + if (alpha !== 0) { + out[i] = Math.round(Math.min((pixels[i] * 255) / alpha, 255)); + out[i + 1] = Math.round(Math.min((pixels[i + 1] * 255) / alpha, 255)); + out[i + 2] = Math.round(Math.min((pixels[i + 2] * 255) / alpha, 255)); + } else { + out[i] = pixels[i]; + out[i + 1] = pixels[i + 1]; + out[i + 2] = pixels[i + 2]; + } + } + } +} diff --git a/src/objects/rooms/Room.ts b/src/objects/rooms/Room.ts new file mode 100644 index 0000000..b78ddf5 --- /dev/null +++ b/src/objects/rooms/Room.ts @@ -0,0 +1,333 @@ +import { Container } from 'pixi.js'; + +import type { Scuti } from '../../Scuti'; +import { RoomVisualization } from './RoomVisualization'; +import { RoomTileMap } from './RoomTileMap'; +import type { Material } from './materials/Material'; +import { WallMaterial } from './materials/WallMaterial'; +import { FloorMaterial } from './materials/FloorMaterial'; +import { RoomCamera } from './RoomCamera'; +import type { EventManager } from '../interactions/EventManager'; +import type { IRoomConfig } from '../../types/Room'; +import type { RoomPartLayer } from './layers/RoomPartLayer'; +import type { RoomObjectLayer } from './layers/RoomObjectLayer'; + +/** + * Room class for rendering rooms like the ones on Habbo Hotel. + * + * @class + * @memberof Scuti + */ +export class Room extends Container { + /** + * The game engine instance that the room will be using to render objects. + * + * @member {Scuti} + * @private + */ + private readonly _engine: Scuti; + + /** + * The room tile map where every informations about the room model is stored. + * + * @member {RoomTileMap} + * @private + */ + private _tileMap: RoomTileMap; + + /** + * The wall material that will be applied in the room, it contains the color and the texture of the wall. + * + * @member {Material} + * @private + */ + private _wallMaterial: Material; + + /** + * The floor material that will be applied in the room, it contains the color and the texture of the wall. + * + * @member {Material} + * @private + */ + private _floorMaterial: Material; + + /** + * The wall thickness of the room. + * + * @member {number} + * @private + */ + private _wallThickness: number; + + /** + * The floor thickness of the room. + * + * @member {number} + * @private + */ + private _floorThickness: number; + + /** + * The wall height of the room, the height is added to the base height of the room. + * + * @member {number} + * @private + */ + private _wallHeight: number; + + /** + * The room view instance, where all the objects like furnitures, avatars or the tiles, walls and stairs are stored. + * + * @member {RoomVisualization} + * @private + */ + private readonly _visualization: RoomVisualization; + + /** + * The room camera, it manage the room dragging and centering the room when it's out of bounds. + * + * @member {RoomCamera} + * @private + */ + private readonly _camera: RoomCamera; + + /** + * @param {Scuti} [engine] - The Scuti instance that will be used to render the room. + * @param {IRoomConfig} [config] - The room configuration. + * @param {string} [config.tilemap] - The room tile map that will be parsed. + * @param {Material} [config.floorMaterial] - The room floor material that will be applied. + * @param {number} [config.floorThickness] - The room floor thickness. + * @param {Material} [config.wallMaterial] - The room wall material that will be applied. + * @param {number} [config.wallHeight] - The room wall height. + * @param {number} [config.wallThickness] - The room wall thickness. + **/ + constructor(engine: Scuti, config: IRoomConfig) { + super(); + + /** Store variables */ + this._engine = engine; + this._wallMaterial = config.wallMaterial ?? new WallMaterial(this._engine); + this._floorMaterial = config.floorMaterial ?? new FloorMaterial(this._engine); + this._wallThickness = config.wallThickness ?? 8; + this._floorThickness = config.floorThickness ?? 8; + this._wallHeight = config.wallHeight ?? 0; + + /** Initialise everything */ + this._tileMap = new RoomTileMap(config.tileMap); + this._visualization = new RoomVisualization(this); + this._camera = new RoomCamera(this); + + /** Add the room view and then the room camera to the PixiJS application */ + this.addChild(this._visualization); + this._engine.application.stage.addChild(this._camera); + } + + /** + * Reference to the game engine instance. + * + * @member {Scuti} + * @readonly + * @public + */ + public get engine(): Scuti { + return this._engine; + } + + /** + * Reference to the room view instance. + * + * @member {RoomVisualization} + * @readonly + * @public + */ + public get visualization(): RoomVisualization { + return this._visualization; + } + + /** + * Reference to the room tile map instance. + * + * @member {RoomTileMap} + * @readonly + * @public + */ + public get tileMap(): RoomTileMap { + return this._tileMap; + } + + /** + * Update the room tileMap. + * + * @param {string} [tileMap] - The new room tileMap. + * @public + */ + public set tileMap(tileMap: RoomTileMap) { + this._tileMap = tileMap; + this._visualization.update(); + } + + /** + * Reference to the wall material instance. + * + * @member {Material} + * @readonly + * @public + */ + public get wallMaterial(): Material { + return this._wallMaterial; + } + + /** + * Update the wall material and rerender the room. + * + * @param {Material} [material] - The room wall material that will be applied. + * @public + */ + public set wallMaterial(material: Material) { + this._wallMaterial = material; + this._visualization.update(); + } + + /** + * Reference to the floor material instance. + * + * @member {Material} + * @readonly + * @public + */ + public get floorMaterial(): Material { + return this._floorMaterial; + } + + /** + * Update the floor material and rerender the room. + * + * @param {Material} [material] - The room floor material that will be applied. + * @public + */ + public set floorMaterial(material: Material) { + this._floorMaterial = material; + this._visualization.update(); + } + + /** + * Reference to the wall thickness. + * + * @member {number} + * @readonly + * @public + */ + public get wallThickness(): number { + return this._wallThickness; + } + + /** + * Update the wall thickness and rerender the room. + * + * @param {number} [thickness] - The room wall thickness that will be applied. + * @public + */ + public set wallThickness(thickness: number) { + this._wallThickness = thickness; + this._visualization.update(); + } + + /** + * Reference to the floor thickness. + * + * @member {number} + * @readonly + * @public + */ + public get floorThickness(): number { + return this._floorThickness; + } + + /** + * Update the floor thickness and rerender the room. + * + * @param {number} [thickness] - The room floor thickness that will be applied. + * @public + */ + public set floorThickness(thickness: number) { + this._floorThickness = thickness; + this._visualization.update(); + } + + /** + * Reference to the wall height. + * + * @member {number} + * @readonly + * @public + */ + public get wallHeight(): number { + return this._wallHeight; + } + + /** + * Update the wall height and rerender the room. + * + * @param {number} [height] - The room wall height that will be applied. + * @public + */ + public set wallHeight(height: number) { + this._wallHeight = height; + this._visualization.update(); + } + + /** + * Reference to the tile event manager. + * + * @member {EventManager} + * @readonly + * @public + */ + public get tiles(): EventManager { + return this._visualization.partLayer.tiles; + } + + /** + * Reference to the wall event manager. + * + * @member {EventManager} + * @readonly + * @public + */ + public get walls(): EventManager { + return this._visualization.partLayer.walls; + } + + /** + * Reference to the object layer container. + * + * @member {RoomObjectLayer} + * @readonly + * @public + */ + public get objects(): RoomObjectLayer { + return this._visualization.objectLayer; + } + + /** + * Reference to the part layer container. + * + * @member {RoomPartLayer} + * @readonly + * @public + */ + public get parts(): RoomPartLayer { + return this._visualization.partLayer; + } + + /** + * Reference to the room camera. + * + * @member {RoomCamera} + * @readonly + * @public + */ + public get camera(): RoomCamera { + return this._camera; + } +} diff --git a/src/objects/rooms/RoomCamera.ts b/src/objects/rooms/RoomCamera.ts new file mode 100644 index 0000000..0fb226c --- /dev/null +++ b/src/objects/rooms/RoomCamera.ts @@ -0,0 +1,269 @@ +import { Container, EventBoundary, FederatedPointerEvent } from 'pixi.js'; +import { Expo, gsap } from 'gsap'; + +import type { Room } from './Room'; +import type { Tile } from './parts/Tile'; +import type { Stair } from './parts/Stair'; +import type { RoomObject } from './objects/RoomObject'; + +/** + * RoomCamera class that manage things like the room dragging or detecting if the room is out of bounds. + * + * @class + * @memberof Scuti + */ +export class RoomCamera extends Container { + /** + * The room instance that will be managed by the camera. + * + * @member {Room} + * @private + */ + private readonly _room: Room; + + /** + * A boolean indicating if the room is being dragged. + * + * @member {boolean} + * @private + */ + private _dragging!: boolean; + + /** + * The current selected tile. + * + * @member {Tile | Stair} + * @private + */ + private _selectedTile!: Tile | Stair; + + /** + * The current zoom level. + * + * @member {number} + * @private + */ + private _zoomLevel: number; + + /** + * @param {Room} [room] - The room instance that will be managed by this camera. + */ + constructor(room: Room) { + super(); + + this._room = room; + this._zoomLevel = 1; + + this.addChild(this._room); + + /** Handle interactions */ + this._room.engine.application.renderer.events.domElement.addEventListener('pointerdown', this._dragStart); + this._room.engine.application.renderer.events.domElement.addEventListener('pointerup', this._dragEnd); + this._room.engine.application.renderer.events.domElement.addEventListener('pointermove', this._dragMove); + + /** Handle tile interactions */ + this._room.engine.application.renderer.events.domElement.addEventListener('pointerdown', this._tilePointerDown); + this._room.engine.application.renderer.events.domElement.addEventListener('pointerup', this._tilePointerUp); + this._room.engine.application.renderer.events.domElement.addEventListener('pointermove', this._tilePointerMove); + + window.addEventListener( + 'wheel', + (event) => { + if (event.ctrlKey) event.preventDefault(); + + const delta = Math.sign(event.deltaY); + const zoomLevel = parseFloat((-delta / 8).toFixed(2)); + if (this.zoomLevel + zoomLevel <= 0.8 || this.zoomLevel + zoomLevel >= 2.8) return; + + this.zoomLevel += zoomLevel; + }, + { passive: false } + ); + + window.addEventListener('resize', () => { + this._room.engine.application.view.height = window.innerHeight; + this._room.engine.application.view.width = window.innerWidth; + }); + + this._updateBounds(); + this.centerCamera(); + } + + /** + * Update the room container bounds. + * + * @return {void} + * @private + */ + private _updateBounds(): void { + this.pivot.x = this._room.visualization.getBounds().x; + this.pivot.y = this._room.visualization.getBounds().y; + } + + /** + * Tween the room container at the center of the PixiJS view. + * + * @return {void} + * @public + */ + public centerCamera(object?: RoomObject): void { + if (object === null || object === undefined) + gsap.to(this, { + x: Math.floor(this._room.engine.application.view.width / 2 - this._room.visualization.width / 2), + y: Math.floor(this._room.engine.application.view.height / 2 - this._room.visualization.height / 2), + duration: 0.8, + ease: 'easeOut' + }); + // TODO: Reimplement this part with the new room object + /*else { + const globalPos: Point = object.getGlobalPosition(new Point(object.width / 2, object.height / 2)); + const diffX: number = globalPos.x - this._room.engine.application.view.width / 2; + const diffY: number = globalPos.y - this._room.engine.application.view.height / 2; + gsap.to(this._roomContainer, { + x: Math.floor(this._roomContainer.x - diffX), + y: Math.floor(this._roomContainer.y - diffY), + duration: 0.8, + ease: 'easeOut' + }); + }*/ + } + + /** + * This method is called when the user start dragging the room. + * + * @return {void} + * @private + */ + private readonly _dragStart = (): void => { + // console.log(event.movementX); + this._dragging = true; + }; + + /** + * This method is called when the user stop dragging the room. + * + * @return {void} + * @private + */ + private readonly _dragEnd = (): void => { + this._dragging = false; + if (this._isOutOfBounds()) this.centerCamera(); + }; + + /** + * This method is called when the user is moving the dragged room in the canvas. + * + * @param {PointerEvent} [event] - The mouse event. + * @return {void} + * @private + */ + private readonly _dragMove = (event: PointerEvent): void => { + if (!this._dragging) return; + this.x = Math.floor(this.x + event.movementX * (1 / this._zoomLevel)); + this.y = Math.floor(this.y + event.movementY * (1 / this._zoomLevel)); + }; + + /** + * Indicate if the room container is out of bounds of the PixiJS view. + * + * @return {boolean} + * @private + */ + private _isOutOfBounds(): boolean { + /** Out of bounds on the right */ + if (this.x > this._room.engine.application.view.width) return true; + /** Out of bounds on the left */ + if (this.x + this.width < 0) return true; + /** Out of bounds on the bottom */ + if (this.y > this._room.engine.application.view.height) return true; + /** Out of bounds on the top */ + if (this.y + this.height < 0) return true; + /** It is not out of bounds */ + return false; + } + + /** + * Manage pointer down event on the canvas for tile interaction. + * + * @return {void} + * @private + */ + private readonly _tilePointerDown = (event: PointerEvent): void => { + const tile = this._room.parts.getFromGlobal({ x: event.clientX, y: event.clientY }); + + if (tile != null) tile.emit('pointerdown', new FederatedPointerEvent(new EventBoundary())); + }; + + /** + * Manage pointer up event on the canvas for tile interaction. + * + * @return {void} + * @private + */ + private readonly _tilePointerUp = (event: PointerEvent): void => { + const tile = this._room.parts.getFromGlobal({ x: event.clientX, y: event.clientY }); + + if (tile != null) tile.emit('pointerup', new FederatedPointerEvent(new EventBoundary())); + }; + + /** + * Manage pointer move event on the canvas for tile interaction. + * + * @return {void} + * @private + */ + private readonly _tilePointerMove = (event: PointerEvent): void => { + const objectPart = this._room.parts.getFromGlobal({ x: event.clientX, y: event.clientY }); + + if (objectPart == null) return; + if (this._selectedTile === objectPart) { + objectPart.emit('pointermove', new FederatedPointerEvent(new EventBoundary())); + } else { + if (this._selectedTile != null) + this._selectedTile.emit('pointerout', new FederatedPointerEvent(new EventBoundary())); + if (objectPart != null) objectPart.emit('pointerover', new FederatedPointerEvent(new EventBoundary())); + this._selectedTile = objectPart; + } + }; + + /** + * Zoom the room container. + * + * @param {number} [zoomLevel] - The zoom ratio as a number. + * @public + */ + public set zoomLevel(zoomLevel: number) { + const origWidth = this.width / this._zoomLevel; + const origHeight = this.height / this._zoomLevel; + + this._zoomLevel = zoomLevel; + + // pivot's container property must be changed + // because by default things are centered and scaled from the top corner + // this.pivot.x = ... / this.pivot.y = ... + gsap.set(this.scale, { x: zoomLevel, y: zoomLevel, ease: Expo.easeOut }); + + const diffWidth = origWidth - this.width; + const diffHeight = origHeight - this.height; + const offsetX = this._room.engine.application.view.width / 2 - (this.x + origWidth / 2); + const offsetY = this._room.engine.application.view.height / 2 - (this.y + origHeight / 2); + + gsap.to(this, { + x: this.x + Math.floor(diffWidth / 2 + offsetX), + y: this.y + Math.floor(diffHeight / 2 + offsetY), + duration: 0.8, + ease: 'easeOut' + }); + } + + /** + * Return the zoom level of the room container. + * + * @member {Application} + * @readonly + * @public + */ + public get zoomLevel(): number { + return this._zoomLevel; + } +} diff --git a/src/objects/rooms/RoomTileMap.ts b/src/objects/rooms/RoomTileMap.ts new file mode 100644 index 0000000..943d222 --- /dev/null +++ b/src/objects/rooms/RoomTileMap.ts @@ -0,0 +1,325 @@ +import type { IPosition2D, ITileInfo, TileMap } from '../../types/Room'; +import { WallType } from '../../enums/WallType'; +import { StairType } from '../../enums/StairType'; +import { Direction } from '../../enums/Direction'; + +/** + * RoomTileMap class that manage all the things about the room model. + * + * @class + * @memberof Scuti + */ +export class RoomTileMap { + /** + * The room tile map where every informations about the room model is stored. + * + * @member {TileMap} + * @private + */ + private readonly _tileMap: TileMap; + + /** + * @param {string} [tileMap] - The room tile map string that need to be parsed. + */ + constructor(tileMap: string) { + /** Parse the tile map string to convert it into a matrix */ + this._tileMap = this._parse(tileMap); + } + + /** + * Parse the given tile map to convert it into a matrix. + * + * @param {string} [tileMap] - The tile map string that we want to convert into a matrix. + * @return {TileMap} + * @private + */ + private _parse(tileMap: string): TileMap { + tileMap = tileMap.replace(/ /g, ''); + tileMap = tileMap.replace(/\n\n/g, '\n'); + return tileMap.split(/\r?\n/).map((line) => { + return line.split(''); + }); + } + + /** + * Reference to the room tile map matrix. + * + * @member {TileMap} + * @readonly + * @public + */ + public get tileMap(): TileMap { + return this._tileMap; + } + + /** + * Convert the 2D position into it's tile type character. + * + * @param {IPosition2D} [position] - The tile position that we want to have the type. + * @return {string} + * @public + */ + public getTile(position: IPosition2D): string { + return position.x < 0 || + position.y < 0 || + this._tileMap[position.y] === undefined || + this._tileMap[position.y][position.x] === undefined + ? 'x' + : this._tileMap[position.y][position.x]; + } + + /** + * Convert the tile character into a number that is the height of the tile. + * + * @param {IPosition2D} [position] - The tile position that we want to have the height. + * @return {string} + * @public + */ + public getTileHeight(position: IPosition2D): number { + const tile = this.getTile(position); + return tile === 'x' ? 0 : isNaN(Number(tile)) ? tile.charCodeAt(0) - 96 + 9 : Number(tile); + } + + /** + * Return informations about the tile (if it's a stair, a door, if there is walls, ...). + * + * @param {IPosition2D} [position] - The tile position that we want to have informations. + * @return {ITileInfo} + * @public + */ + public getTileInfo(position: IPosition2D): ITileInfo { + return { + tile: this.isTile(position), + door: this.isDoor(position), + height: this.getTileHeight(position), + stairType: this._getStairType(position), + wallType: this._getWallType(position) + }; + } + + /** + * Return walls informations about the given tile, like if it's a left wall, a corner wall, ... + * + * @param {IPosition2D} [position] - The tile position where we want to have the wall informations. + * @return {WallType} + * @private + */ + private _getWallType(position: IPosition2D): WallType | undefined { + const topLeftTile: IPosition2D = { x: position.x - 1, y: position.y - 1 }; + const topTile: IPosition2D = { x: position.x, y: position.y - 1 }; + const midLeftTile: IPosition2D = { x: position.x - 1, y: position.y }; + + if (this.isDoor(position)) return; + + if (!this.isTile(topLeftTile) && !this.isTile(topTile) && !this.isTile(midLeftTile) && this.isTile(position)) + return WallType.CORNER_WALL; + if (!this.isTile(midLeftTile) && this.isTile(position)) return WallType.LEFT_WALL; + if (!this.isTile(topTile) && this.isTile(position)) return WallType.RIGHT_WALL; + } + + /** + * Return stairs informations about the given tile, like if it's a normal stair, a corner stair, ... + * + * @param {IPosition2D} [position] - The tile position where we want to have the stair informations. + * @return {{ type: StairType, direction: Direction }} + * @private + */ + private _getStairType(position: IPosition2D): { type: StairType; direction: Direction } | undefined { + const topLeftTile: IPosition2D = { x: position.x - 1, y: position.y - 1 }; + const topTile: IPosition2D = { x: position.x, y: position.y - 1 }; + const topRightTile: IPosition2D = { x: position.x + 1, y: position.y - 1 }; + + const midLeftTile: IPosition2D = { x: position.x - 1, y: position.y }; + const midRightTile: IPosition2D = { x: position.x + 1, y: position.y }; + + const botLeftTile: IPosition2D = { x: position.x - 1, y: position.y + 1 }; + const botTile: IPosition2D = { x: position.x, y: position.y + 1 }; + const botRightTile: IPosition2D = { x: position.x + 1, y: position.y + 1 }; + + if ( + this.isTile(position) && + this.isTile(topRightTile) && + this._getTileDifference(topRightTile, position) === 1 && + this._getTileDifference(midRightTile, position) === 1 && + this._getTileDifference(topTile, position) === 1 + ) + return { type: StairType.INNER_CORNER_STAIR, direction: Direction.NORTH_EAST }; + if ( + this.isTile(position) && + this.isTile(botRightTile) && + this._getTileDifference(botRightTile, position) === 1 && + this._getTileDifference(midRightTile, position) === 1 && + this._getTileDifference(botTile, position) === 1 + ) + return { type: StairType.INNER_CORNER_STAIR, direction: Direction.SOUTH_EAST }; + if ( + this.isTile(position) && + this.isTile(botLeftTile) && + this._getTileDifference(botLeftTile, position) === 1 && + this._getTileDifference(midLeftTile, position) === 1 && + this._getTileDifference(botTile, position) === 1 + ) + return { type: StairType.INNER_CORNER_STAIR, direction: Direction.SOUTH_WEST }; + if ( + this.isTile(position) && + this.isTile(topLeftTile) && + this._getTileDifference(topLeftTile, position) === 1 && + this._getTileDifference(midLeftTile, position) === 1 && + this._getTileDifference(topTile, position) === 1 + ) + return { type: StairType.INNER_CORNER_STAIR, direction: Direction.NORTH_WEST }; + if (this.isTile(position) && this.isTile(topTile) && this._getTileDifference(topTile, position) === 1) + return { type: StairType.STAIR, direction: Direction.NORTH }; + if ( + this.isTile(position) && + this.isTile(topRightTile) && + this._getTileDifference(topRightTile, position) === 1 && + this._getTileDifference(midRightTile, position) === 0 && + this._getTileDifference(topTile, position) === 0 + ) + return { type: StairType.OUTER_CORNER_STAIR, direction: Direction.NORTH_EAST }; + if (this.isTile(position) && this.isTile(midRightTile) && this._getTileDifference(midRightTile, position) === 1) + return { type: StairType.STAIR, direction: Direction.EAST }; + if ( + this.isTile(position) && + this.isTile(botRightTile) && + this._getTileDifference(botRightTile, position) === 1 && + this._getTileDifference(midRightTile, position) === 0 && + this._getTileDifference(botTile, position) === 0 + ) + return { type: StairType.OUTER_CORNER_STAIR, direction: Direction.SOUTH_EAST }; + if (this.isTile(position) && this.isTile(botTile) && this._getTileDifference(botTile, position) === 1) + return { type: StairType.STAIR, direction: Direction.SOUTH }; + if ( + this.isTile(position) && + this.isTile(botLeftTile) && + this._getTileDifference(botLeftTile, position) === 1 && + this._getTileDifference(midLeftTile, position) === 0 && + this._getTileDifference(botTile, position) === 0 + ) + return { type: StairType.OUTER_CORNER_STAIR, direction: Direction.SOUTH_WEST }; + if (this.isTile(position) && this.isTile(midLeftTile) && this._getTileDifference(midLeftTile, position) === 1) + return { type: StairType.STAIR, direction: Direction.WEST }; + if ( + this.isTile(position) && + this.isTile(topLeftTile) && + this._getTileDifference(topLeftTile, position) === 1 && + this._getTileDifference(midLeftTile, position) === 0 && + this._getTileDifference(topTile, position) === 0 + ) + return { type: StairType.OUTER_CORNER_STAIR, direction: Direction.NORTH_WEST }; + } + + /** + * Calculate the height differencte between two tile position. + * + * @param {IPosition2D} [position1] - The tile position that we want to compare the height. + * @param {IPosition2D} [position2] - The tile position that we want to compare the height. + * @return {number} + * @private + */ + private _getTileDifference(position1: IPosition2D, position2: IPosition2D): number { + return Number(this.getTileHeight(position1)) - Number(this.getTileHeight(position2)); + } + + /** + * Return a boolean that indicate if the tile position given refer to an existing tile. + * + * @param {IPosition2D} [position] - The tile position that we want to see if it exist. + * @return {boolean} + * @public + */ + public isTile(position: IPosition2D): boolean { + return this.getTile(position) !== 'x'; + } + + /** + * Return a boolean that indicate if the given tile is a door. + * + * @param {IPosition2D} [position] - The tile position that we want to see if it's a door. + * @return {boolean} + * @public + */ + public isDoor(position: IPosition2D): boolean { + const topLeftTile: IPosition2D = { x: position.x - 1, y: position.y - 1 }; + const topTile: IPosition2D = { x: position.x, y: position.y - 1 }; + + const midLeftTile: IPosition2D = { x: position.x - 1, y: position.y }; + const midTile: IPosition2D = { x: position.x, y: position.y }; + + const botLeftTile: IPosition2D = { x: position.x - 1, y: position.y + 1 }; + const botTile: IPosition2D = { x: position.x, y: position.y + 1 }; + + return ( + !this.isTile(topTile) && + !this.isTile(topLeftTile) && + !this.isTile(midLeftTile) && + !this.isTile(botLeftTile) && + !this.isTile(botTile) && + this.isTile(midTile) + ); + } + + /** + * Return the max Z value of this tile map. + * + * @return {number} + * @public + */ + public get maxZ(): number { + let z = 0; + for (let y = 0; y < this._tileMap.length; y++) { + for (let x = 0; x < this._tileMap[y].length; x++) { + const height = this.getTileHeight({ x, y }); + if (height > z) z = height; + } + } + return z; + } + + /** + * Indicate if the given tile position have a left or a right wall. + * + * @param {IPosition2D} [position] - The given tile position that we wan't to check if it have walls. + * @return {{ x: boolean, y: boolean }} + * @public + */ + public hasWall(position: IPosition2D): { x: boolean; y: boolean } { + // TODO: Integrate it in _getWallType() + let wallX = false; + let wallY = false; + for (let i = position.y - 1; i >= 0; i--) { + const wall: WallType | undefined = this._getWallType({ x: position.x, y: i }); + if (wall) { + if (wall === WallType.RIGHT_WALL || wall === (WallType.CORNER_WALL as WallType)) { + wallY = true; + } + } + for (let j = position.x - 1; j >= 0; j--) { + const wall2: WallType | undefined = this._getWallType({ x: j, y: i }); + if (wall2) { + if (wall2 === WallType.LEFT_WALL || wall2 === (WallType.CORNER_WALL as WallType)) { + wallY = true; + } + } + } + } + for (let i = position.x - 1; i >= 0; i--) { + const wall: WallType | undefined = this._getWallType({ x: i, y: position.y }); + if (wall) { + if (wall === WallType.LEFT_WALL || wall === (WallType.CORNER_WALL as WallType)) { + wallX = true; + } + } + for (let j = position.y - 1; j >= 0; j--) { + const wall2: WallType | undefined = this._getWallType({ x: i, y: j }); + if (wall2) { + if (wall2 === WallType.RIGHT_WALL || wall2 === (WallType.CORNER_WALL as WallType)) { + wallX = true; + } + } + } + } + return { x: wallX, y: wallY }; + } +} diff --git a/src/objects/rooms/RoomVisualization.ts b/src/objects/rooms/RoomVisualization.ts new file mode 100644 index 0000000..722b0ec --- /dev/null +++ b/src/objects/rooms/RoomVisualization.ts @@ -0,0 +1,404 @@ +import { Container, Ticker } from 'pixi.js'; + +import type { Room } from './Room'; +import type { IPosition3D, ITileInfo } from '../../types/Room'; +import { Tile } from './parts/Tile'; +import { Wall } from './parts/Wall'; +import { Stair } from './parts/Stair'; +import { WallType } from '../../enums/WallType'; +import type { StairType } from '../../enums/StairType'; +import { Cursor } from './parts/Cursor'; +import { RoomObjectLayer } from './layers/RoomObjectLayer'; +import { RoomPartLayer } from './layers/RoomPartLayer'; + +/** + * RoomView class that manage all the rendering part of the room. + * + * @class + * @memberof Scuti + */ +export class RoomVisualization extends Container { + /** + * The room instance that will be managed by the camera. + * + * @member {Room} + * @private + */ + private readonly _room: Room; + + /** + * The container that will contains all the objects like avatars or furnitures. + * + * @member {RoomObjectLayer} + * @private + */ + private readonly _objectLayer: RoomObjectLayer; + + /** + * The container that will contains all the parts like tiles, walls and stairs. + * + * @member {RoomPartLayer} + * @private + */ + private readonly _partLayer: RoomPartLayer; + + /** + * List containing all the walls instances. + * + * @member {Wall} + * @private + */ + private _walls: Wall[] = []; + + /** + * List containing all the tiles and stairs instances. + * + * @member {Tile | Stair} + * @private + */ + private _tiles: Array = []; + + /** + * Infos related to the door tile. + * + * @member {ITileInfo} + * @private + */ + private _doorTile!: ITileInfo; + + /** + * The room tile cursor instance. + * + * @member {Cursor} + * @private + */ + private _cursor!: Cursor; + + /** + * The room animation ticker instance that will manage all the objects animations + * + * @member {Ticker} + * @private + */ + private readonly _animationTicker = new Ticker(); + + /** + * @param {Room} [room] - The room instance that we want to visualize. + */ + constructor(room: Room) { + super(); + + this._room = room; + this._objectLayer = new RoomObjectLayer(this._room); + this._partLayer = new RoomPartLayer(this._room); + + /** Start the animation ticker */ + this._animationTicker.maxFPS = 4; + this._animationTicker.start(); + + /** Render everything */ + this._draw(); + } + + /** + * Draw the room visualization with all the tiles and walls. + * + * @return {void} + * @private + */ + private _draw(): void { + this._destroyParts(); + this._destroyCursor(); + + for (let y = 0; y < this._room.tileMap.tileMap.length; y++) { + for (let x = 0; x < this._room.tileMap.tileMap[y].length; x++) { + const tileInfo = this._room.tileMap.getTileInfo({ x, y }); + + // todo: avoid duplicate tile doors + if (tileInfo.door && this._doorTile != null) tileInfo.door = false; + if (tileInfo.door && this._doorTile == null) this._doorTile = tileInfo; + + this._createPart(tileInfo, { x, y, z: tileInfo.height }); + } + } + } + + /** + * Destroy all the parts (tiles, walls, stairs, ...). + * + * @return {void} + * @private + */ + private _destroyParts(): void { + [...this._tiles, ...this._walls].forEach((part) => part.destroy()); + this._tiles = []; + this._walls = []; + } + + /** + * Rerender all the room visualization. + * + * @return {void} + * @private + */ + public update(): void { + this._draw(); + } + + /** + * Create a room part and add it into the visualization. + * + * @param {ITileInfo} [tileInfo] - The tile informations where we want to create the part. + * @param {IPosition3D} [position] - And the position. + * @return {void} + * @private + */ + private _createPart(tileInfo: ITileInfo, position: IPosition3D): void { + if (tileInfo.wallType !== null || tileInfo.door) { + if ( + tileInfo.wallType === WallType.CORNER_WALL && + !this._room.tileMap.hasWall(position).x && + !this._room.tileMap.hasWall(position).y + ) { + this._createWall(position, WallType.CORNER_WALL); + this._createWall(position, WallType.LEFT_WALL); + this._createWall(position, WallType.RIGHT_WALL); + } else if (tileInfo.wallType === WallType.CORNER_WALL && !this._room.tileMap.hasWall(position).x) { + this._createWall(position, WallType.LEFT_WALL); + } else if (tileInfo.wallType === WallType.CORNER_WALL && !this._room.tileMap.hasWall(position).y) { + this._createWall(position, WallType.RIGHT_WALL); + } + + if (tileInfo.wallType === WallType.LEFT_WALL && !this._room.tileMap.hasWall(position).y) + this._createWall(position, WallType.LEFT_WALL); + if (tileInfo.wallType === WallType.RIGHT_WALL && !this._room.tileMap.hasWall(position).y) + this._createWall(position, WallType.RIGHT_WALL); + + if (tileInfo.door) this._createWall(position, WallType.DOOR_WALL); + } + + if (tileInfo.stairType != null) { + position.direction = tileInfo.stairType.direction; + this._createStair(position, tileInfo.stairType.type); + } else if (tileInfo.door) { + this._createDoor(position); + } else if (tileInfo.tile) { + this._createTile(position, tileInfo); + } + } + + /** + * Destroy the current cursor and draw a new one at the new position. + * + * @param {IPosition3D} [position] - The cursor position. + * @return {void} + * @private + */ + private _createCursor(position: IPosition3D): void { + if (this._cursor != null) { + this._cursor.visible = true; + return this._cursor.moveTo(position); + } + + this._destroyCursor(); + const cursor = new Cursor(this._room, { position }); + this.addChild(cursor); + this._cursor = cursor; + } + + /** + * Destroy the room cursor + * + * @return {void} + * @private + */ + private _destroyCursor(): void { + if (this._cursor != null) this._cursor.visible = false; + } + + /** + * Create a tile. + * + * @param {IPosition3D} [position] - The tile position. + * @param {ITileInfo} [tileInfo] + * @return {void} + * @private + */ + private _createTile(position: IPosition3D, tileInfo: ITileInfo): void { + const tile = new Tile( + this._room, + { position, material: this._room.floorMaterial, thickness: this._room.floorThickness }, + tileInfo + ); + + /** Register interactions */ + tile.onPointerDown = (event) => { + if (this._partLayer.tiles.onPointerDown != null) this._partLayer.tiles.onPointerDown(event); + }; + tile.onPointerUp = (event) => { + if (this._partLayer.tiles.onPointerUp != null) this._partLayer.tiles.onPointerUp(event); + }; + tile.onPointerMove = (event) => { + if (this._partLayer.tiles.onPointerMove != null) this._partLayer.tiles.onPointerMove(event); + }; + tile.onPointerOut = (event) => { + if (this._partLayer.tiles.onPointerOut != null) this._partLayer.tiles.onPointerOut(event); + this._destroyCursor(); + }; + tile.onPointerOver = (event) => { + if (this._partLayer.tiles.onPointerOver != null) this._partLayer.tiles.onPointerOver(event); + this._createCursor(position); + }; + tile.onDoubleClick = (event) => { + if (this._partLayer.tiles.onDoubleClick != null) this._partLayer.tiles.onDoubleClick(event); + }; + + this.addChild(tile); + this._tiles.push(tile); + } + + /** + * Create a door. + * + * @param {IPosition3D} [position] - The door position. + * @return {void} + * @private + */ + private _createDoor(position: IPosition3D): void { + const tile = new Tile(this._room, { position, material: this._room.floorMaterial }); + + /** Register interactions */ + tile.onPointerDown = (event) => { + if (this._partLayer.tiles.onPointerDown != null) this._partLayer.tiles.onPointerDown(event); + }; + tile.onPointerUp = (event) => { + if (this._partLayer.tiles.onPointerUp != null) this._partLayer.tiles.onPointerUp(event); + }; + tile.onPointerMove = (event) => { + if (this._partLayer.tiles.onPointerMove != null) this._partLayer.tiles.onPointerMove(event); + }; + tile.onPointerOut = (event) => { + if (this._partLayer.tiles.onPointerOut != null) this._partLayer.tiles.onPointerOut(event); + this._destroyCursor(); + }; + tile.onPointerOver = (event) => { + if (this._partLayer.tiles.onPointerOver != null) this._partLayer.tiles.onPointerOver(event); + this._createCursor(position); + }; + tile.onDoubleClick = (event) => { + if (this._partLayer.tiles.onDoubleClick != null) this._partLayer.tiles.onDoubleClick(event); + }; + + this.addChild(tile); + this._tiles.push(tile); + } + + /** + * Create a wall. + * + * @param {IPosition3D} [position] - The wall position. + * @param {WallType} [type] - The wall type. + * @return {void} + * @private + */ + private _createWall(position: IPosition3D, type: WallType): void { + const wall = new Wall(this._room, { + position, + material: this._room.wallMaterial, + thickness: this._room.wallThickness, + height: this._room.wallHeight, + type + }); + + // todo!(): register event interactions for walls */ + + this.addChild(wall); + this._walls.push(wall); + } + + /** + * Create stairs. + * + * @param {IPosition3D} [position] - The stairs position. + * @param {StairType} [type] - The stairs type. + * @return {void} + * @private + */ + private _createStair(position: IPosition3D, type: StairType): void { + const stair = new Stair(this._room, { + position, + material: this._room.floorMaterial, + thickness: this._room.floorThickness, + type + }); + + /** Register interactions */ + stair.onPointerDown = (event) => { + if (this._partLayer.tiles.onPointerDown != null) this._partLayer.tiles.onPointerDown(event); + }; + stair.onPointerUp = (event) => { + if (this._partLayer.tiles.onPointerUp != null) this._partLayer.tiles.onPointerUp(event); + }; + stair.onPointerMove = (event) => { + if (this._partLayer.tiles.onPointerMove != null) this._partLayer.tiles.onPointerMove(event); + }; + stair.onPointerOut = (event) => { + if (this._partLayer.tiles.onPointerOut != null) this._partLayer.tiles.onPointerOut(event); + this._destroyCursor(); + }; + stair.onPointerOver = (event) => { + if (this._partLayer.tiles.onPointerOver != null) this._partLayer.tiles.onPointerOver(event); + this._createCursor(position); + }; + stair.onDoubleClick = (event) => { + if (this._partLayer.tiles.onDoubleClick != null) this._partLayer.tiles.onDoubleClick(event); + }; + + this.addChild(stair); + this._tiles.push(stair); + } + + /** + * Reference to the room visualization room instance. + * + * @member {Room} + * @readonly + * @public + */ + public get room(): Room { + return this._room; + } + + /** + * Reference to the object layer container. + * + * @member {RoomObjectLayer} + * @readonly + * @public + */ + public get objectLayer(): RoomObjectLayer { + return this._objectLayer; + } + + /** + * Reference to the part layer container. + * + * @member {RoomObjectLayer} + * @readonly + * @public + */ + public get partLayer(): RoomPartLayer { + return this._partLayer; + } + + /** + * Reference to the room animation ticker instance. + * + * @member {Ticker} + * @readonly + * @public + */ + public get animationTicker(): Ticker { + return this._animationTicker; + } +} diff --git a/src/objects/rooms/layers/RoomObjectLayer.ts b/src/objects/rooms/layers/RoomObjectLayer.ts new file mode 100644 index 0000000..e913eec --- /dev/null +++ b/src/objects/rooms/layers/RoomObjectLayer.ts @@ -0,0 +1,72 @@ +import type { RoomObject } from '../objects/RoomObject'; +import type { Room } from '../Room'; + +/** + * RoomObjectLayer class that manage all the room objects. + * + * @class + * @memberof Scuti + */ +export class RoomObjectLayer { + /** + * The room instance that will be managed by the camera. + * + * @member {Room} + * @private + */ + private readonly _room: Room; + + /** + * The object list. + * + * @member {RoomObject[]} + * @private + */ + private _objects: RoomObject[] = []; + + /** + * @param {Room} [room] - The room instance that we want to visualize. + */ + constructor(room: Room) { + this._room = room; + } + + /** + * Add the given room object into the object layer of the room. + * + * @param {RoomObject[]} [objects] - The room objects that we want to add. + * @return {void} + * @public + */ + public add(...objects: RoomObject[]): void { + return objects.forEach((object) => { + object.room = this._room; + this._objects.push(object); + // @ts-expect-error + this._room.visualization.addChild(object); + if (object.onRoomAdded !== undefined) object.onRoomAdded(this._room); + if (object.visualization.loaded) object.visualization.render(); + else object.visualization.renderPlaceholder(); + }); + } + + /** + * Remove the given room object into the object layer of the room. + * + * @param {RoomObject[]} [objects] - The room objects that we want to remove. + * @return {void} + * @public + */ + public remove(...objects: RoomObject[]): void { + return objects.forEach((object) => { + if (object.onRoomRemoved !== undefined) object.onRoomRemoved(this._room); + + object.room = undefined; + // @ts-expect-error + this._room.visualization.removeChild(object); + this._objects = this._objects.filter((fObject) => fObject !== object); + + object.destroy(); + }); + } +} diff --git a/src/objects/rooms/layers/RoomPartLayer.ts b/src/objects/rooms/layers/RoomPartLayer.ts new file mode 100644 index 0000000..beb84fd --- /dev/null +++ b/src/objects/rooms/layers/RoomPartLayer.ts @@ -0,0 +1,104 @@ +import { Point } from 'pixi.js'; + +import type { Room } from '../Room'; +import type { RoomPart } from '../parts/RoomPart'; +import { EventManager } from '../../interactions/EventManager'; +import type { Tile } from '../parts/Tile'; +import type { Stair } from '../parts/Stair'; +import type { Dimension } from '../../../types/Dimension'; + +/** + * RoomPartLayer class that manage all the room parts. + * + * @class + * @memberof Scuti + */ +export class RoomPartLayer { + /** + * The room instance that will be managed by the camera. + * + * @member {Room} + * @private + */ + private readonly _room: Room; + + /** + * The part list. + * + * @member {RoomPart[]} + * @private + */ + private readonly _parts: RoomPart[] = []; + + /** + * The room tiles interaction manager. + * + * @member {EventManager} + * @private + */ + private readonly _tileInteractionManager = new EventManager(); + + /** + * The room walls interaction manager. + * + * @member {EventManager} + * @private + */ + private readonly _wallInteractionManager = new EventManager(); + + /** + * @param {Room} [room] - The room instance that we want to visualize. + */ + constructor(room: Room) { + this._room = room; + } + + /** + * Add the given room part into the part layer of the room. + * + * @param {RoomPart} [part] - The room part that we want to add. + * @return {void} + * @public + */ + public add(part: RoomPart): void { + this._parts.push(part); + } + + /** + * Return the part at the specified screen position. + * + * @param {IPosition2D} [position] - The screen position. + * @return {Tile | Stair} + * @public + */ + public getFromGlobal(position: Dimension.IPosition2D): Tile | Stair { + const container = this._room.visualization.children.find((container) => { + const point = new Point(position.x, position.y); + if (Boolean(container.hitArea?.contains(container.toLocal(point).x, container.toLocal(point).y))) + return container; + }); + + // @ts-expect-error + return container; + } + + /** + * Return the tile event manager. + * + * @return {EventManager} + * @public + */ + public get tiles(): EventManager { + return this._tileInteractionManager; + } + + /** + * Return the wall event manager. + * + * @return {EventManager} + * @public + */ + public get walls(): EventManager { + return this._wallInteractionManager; + } +} diff --git a/src/objects/rooms/materials/FloorMaterial.ts b/src/objects/rooms/materials/FloorMaterial.ts new file mode 100644 index 0000000..d595515 --- /dev/null +++ b/src/objects/rooms/materials/FloorMaterial.ts @@ -0,0 +1,73 @@ +import type { Spritesheet } from 'pixi.js'; +import { Assets, Sprite, Texture } from 'pixi.js'; + +import { Material } from './Material'; +import type { Scuti } from '../../../Scuti'; +import type { RoomMaterial } from '../../../types/RoomMaterial'; +import { Logger } from '../../../utilities/Logger'; + +export class FloorMaterial extends Material { + /** + * The game engine instance that the room will be using to render texture. + * + * @member {Scuti} + * @private + */ + private readonly _engine: Scuti; + + /** + * The material id from materials.json. + * + * @member {number} + * @private + */ + private readonly _id: number | undefined; + + /** + * @param {Scuti} [engine] - The scuti engine instance to use. + * @param {number} [id] - The id of the material (it can be found into materials.json). + **/ + constructor(engine: Scuti, id?: number) { + super(0xffffff, Texture.WHITE); + + this._engine = engine; + this._id = id; + + this._load(); + } + + /** + * Load the material. + * + * @return {void} + * @private + */ + private _load(): void { + const materials = Assets.get('room/materials'); + const defaultMaterial = materials.floorData.floors[0]; + + let material = materials.floorData.floors.find((material) => { + if (this._id != null) return material.id === this._id.toString(); + else return defaultMaterial; + }); + + if (material == null) { + const console = new Logger('FloorMaterial'); + this._id != null && console.warn(`Unknown floor id: "${this._id}"`); + /** apply default (white) one rather than throwing an error */ + material = defaultMaterial; + } + + const { color, materialId } = material.visualizations[0].layers[0]; + const materialTexture = materials.floorData.textures.find((texture) => { + return texture.id === materialId.toString(); + }); + + const name = materialTexture?.bitmaps[0].assetName as string; + const texture = Assets.get('room/room').textures[`room_${name}.png`]; + const sprite = new Sprite(texture); + + this.color = color; + this.texture = new Texture(this._engine.application.renderer.generateTexture(sprite).baseTexture); + } +} diff --git a/src/objects/rooms/materials/Material.ts b/src/objects/rooms/materials/Material.ts new file mode 100644 index 0000000..156ed40 --- /dev/null +++ b/src/objects/rooms/materials/Material.ts @@ -0,0 +1,76 @@ +import type { Texture } from 'pixi.js'; + +/** + * Material class that regroup the color and texture to be applied on the wall or the floor. + * + * @class + * @memberof Scuti + */ +export class Material { + /** + * The material color. + * + * @member {number} + * @private + */ + private _color: number; + + /** + * The material texture. + * + * @member {Texture} + * @private + */ + private _texture: Texture; + + /** + * @param {number} [color] - The material color. + * @param {Texture} [texture] - The material texture. + **/ + constructor(color: number, texture: Texture) { + this._color = color; + this._texture = texture; + } + + /** + * Reference to the material color. + * + * @member {number} + * @readonly + * @public + */ + public get color(): number { + return this._color; + } + + /** + * Update the material color. + * + * @param {number} [color] - The new material color. + * @public + */ + public set color(color: number) { + this._color = color; + } + + /** + * Reference to the material texture. + * + * @member {Texture} + * @readonly + * @public + */ + public get texture(): Texture { + return this._texture; + } + + /** + * Update the material texture. + * + * @param {Texture} [texture] - The new material texture. + * @public + */ + public set texture(texture: Texture) { + this._texture = texture; + } +} diff --git a/src/objects/rooms/materials/WallMaterial.ts b/src/objects/rooms/materials/WallMaterial.ts new file mode 100644 index 0000000..dcc0846 --- /dev/null +++ b/src/objects/rooms/materials/WallMaterial.ts @@ -0,0 +1,73 @@ +import type { Spritesheet } from 'pixi.js'; +import { Assets, Sprite, Texture } from 'pixi.js'; + +import { Material } from './Material'; +import type { Scuti } from '../../../Scuti'; +import type { RoomMaterial } from '../../../types/RoomMaterial'; +import { Logger } from '../../../utilities/Logger'; + +export class WallMaterial extends Material { + /** + * The game engine instance that the room will be using to render texture. + * + * @member {Scuti} + * @private + */ + private readonly _engine: Scuti; + + /** + * The material id from materials.json. + * + * @member {number} + * @private + */ + private readonly _id: number | undefined; + + /** + * @param {Scuti} [engine] - The scuti engine instance to use. + * @param {number} [id] - The id of the material (it can be found into materials.json). + **/ + constructor(engine: Scuti, id?: number) { + super(0xffffff, Texture.WHITE); + + this._engine = engine; + this._id = id; + + this._load(); + } + + /** + * Load the material. + * + * @return {void} + * @private + */ + private _load(): void { + const materials = Assets.get('room/materials'); + const defaultMaterial = materials.wallData.walls[0]; + + let material = materials.wallData.walls.find((material) => { + if (this._id != null) return material.id === this._id.toString(); + else return defaultMaterial; + }); + + if (material == null) { + const console = new Logger('WallMaterial'); + this._id != null && console.warn(`Unknown wall id: "${this._id}"`); + /** apply default (white) one rather than throwing an error */ + material = defaultMaterial; + } + + const { color, materialId } = material.visualizations[0].layers[0]; + const materialTexture = materials.wallData.textures.find((texture) => { + return texture.id === materialId.toString(); + }); + + const name = materialTexture?.bitmaps[0].assetName as string; + const texture = Assets.get('room/room').textures[`room_${name}.png`]; + const sprite = new Sprite(texture); + + this.color = color; + this.texture = new Texture(this._engine.application.renderer.generateTexture(sprite).baseTexture); + } +} diff --git a/src/objects/rooms/objects/RoomObject.ts b/src/objects/rooms/objects/RoomObject.ts new file mode 100644 index 0000000..24123a7 --- /dev/null +++ b/src/objects/rooms/objects/RoomObject.ts @@ -0,0 +1,547 @@ +import type { Filter } from 'pixi.js'; +import { Container } from 'pixi.js'; +import gsap from 'gsap'; + +import { EventManager } from '../../interactions/EventManager'; +import { Logger } from '../../../utilities/Logger'; +import type { Room } from '../Room'; +import type { Direction } from '../../../enums/Direction'; +import type { RoomObjectVisualization } from './RoomObjectVisualization'; +import type { IFloorPosition, IWallPosition } from '../../../types/Furniture'; +import type { Dimension, IAvatarPosition, IRoomObjectConfig, IInteractionEvent } from '../../../types'; +import type { FurnitureData } from '../../furnitures/visualizations/FurnitureData'; +import { FloorFurniture } from '../../furnitures/FloorFurniture'; +import { WallFurniture } from '../../furnitures/WallFurniture'; + +/** + * RoomObject class that is extended by the avatars or furnitures. + * + * @class + * @memberof Scuti + */ +export abstract class RoomObject extends Container { + /** + * The object's position in the room. + * + * @member {FloorPosition | IWallPosition | IAvatarPosition} + * @private + */ + private readonly _position: IFloorPosition | IWallPosition | IAvatarPosition; + + private _isAnimating = false; + + /** + * The object's direction in the room. + * + * @member {Direction} + * @private + */ + private _direction: Direction; + + /** + * The object's state that represent it's current playing animation. + * + * @member {number} + * @private + */ + public _state!: number; + + /** + * The object's visualization. + * + * @member {FurnitureData} + * @private + */ + public _visualization!: RoomObjectVisualization; + + /** + * The object's data. + * + * @member {FurnitureData} + * @private + */ + public _data!: FurnitureData; + + /** + * The room object's logger instance. + * + * @member {Logger} + * @private + */ + private readonly _logger = new Logger('RoomObject'); + + /** + * The object interaction manager to handle all the clicks and taps. + * + * @member {EventManager} + * @private + */ + private readonly _eventManager = new EventManager(); + + /** + * The room instance that will be managed by the camera. + * + * @member {Room} + * @private + */ + private _room!: Room; + + /** + * The room object filters. + * + * @member {Filter[]}n + * @private + */ + private _filters: Filter[] = []; + + protected constructor(config: IRoomObjectConfig) { + super(); + + this._position = config.position; + this._direction = config.direction; + } + + /** + * Move the object at te given position and in time. + * + * @param {IFloorPosition | IWallPosition | IAvatarPosition} [position] - The position where we want to move the furniture. + * @param {number} [duration] - The time to move the furniture to the given position. + * @return {void} + * @public + */ + abstract move(position: IFloorPosition | IWallPosition | IAvatarPosition, duration: number): void; + + /** + * Rotate the furniture at the given direction and in time. + * + * @param {Direction} [direction] - The new direction of the furniture. + * @param {number} [duration] - The time to rotate the furniture at the given direction. + * @return {void} + * @public + */ + public rotate(direction?: Direction, duration: number = 0.15): void { + if (this instanceof FloorFurniture || this instanceof WallFurniture) { + if (this._visualization === undefined || this._isAnimating) return; + + const z = (this.position as Dimension.IPosition3D).z; + + gsap.to(this.position, { + z: z + 0.5, + duration, + onStart: () => { + this._isAnimating = true; + }, + onUpdate: () => this._visualization.updatePosition(), + onComplete: () => { + if (direction == null) { + const direction = this.visualization.directions.indexOf(this.direction); + const nextDirection = (direction + 1) % this.visualization.directions.length; + + this._direction = this.visualization.directions[nextDirection]; + } else this._direction = direction; + + this._visualization.render(); + gsap.to(this.position, { + z, + duration, + onComplete: () => { + this._visualization.render(); + this._isAnimating = false; + }, + onUpdate: () => this._visualization.updatePosition() + }); + } + }); + } else { + // todo!(): rotate entities (avatar, pet or bot) + } + } + + /** + * Reference to the room object room instance. + * + * @member {Room | undefined} + * @readonly + * @public + */ + get room(): Room { + return this._room; + } + + /** + * Update the current room instance. + * + * @param {Room} [room] - The new room instance. + * @public + */ + set room(room: Room) { + this._room = room; + } + + /** + * Reference to the object event manager. + * + * @member {EventManager} + * @readonly + * @public + */ + get eventManager(): EventManager { + return this._eventManager; + } + + /** + * Reference to the pointer down event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onPointerDown(): (event: IInteractionEvent) => void { + return this._eventManager.onPointerDown; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onPointerDown(value: (event: IInteractionEvent) => void) { + this._eventManager.onPointerDown = value; + } + + /** + * Reference to the pointer up event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onPointerUp(): (event: IInteractionEvent) => void { + return this._eventManager.onPointerUp; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onPointerUp(value: (event: IInteractionEvent) => void) { + this._eventManager.onPointerUp = value; + } + + /** + * Reference to the pointer move event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onPointerMove(): (event: IInteractionEvent) => void { + return this._eventManager.onPointerMove; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onPointerMove(value: (event: IInteractionEvent) => void) { + this._eventManager.onPointerMove = value; + } + + /** + * Reference to the pointer out event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onPointerOut(): (event: IInteractionEvent) => void { + return this._eventManager.onPointerOut; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onPointerOut(value: (event: IInteractionEvent) => void) { + this._eventManager.onPointerOut = value; + } + + /** + * Reference to the pointer over event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onPointerOver(): (event: IInteractionEvent) => void { + return this._eventManager.onPointerOver; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onPointerOver(value: (event: IInteractionEvent) => void) { + this._eventManager.onPointerOver = value; + } + + /** + * Reference to the pointer double click event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + get onDoubleClick(): (event: IInteractionEvent) => void { + return this._eventManager.onDoubleClick; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + set onDoubleClick(value: (event: IInteractionEvent) => void) { + this._eventManager.onDoubleClick = value; + } + + /** + * Reference to the assets starting load event. + * + * @member {() => void} + * @readonly + * @public + */ + public get onLoad(): () => void { + return this._eventManager.onLoad; + } + + /** + * Update the event function that will be executed. + * + * @param {() => void} [value] - The event function that will be executed. + * @public + */ + public set onLoad(value: () => void) { + this._eventManager.onLoad = value; + } + + /** + * Reference to the assets ending load event. + * + * @member {() => void} + * @readonly + * @public + */ + public get onLoadComplete(): () => void { + return this._eventManager.onLoadComplete; + } + + /** + * Update the event function that will be executed. + * + * @param {() => void} [value] - The event function that will be executed. + * @public + */ + public set onLoadComplete(value: () => void) { + this._eventManager.onLoadComplete = value; + } + + /** + * Reference to the room add event. + * + * @member {(room: Room) => void} + * @readonly + * @public + */ + get onRoomAdded(): (event: Room) => void { + return this._eventManager.onRoomAdded; + } + + /** + * Update the event function that will be executed. + * + * @param {(room: Room) => void} [value] - The event function that will be executed. + * @public + */ + set onRoomAdded(value: (room: Room) => void) { + this._eventManager.onRoomAdded = value; + } + + /** + * Reference to the room remove event. + * + * @member {(room: Room) => void} + * @readonly + * @public + */ + get onRoomRemoved(): (event: Room) => void { + return this._eventManager.onRoomRemoved; + } + + /** + * Update the event function that will be executed. + * + * @param {(room: Room) => void} [value] - The event function that will be executed. + * @public + */ + set onRoomRemoved(value: (room: Room) => void) { + this._eventManager.onRoomRemoved = value; + } + + /** + * Reference to the furniture state. + * + * @member {number} + * @readonly + * @public + */ + public get state(): number { + return this._state; + } + + /** + * Update the furniture state (so the animation). + * + * @param {number} [state] - The new furniture state. + * @public + */ + public set state(state: number) { + this._state = state; + this._visualization.render(); + } + + /** + * Reference the filters list. + * + * @member {Filter[]} + * @readonly + * @public + */ + // @ts-expect-error + public get filters(): Filter[] { + return this._filters; + } + + /** + * Update the filters list. + * + * @member {Filter[]} + * @readonly + * @public + */ + public set filters(filters: Filter[]) { + this._filters = filters; + } + + /** + * Add a filter to the room object. + * + * @param {Filter} [filter] - The filter. + * @public + */ + public addFilter(filter: Filter): void { + if (this._filters.includes(filter)) return; + this._filters.push(filter); + this._visualization.render(); + } + + /** + * Remove filter from the room object. + * + * @param {Filter} [filter] - The filter. + * @public + */ + public removeFilter(filter: Filter): void { + this._filters = this._filters.filter((fFilter) => fFilter !== filter); + this._visualization.render(); + } + + /** + * Reference to the object's direction. + * + * @member {Direction} + * @public + */ + public get direction(): Direction { + return this._direction; + } + + /** + * Update the object's direction + * + * @member {Direction} + * @public + */ + public set direction(direction: Direction) { + this._direction = direction; + } + + /** + * Reference to the furniture position in the room. + * + * @member {IFloorPosition} + * @readonly + * @public + */ + // @ts-expect-error + public get position(): IFloorPosition | IWallPosition | IAvatarPosition { + return this._position; + } + + /** + * Destroy the room object from the room. + * + * @return {void} + * @public + */ + destroy(): void { + if (this._visualization === undefined) return; + this._visualization.destroy(); + if (this._room == null) return; + this._room.objects.remove(this); + } + + /** + * Reference to the room object logger instance. + * + * @member {Logger} + * @readonly + * @public + */ + public get logger(): Logger { + return this._logger; + } + + /** + * Reference to the visualization instance. + * + * @member {RoomObjectVisualization} + * @readonly + * @public + */ + public get visualization(): RoomObjectVisualization { + return this._visualization; + } + + /** + * Reference to the furniture data. + * + * @member {FurnitureData} + * @readonly + * @public + */ + public get data(): FurnitureData { + return this._data; + } +} diff --git a/src/objects/rooms/objects/RoomObjectVisualization.ts b/src/objects/rooms/objects/RoomObjectVisualization.ts new file mode 100644 index 0000000..cf8a2ac --- /dev/null +++ b/src/objects/rooms/objects/RoomObjectVisualization.ts @@ -0,0 +1,76 @@ +import type { Sprite } from 'pixi.js'; + +import type { Direction } from '../../../enums/Direction'; +import { Logger } from '../../../utilities/Logger'; + +export abstract class RoomObjectVisualization { + /** + * The room object visualization's logger instance. + * + * @member {Logger} + * @private + */ + private readonly _logger = new Logger('RoomObjectVisualization'); + + private _placeholder!: Sprite; + + private _directions!: Direction[]; + + private _loaded = false; + + /** + * Renders the visual layers of the room object. + * + * @public + */ + public abstract render(): void; + + /** + * Renders the placeholder right before the layers of the room object when loading. + * + * @public + */ + public abstract renderPlaceholder(): void; + + /** + * Updates the position of each visual layer of the room object. + * + * @public + */ + public abstract updatePosition(): void; + + /** + * Removes all visual layers of the room object. + * + * @public + */ + public abstract destroy(): void; + + get loaded(): boolean { + return this._loaded; + } + + set loaded(load: boolean) { + this._loaded = load; + } + + set placeholder(placeholder: Sprite) { + this._placeholder = placeholder; + } + + get placeholder(): Sprite { + return this._placeholder; + } + + set directions(directions: Direction[]) { + this._directions = directions; + } + + get directions(): Direction[] { + return this._directions; + } + + get logger(): Logger { + return this._logger; + } +} diff --git a/src/objects/rooms/parts/Cursor.ts b/src/objects/rooms/parts/Cursor.ts new file mode 100644 index 0000000..d0200fa --- /dev/null +++ b/src/objects/rooms/parts/Cursor.ts @@ -0,0 +1,69 @@ +import type { Spritesheet } from 'pixi.js'; +import { Container, Assets, Sprite } from 'pixi.js'; + +import type { Room } from '../Room'; +import type { ICursorConfiguration, IPosition3D } from '../../../types/Room'; +import { ZOrder } from '../../../utilities/ZOrder'; + +/** + * Cursor class that show up when we move the cursor on a room tile. + * + * @class + * @memberof Scuti + */ +export class Cursor extends Container { + /** + * The cursor position. + * + * @member {IPosition3D} + * @private + */ + private _position: IPosition3D; + + /** + * @param {Room} [_room] - The room instance where the cursor will be drawn. + * @param {ICursorConfiguration} [configuration] - The tile configuration. + **/ + constructor(_room: Room, configuration: ICursorConfiguration) { + super(); + + this._position = configuration.position; + + /** Draw the cursor */ + this._draw(); + // todo!(): create the blue circle 'cursor_64_b' cursor when needed + } + + /** + * Draw the cursor. + * + * @return {void} + * @private + */ + private _draw(): void { + /** Creating the sprite */ + const texture = Assets.get('room/cursors').textures['tile_cursor_64_a_0_0.png']; + const sprite = new Sprite(texture); + + sprite.y = -20; + this.addChild(sprite); + + /** Positionate the cursor and its zIndex */ + this.moveTo(this._position); + this.zIndex = ZOrder.tileCursor(this._position); + } + + /** + * Apply position of the cursor on the x, y axis relative to the local coordinates of the parent. + * + * @param {IPosition3D} [position] - The cursor position. + * @return {void} + * @public + */ + public moveTo(position: IPosition3D): void { + // this.zOrder = ZOrder.tileCursor(position); + this._position = position; + this.x = 32 * this._position.x - 32 * this._position.y; + this.y = 16 * this._position.x + 16 * this._position.y - 32 * this._position.z; + } +} diff --git a/src/objects/rooms/parts/RoomPart.ts b/src/objects/rooms/parts/RoomPart.ts new file mode 100644 index 0000000..0759d6e --- /dev/null +++ b/src/objects/rooms/parts/RoomPart.ts @@ -0,0 +1,228 @@ +import { Container, Point } from 'pixi.js'; + +import type { Dimension } from '../../../types/Dimension'; +import type { Room } from '../Room'; +import type { Stair, Tile } from '.'; +import { EventManager } from '../../interactions/EventManager'; +import type { IInteractionEvent } from '../../../types/Interaction'; + +export abstract class RoomPart extends Container { + /** + * The part's position in the room. + * + * @member {IWallPosition | IPosition3D} + * @private + */ + abstract _position: Dimension.IPosition3D; + + /** + * The part interaction manager to handle all the clicks and taps. + * + * @member {EventManager} + * @private + */ + private readonly _interactionManager = new EventManager(); + + /** + * The room instance where the ârt will be drawn. + * + * @member {Room} + * @private + */ + private _room: Room; + + constructor(room: Room) { + super(); + + this._room = room; + } + + /** + * Return the part at the specified screen position. + * + * @param {IPosition2D} [position] - The screen position. + * @return {Tile | Stair} + * @public + */ + public getFromGlobal(position: Dimension.IPosition2D): Tile | Stair { + const container = this._room.visualization.children.find((container) => { + const point = new Point(position.x, position.y); + if (Boolean(container.hitArea?.contains(container.toLocal(point).x, container.toLocal(point).y))) + return container; + }); + + // @ts-expect-error + return container; + } + + public registerInteractions(position: Dimension.IPosition3D): void { + this.on('pointerdown', (event) => { + return this.interactionManager.handlePointerDown({ + event, + position: { x: position.x, y: position.y, z: position.z } + }); + }); + this.on('pointerup', (event) => { + return this.interactionManager.handlePointerUp({ + event, + position: { x: position.x, y: position.y, z: position.z } + }); + }); + this.on('pointermove', (event) => { + return this.interactionManager.handlePointerMove({ + event, + position: { x: position.x, y: position.y, z: position.z } + }); + }); + this.on('pointerout', (event) => { + return this.interactionManager.handlePointerOut({ + event, + position: { x: position.x, y: position.y, z: position.z } + }); + }); + this.on('pointerover', (event) => { + return this.interactionManager.handlePointerOver({ + event, + position: { x: position.x, y: position.y, z: position.z } + }); + }); + } + + public set room(room: Room) { + this._room = room; + } + + public get room(): Room { + return this._room; + } + + public get interactionManager(): EventManager { + return this._interactionManager; + } + + /** + * Reference to the pointer down event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerDown(): (event: IInteractionEvent) => void { + return this._interactionManager.onPointerDown; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerDown(value: (event: IInteractionEvent) => void) { + this._interactionManager.onPointerDown = value; + } + + /** + * Reference to the pointer up event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerUp(): (event: IInteractionEvent) => void { + return this._interactionManager.onPointerUp; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerUp(value: (event: IInteractionEvent) => void) { + this._interactionManager.onPointerUp = value; + } + + /** + * Reference to the pointer move event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerMove(): (event: IInteractionEvent) => void { + return this._interactionManager.onPointerMove; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerMove(value: (event: IInteractionEvent) => void) { + this._interactionManager.onPointerMove = value; + } + + /** + * Reference to the pointer out event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerOut(): (event: IInteractionEvent) => void { + return this._interactionManager.onPointerOut; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerOut(value: (event: IInteractionEvent) => void) { + this._interactionManager.onPointerOut = value; + } + + /** + * Reference to the pointer over event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onPointerOver(): (event: IInteractionEvent) => void { + return this._interactionManager.onPointerOver; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onPointerOver(value: (event: IInteractionEvent) => void) { + this._interactionManager.onPointerOver = value; + } + + /** + * Reference to the pointer double click event. + * + * @member {(event: IInteractionEvent) => void} + * @readonly + * @public + */ + public get onDoubleClick(): (event: IInteractionEvent) => void { + return this._interactionManager.onDoubleClick; + } + + /** + * Update the event function that will be executed. + * + * @param {(event: IInteractionEvent) => void} [value] - The event function that will be executed. + * @public + */ + public set onDoubleClick(value: (event: IInteractionEvent) => void) { + this._interactionManager.onDoubleClick = value; + } +} diff --git a/src/objects/rooms/parts/Stair.ts b/src/objects/rooms/parts/Stair.ts new file mode 100644 index 0000000..83e5b4e --- /dev/null +++ b/src/objects/rooms/parts/Stair.ts @@ -0,0 +1,579 @@ +import { Container, Graphics, Matrix, Point, Polygon } from 'pixi.js'; +import { Color } from '@pixi/color'; + +import type { Room } from '../Room'; +import type { IPosition3D, IPosition2D, IStairConfiguration } from '../../../types/Room'; +import type { Material } from '../materials/Material'; +import { StairType } from '../../../enums/StairType'; +import { Direction } from '../../../enums/Direction'; +import { FloorMaterial } from '../materials/FloorMaterial'; +import { ZOrder } from '../../../utilities/ZOrder'; +import { RoomPart } from './RoomPart'; + +/** + * Stair class that show up when two tiles side by side have a height difference of one. + * + * @class + * @memberof Scuti + */ +export class Stair extends RoomPart { + /** + * The thickness of the stair part. + * + * @member {number} + * @private + */ + private readonly _thickness: number; + + /** + * The stair material that will be applied to this part, it contains the color and the texture of the stair. + * + * @member {Material} + * @private + */ + private readonly _material: Material; + + /** + * The stair position. + * + * @member {IPosition3D} + * @private + */ + readonly _position: IPosition3D; + + /** + * The stair type. + * + * @member {StairType} + * @private + */ + private readonly _type: StairType; + + /** + * @param {Room} [room] - The room instance where the stair will be drawn. + * @param {IStairConfiguration} [configuration] - The stair configuration. + * @param {Material} [configuration.material] - The stair material that will be applied. + * @param {number} [configuration.thickness] - The stair thickness. + * @param {IPosition3D} [configuration.position] - The stair position. + * @param {StairType} [configuration.type] - The stair type. + **/ + constructor(room: Room, configuration: IStairConfiguration) { + super(room); + + /** Store the configuration */ + this.room = room; + this._position = configuration.position; + this._thickness = configuration.thickness ?? 8; + this._material = configuration.material ?? new FloorMaterial(this.room.engine, 111); + + this._type = configuration.type; + + /** Register interactions */ + this.registerInteractions(this._position); + + /** Draw the stair */ + this._draw(); + } + + /** + * Select which stair should be drawn by it's type. + * + * @return {void} + * @private + */ + private _draw(): void { + if (this._type === StairType.STAIR) { + /** Straight stair */ + + switch (this._position.direction) { + /** Draw a north stair */ + case Direction.NORTH: + return this._drawStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 40, y: 12 }, + { x: 32, y: 16 } + ], + [ + { x: 8, y: -12 }, + { x: 0, y: 0 } + ] + ); + + /** Draw an east stair */ + case Direction.EAST: + return this._drawStair( + [ + { x: 0, y: 0 }, + { x: 32, y: -16 }, + { x: 40, y: -12 }, + { x: 8, y: 4 } + ], + [ + { x: 8, y: -4 }, + { x: 0, y: 0 } + ] + ); + + /** Draw a south stair */ + case Direction.SOUTH: + return this._drawStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 40, y: 12 }, + { x: 32, y: 16 } + ], + [ + { x: -8, y: -4 }, + { x: 24, y: -12 } + ] + ); + + /** Draw a west stair */ + case Direction.WEST: + return this._drawStair( + [ + { x: 0, y: 0 }, + { x: 32, y: -16 }, + { x: 40, y: -12 }, + { x: 8, y: 4 } + ], + [ + { x: -8, y: -12 }, + { x: 24, y: 12 } + ] + ); + } + } else if (this._type === StairType.OUTER_CORNER_STAIR) { + /** Corner stair */ + switch (this._position.direction) { + /** Draw a north east stair */ + case Direction.NORTH_EAST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: 16, y: -8 }, + { x: 24, y: -12 }, + { x: 0, y: 0 }, + { x: 8, y: -4 } + ] + ); + + /** Draw a south east stair */ + case Direction.SOUTH_EAST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: -8, y: 4 }, + { x: 24, y: -12 }, + { x: 0, y: 0 } + ] + ); + + /** Draw a south west stair */ + case Direction.SOUTH_WEST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: -8, y: -4 }, + { x: 16, y: 16 }, + { x: 24, y: -12 }, + { x: -16, y: -8 } + ] + ); + + /** Draw a north west stair */ + case Direction.NORTH_WEST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: 8, y: -12 }, + { x: 48, y: 0 }, + { x: 0, y: 0 }, + { x: -8, y: -12 } + ] + ); + } + } else if (this._type === StairType.INNER_CORNER_STAIR) { + /** Inner corner stair */ + switch (this._position.direction) { + /** Draw a north east inner stair */ + case Direction.NORTH_EAST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: -8, y: 12 }, + { x: 16, y: 16 }, + { x: 24, y: -36 }, + { x: -16, y: 8 } + ] + ); + case Direction.SOUTH_EAST: + break; + + /** Draw a south west inner stair */ + case Direction.SOUTH_WEST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: 16, y: 8 }, + { x: 24, y: -12 }, + { x: 0, y: -24 }, + { x: 8, y: 12 } + ] + ); + + /** Draw a north west inner stair */ + case Direction.NORTH_WEST: + return this._drawCornerStair( + [ + { x: 0, y: 0 }, + { x: 8, y: -4 }, + { x: 16, y: 0 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 8, y: 4 }, + { x: 8, y: 4 } + ], + [ + { x: 0, y: 16 }, + { x: -8, y: 4 }, + { x: 24, y: -36 }, + { x: 0, y: 16 } + ] + ); + } + } + } + + /** + * Draw the stair using the given points and offsets. + * + * @param {IPosition2D[]} [points] - The point list that will be used to draw the stair. + * @param {IPosition2D[]} [offsets] - The offset list that will be used to draw the stair. + * @return {void} + * @private + */ + private _drawStair(points: IPosition2D[], offsets: IPosition2D[]): void { + for (let i = 0; i < 4; i++) { + const step = new Container(); + /** Top face */ + + const top = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(1).toNumber(), + matrix: new Matrix(1, 0.5, 1, -0.5, this._position.y % 2 === 0 ? 32 : 64, this._position.y % 2 === 0 ? 16 : 0) + }) + .moveTo(points[0].x, points[0].y) + .lineTo(points[1].x, points[1].y) + .lineTo(points[2].x, points[2].y) + .lineTo(points[3].x, points[3].y) + .lineTo(points[0].x, points[0].y) + .endFill(); + + /** Left face */ + const left = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.8).toNumber(), + matrix: new Matrix(1, 0.5, 0, 1, 0, 0) + }) + .moveTo(points[0].x, points[0].y) + .lineTo(points[0].x, points[0].y + this._thickness) + .lineTo(points[3].x, points[3].y + this._thickness) + .lineTo(points[3].x, points[3].y) + .endFill(); + + /** Right face */ + const right = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.71).toNumber(), + matrix: new Matrix(1, -0.5, 0, 1, 0, 0) + }) + .moveTo(points[3].x, points[3].y) + .lineTo(points[3].x, points[3].y + this._thickness) + .lineTo(points[2].x, points[2].y + this._thickness) + .lineTo(points[2].x, points[2].y) + .lineTo(points[3].x, points[3].y) + .endFill(); + + /** And we combine everything */ + step.addChild(top); + step.addChild(left); + step.addChild(right); + + /** Add the offsets to the step */ + step.x = offsets[0].x * i; + step.y = offsets[0].y * i; + + /** Add the step to the stair */ + this.addChild(step); + } + + /** Positionate the stair */ + this.x = 32 * this._position.x - 32 * this._position.y + offsets[1].x; + this.y = 16 * this._position.x + 16 * this._position.y - 32 * this._position.z + offsets[1].y; + + /** Set the hit area */ + this.hitArea = new Polygon( + new Point(0 - offsets[1].x, 0 - offsets[1].y), + new Point(32 - offsets[1].x, -16 - offsets[1].y), + new Point(64 - offsets[1].x, 0 - offsets[1].y), + new Point(32 - offsets[1].x, 16 - offsets[1].y), + new Point(0 - offsets[1].x, 0 - offsets[1].y) + ); + /** Set the zIndex */ + this.zIndex = ZOrder.floor(this._position); + } + + /** + * Draw the corner stair using the given points, points offsets and offsets. + * + * @param {IPosition2D[]} [points] - The point list that will be used to draw the stair. + * @param {IPosition2D[]} [pointsOffsets] - The offset point list that will be used to draw the stair. + * @param {IPosition2D[]} [offsets] - The offset list that will be used to draw the stair. + * @return {void} + * @private + */ + private _drawCornerStair(points: IPosition2D[], pointsOffsets: IPosition2D[], offsets: IPosition2D[]): void { + for (let i = 0; i < 3; i++) { + const step = new Container(); + /** Top face */ + + const top = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(1).toNumber(), + matrix: new Matrix(1, 0.5, 1, -0.5, this._position.y % 2 === 0 ? 32 : 64, this._position.y % 2 === 0 ? 16 : 0) + }) + .moveTo(points[0].x + -pointsOffsets[2].x * (2 - i), points[0].y + pointsOffsets[2].y * (2 - i)) + .lineTo(points[1].x + pointsOffsets[1].x * (2 - i), points[1].y + pointsOffsets[1].y * (2 - i)) + .lineTo(points[2].x + pointsOffsets[0].x * (2 - i), points[2].y + pointsOffsets[0].y * (2 - i)) + .lineTo(points[3].x + -pointsOffsets[3].x * (2 - i), points[3].y + pointsOffsets[3].y * (2 - i)) + .lineTo(points[0].x + -pointsOffsets[2].x * (2 - i), points[0].y + pointsOffsets[2].y * (2 - i)) + .endFill(); + + /** Left face */ + const left = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.8).toNumber(), + matrix: new Matrix(1, 0.5, 0, 1, 0, 0) + }) + .moveTo(points[0].x + -pointsOffsets[2].x * (2 - i), points[0].y + pointsOffsets[2].y * (2 - i)) + .lineTo( + points[0].x + -pointsOffsets[2].x * (2 - i), + points[0].y + pointsOffsets[2].y * (2 - i) + this._thickness + ) + .lineTo( + points[3].x + -pointsOffsets[3].x * (2 - i), + points[3].y + pointsOffsets[3].y * (2 - i) + this._thickness + ) + .lineTo(points[3].x + -pointsOffsets[3].x * (2 - i), points[3].y + pointsOffsets[3].y * (2 - i)) + .endFill(); + + /** Right face */ + const right = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.71).toNumber(), + matrix: new Matrix(1, -0.5, 0, 1, 0, 0) + }) + .moveTo(points[3].x + -pointsOffsets[3].x * (2 - i), points[3].y + pointsOffsets[3].y * (2 - i)) + .lineTo( + points[3].x + -pointsOffsets[3].x * (2 - i), + points[3].y + pointsOffsets[3].y * (2 - i) + this._thickness + ) + .lineTo( + points[2].x + -pointsOffsets[0].x * (2 - i), + points[2].y + pointsOffsets[0].y * (2 - i) + this._thickness + ) + .lineTo(points[2].x + -pointsOffsets[0].x * (2 - i), points[2].y + pointsOffsets[0].y * (2 - i)) + .lineTo(points[3].x + -pointsOffsets[3].x * (2 - i), points[3].y + pointsOffsets[3].y * (2 - i)) + .endFill(); + + /** And we combine everything */ + step.addChild(top); + step.addChild(left); + step.addChild(right); + + /** Add the offsets to the step */ + step.x = offsets[3].x * i + offsets[1].x; + step.y = offsets[3].y * i + offsets[1].y; + + /** zIndex */ + if (this._type === StairType.OUTER_CORNER_STAIR) step.zIndex = -i; + if ( + this._type === StairType.INNER_CORNER_STAIR || + (this._type === StairType.OUTER_CORNER_STAIR && this._position.direction === Direction.SOUTH_WEST) + ) + step.zIndex = 4 - i; + + /** Add the step to the stair */ + this.addChild(step); + } + + for (let i = 0; i < 4; i++) { + const step = new Container(); + + /** Top face */ + const top = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(1).toNumber(), + matrix: new Matrix(1, 0.5, 1, -0.5, 0, 0) + }) + .moveTo(points[0].x + pointsOffsets[0].x * (3 - i), points[0].y + pointsOffsets[0].y * (3 - i)) + .lineTo(points[1].x + pointsOffsets[1].x * (3 - i), points[1].y + pointsOffsets[1].y * (3 - i)) + .lineTo(points[2].x + pointsOffsets[2].x * (3 - i), points[2].y + pointsOffsets[2].y * (3 - i)) + .lineTo(points[3].x + pointsOffsets[3].x * (3 - i), points[3].y + pointsOffsets[3].y * (3 - i)) + .lineTo(points[0].x + pointsOffsets[0].x * (3 - i), points[0].y + pointsOffsets[0].y * (3 - i)) + .endFill(); + + /** Left face */ + const left = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.8).toNumber(), + matrix: new Matrix(1, 0.5, 0, 1, 0, 0) + }) + .moveTo(points[0].x + pointsOffsets[0].x * (3 - i), points[0].y + pointsOffsets[0].y * (3 - i)) + .lineTo( + points[0].x + pointsOffsets[0].x * (3 - i), + points[0].y + pointsOffsets[0].y * (3 - i) + this._thickness + ) + .lineTo( + points[3].x + pointsOffsets[3].x * (3 - i), + points[3].y + pointsOffsets[3].y * (3 - i) + this._thickness + ) + .lineTo(points[3].x + pointsOffsets[3].x * (3 - i), points[3].y + pointsOffsets[3].y * (3 - i)) + .endFill(); + + /** Right face */ + const right = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.71).toNumber(), + matrix: new Matrix(1, -0.5, 0, 1, 0, 0) + }) + .moveTo(points[3].x + pointsOffsets[3].x * (3 - i), points[3].y + pointsOffsets[3].y * (3 - i)) + .lineTo( + points[3].x + pointsOffsets[3].x * (3 - i), + points[3].y + pointsOffsets[3].y * (3 - i) + this._thickness + ) + .lineTo( + points[2].x + pointsOffsets[2].x * (3 - i), + points[2].y + pointsOffsets[2].y * (3 - i) + this._thickness + ) + .lineTo(points[2].x + pointsOffsets[2].x * (3 - i), points[2].y + pointsOffsets[2].y * (3 - i)) + .lineTo(points[3].x + pointsOffsets[3].x * (3 - i), points[3].y + pointsOffsets[3].y * (3 - i)) + .endFill(); + + /** And we combine everything */ + step.addChild(top); + step.addChild(left); + step.addChild(right); + + /** Add the offsets to the step */ + step.x = offsets[0].x * i; + step.y = offsets[0].y * i; + + /** zIndex */ + if (this._type === StairType.INNER_CORNER_STAIR) step.zIndex = 3 - i; + + /** Add the step to the stair */ + this.addChild(step); + } + + this.sortableChildren = true; + + /** Positionate the stair */ + this.x = 32 * this._position.x - 32 * this._position.y + offsets[2].x; + this.y = 16 * this._position.x + 16 * this._position.y - 32 * this._position.z + offsets[2].y; + + /** Set the hit area */ + this.hitArea = new Polygon( + new Point(0 - offsets[2].x, 0 - offsets[2].y), + new Point(32 - offsets[2].x, -16 - offsets[2].y), + new Point(64 - offsets[2].x, 0 - offsets[2].y), + new Point(32 - offsets[2].x, 16 - offsets[2].y), + new Point(0 - offsets[2].x, 0 - offsets[2].y) + ); + /** Set the zIndex */ + this.zIndex = ZOrder.floor(this._position); + } +} diff --git a/src/objects/rooms/parts/Tile.ts b/src/objects/rooms/parts/Tile.ts new file mode 100644 index 0000000..0b418b0 --- /dev/null +++ b/src/objects/rooms/parts/Tile.ts @@ -0,0 +1,219 @@ +import { Graphics, Matrix, Point, Polygon } from 'pixi.js'; +import { Color } from '@pixi/color'; + +import type { Room } from '../Room'; +import type { ITileConfiguration, ITileInfo } from '../../../types/Room'; +import type { Material } from '../materials/Material'; +import { FloorMaterial } from '../materials/FloorMaterial'; +import { ZOrder } from '../../../utilities/ZOrder'; +import { RoomPart } from './RoomPart'; +import type { Dimension } from '../../../types/Dimension'; + +/** + * Tile class that show up during room rendering. + * + * @class + * @memberof Scuti + */ +export class Tile extends RoomPart { + /** + * The ITileInfo instance + * + * @member {ITileInfo} + * @private + */ + private readonly _tileInfo?: ITileInfo; + + /** + * The thickness of the tile part. + * + * @member {number} + * @private + */ + private _thickness: number; + + /** + * The tile material that will be applied to this part, it contains the color and the texture of the tile. + * + * @member {Material} + * @private + */ + private _material: Material; + + /** + * The tile position. + * + * @member {IPosition3D} + * @private + */ + readonly _position: Dimension.IPosition3D; + + /** + * @param {Room} [room] - The room instance where the tile will be drawn. + * @param {ITileConfiguration} [configuration] - The tile configuration. + * @param {Material} [configuration.material] - The time material that will be applied. + * @param {number} [configuration.thickness] - The tile thickness. + * @param {IPosition3D} [configuration.position] - The tile position. + **/ + constructor(room: Room, configuration: ITileConfiguration, tileInfo?: ITileInfo) { + super(room); + + /** Store the configuration */ + this.room = room; + this._tileInfo = tileInfo; + this._position = configuration.position; + this._thickness = configuration.thickness ?? 8; + this._material = configuration.material ?? new FloorMaterial(this.room.engine, 111); + + /** Register interactions */ + this.registerInteractions(this._position); + + // TODO: Make the method public and use it when adding it to a room, not when instancing the class + /** Draw the tile */ + this._draw(); + } + + /** + * Draw the tile. + * + * @return {void} + * @private + */ + private _draw(): void { + /** Top face */ + const top = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(1).toNumber(), + //matrix: new Matrix(1, 0.5, 1, -0.5, (this._position.x % 2 === 0 || this._position.y % 2 === 0) && !(this._position.x % 2 === 0 && this._position.y % 2 === 0) ? 32 : 0, (this._position.x % 2 === 0 || this._position.y % 2 === 0) && !(this._position.x % 2 === 0 && this._position.y % 2 === 0) ? 16 : 0) + matrix: new Matrix(1, 0.5, 1, -0.5, this._position.y % 2 === 0 ? 32 : 64, this._position.y % 2 === 0 ? 16 : 0) + }) + .moveTo(0, 0) + .lineTo(32, -16) + .lineTo(64, 0) + .lineTo(32, 16) + .lineTo(0, 0) + .endFill(); + + this.addChild(top); + + let bottomTile; + let rightTile; + + if ( + this.room.tileMap.tileMap[this._position.y + 1] !== undefined && + this.room.tileMap.tileMap[this._position.y + 1][this._position.x] !== undefined + ) { + bottomTile = this.room.tileMap.getTileInfo({ x: this._position.x, y: this._position.y + 1 }); + } + + if (this.room.tileMap.tileMap[this._position.y][this._position.x + 1] !== undefined) { + rightTile = this.room.tileMap.getTileInfo({ x: this._position.x + 1, y: this._position.y }); + } + + if ( + !Boolean(bottomTile) || + Boolean(bottomTile?.stairType) || + !Boolean(bottomTile?.tile) || + (this._tileInfo != null && bottomTile?.height !== this._tileInfo.height) + ) { + /** Left face */ + const left = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.8).toNumber(), + matrix: new Matrix(1, 0.5, 0, 1, 0, 0) + }) + .moveTo(0, 0) + .lineTo(0, this._thickness) + .lineTo(32, 16 + this._thickness) + .lineTo(32, 16) + .endFill(); + + this.addChild(left); + } + + if ( + rightTile == null || + rightTile.stairType != null || + !rightTile.tile || + (this._tileInfo != null && rightTile.height !== this._tileInfo.height) + ) { + /** Right face */ + const right = new Graphics() + .beginTextureFill({ + texture: this._material.texture, + color: new Color(this._material.color).premultiply(0.71).toNumber(), + matrix: new Matrix(1, -0.5, 0, 1, 0, 0) + }) + .moveTo(32, 16) + .lineTo(32, 16 + this._thickness) + .lineTo(64, this._thickness) + .lineTo(64, 0) + .lineTo(32, 16) + .endFill(); + + this.addChild(right); + } + + /** Positionate the wall */ + this.x = 32 * this._position.x - 32 * this._position.y; + this.y = 16 * this._position.x + 16 * this._position.y - 32 * this._position.z; + /** Set the hit area */ + this.hitArea = new Polygon( + new Point(0, 0), + new Point(32, -16), + new Point(64, 0), + new Point(32, 16), + new Point(0, 0) + ); + /** Set the zIndex */ + this.zIndex = ZOrder.floor(this._position); + } + + /** + * Reference to the tile thickness. + * + * @member {number} + * @readonly + * @public + */ + public get thickness(): number { + return this._thickness; + } + + /** + * Update the tile thickness and redraw the tile. + * + * @param {number} [thickness] - The room tile thickness that will be applied. + * @public + */ + public set thickness(thickness: number) { + this._thickness = thickness; + /** Redraw the tile */ + this._draw(); + } + + /** + * Reference to the tile material instance. + * + * @member {Material} + * @readonly + * @public + */ + public get material(): Material { + return this._material; + } + + /** + * Update the tile material and redraw the tile. + * + * @param {Material} [material] - The room tile material that will be applied. + * @public + */ + public set material(material: Material) { + this._material = material; + /** Redraw the tile */ + this._draw(); + } +} diff --git a/src/objects/rooms/parts/Wall.ts b/src/objects/rooms/parts/Wall.ts new file mode 100644 index 0000000..99306c2 --- /dev/null +++ b/src/objects/rooms/parts/Wall.ts @@ -0,0 +1,314 @@ +import { Graphics, Matrix, Texture } from 'pixi.js'; +import { Color } from '@pixi/color'; + +import type { Room } from '../Room'; +import type { IPosition3D, IPosition2D, IWallConfiguration } from '../../../types/Room'; +import type { Material } from '../materials/Material'; +import { WallType } from '../../../enums/WallType'; +import { WallMaterial } from '../materials/WallMaterial'; +import { ZOrder } from '../../../utilities/ZOrder'; +import { RoomPart } from './RoomPart'; + +/** + * Wall class that show up on the sides of the tiles. + * + * @class + * @memberof Scuti + */ +export class Wall extends RoomPart { + /** + * The thickness of the wall part. + * + * @member {number} + * @private + */ + private readonly _thickness: number; + + /** + * The wall material that will be applied to this part, it contains the color and the texture of the wall. + * + * @member {Material} + * @private + */ + private readonly _material: Material; + + /** + * The wall position. + * + * @member {IPosition3D} + * @private + */ + readonly _position: IPosition3D; + + /** + * The wall type. + * + * @member {WallType} + * @private + */ + private readonly _type: WallType; + + /** + * @param {Room} [room] - The room instance where the wall will be drawn. + * @param {IWallConfiguration} [configuration] - The wall configuration. + * @param {Material} [configuration.material] - The wall material that will be applied. + * @param {number} [configuration.thickness] - The wall thickness. + * @param {number} [configuration.height] - The wall height. + * @param {IPosition3D} [configuration.position] - The wall position. + * @param {WallType} [configuration.type] - The wall type. + * @param {boolean} [configuration.door] - Is it a door wall? + **/ + constructor(room: Room, configuration: IWallConfiguration) { + super(room); + + /** Store the configuration */ + this.room = room; + this._position = configuration.position; + this._thickness = configuration.thickness ?? 8; + this._material = configuration.material ?? new WallMaterial(this.room.engine); + this._height = configuration.height ?? 0; + this._type = configuration.type; + + /** Draw the wall */ + this._draw(); + } + + /** + * Select which wall should be drawn by it's type. + * + * @return {void} + * @private + */ + private _draw(): void { + if (this._type === WallType.LEFT_WALL) { + /** Draw a left wall */ + this._drawWall([ + { + x: -this._thickness, + y: -this._thickness / 2 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._height * 64 + }, + { + x: -this._thickness + 32, + y: -this._thickness / 2 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 131 - this._height * 64 + }, + { + x: -this._thickness + 32 + this._thickness, + y: + -this._thickness / 2 + + this._position.z * 32 - + this.room.tileMap.maxZ * 32 - + 131 + + this._thickness / 2 - + this._height * 64 + }, + { + x: -this._thickness + this._thickness, + y: + -this._thickness / 2 + + this._position.z * 32 - + this.room.tileMap.maxZ * 32 - + 115 + + this._thickness / 2 - + this._height * 64 + } + ]); + } else if (this._type === WallType.RIGHT_WALL) { + /** Draw a right wall */ + this._drawWall([ + { + x: 32, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._height * 64 + }, + { + x: 32 + this._thickness, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._thickness / 2 - this._height * 64 + }, + { + x: 64 + this._thickness, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 99 - this._thickness / 2 - this._height * 64 + }, + { + x: 64, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 99 - this._height * 64 + } + ]); + } else if (this._type === WallType.CORNER_WALL) { + /** Draw a corner wall */ + this._drawWall([ + { + x: 32 - this._thickness, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._thickness / 2 - this._height * 64 + }, + { + x: 32, + y: + -16 + + this._position.z * 32 - + this.room.tileMap.maxZ * 32 - + 115 - + 2 * (this._thickness / 2) - + this._height * 64 + }, + { + x: 32 + this._thickness, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._thickness / 2 - this._height * 64 + }, + { + x: 32, + y: -16 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._height * 64 + } + ]); + } else if (this._type === WallType.DOOR_WALL) { + /** Draw a door wall */ + this._drawWall([ + { + x: -this._thickness + 32, + y: -this._thickness / 2 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 99 - this._height * 64 + }, + { + x: -this._thickness + 64, + y: -this._thickness / 2 + this._position.z * 32 - this.room.tileMap.maxZ * 32 - 115 - this._height * 64 + }, + { + x: -this._thickness + 64 + this._thickness, + y: + -this._thickness / 2 + + this._position.z * 32 - + this.room.tileMap.maxZ * 32 - + 115 + + this._thickness / 2 - + this._height * 64 + }, + { + x: -this._thickness + 32 + this._thickness, + y: + -this._thickness / 2 + + this._position.z * 32 - + this.room.tileMap.maxZ * 32 - + 99 + + this._thickness / 2 - + this._height * 64 + } + ]); + } + } + + /** + * Draw the wall using the given points. + * + * @param {IPosition2D[]} [points] - The point list that will be used to draw the wall. + * @return {void} + * @private + */ + private _drawWall(points: IPosition2D[]): void { + /** Top face */ + const top = new Graphics() + .beginTextureFill({ + texture: Texture.WHITE, + color: new Color(this._material.color).premultiply(0.61).toNumber() + }) + .moveTo(points[0].x, points[0].y) + .lineTo(points[1].x, points[1].y) + .lineTo(points[2].x, points[2].y) + .lineTo(points[3].x, points[3].y) + .lineTo(points[0].x, points[0].y) + .endFill(); + + /** Left face */ + const left = new Graphics() + .beginTextureFill({ + texture: this._type === WallType.RIGHT_WALL ? this._material.texture : Texture.WHITE, + color: new Color(this._material.color).premultiply(1).toNumber(), + matrix: new Matrix(1, 0.5, 0, 1, points[0].x, points[0].y) + }) + .moveTo(points[0].x, points[0].y) + .lineTo( + points[0].x, + points[0].y + + 115 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ) + .lineTo( + points[3].x, + points[3].y + + 115 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ) + .lineTo(points[3].x, points[3].y) + .endFill(); + + /** Right face */ + const right = new Graphics() + .beginTextureFill({ + texture: + this._type === WallType.LEFT_WALL || this._type === WallType.DOOR_WALL + ? this._material.texture + : Texture.WHITE, + color: new Color(this._material.color).premultiply(0.8).toNumber(), + matrix: new Matrix(1, -0.5, 0, 1, points[0].x + this._thickness, points[0].y + 4) + }) + .moveTo(points[3].x, points[3].y); + + if (this._type === WallType.DOOR_WALL) { + right + .lineTo( + points[3].x, + points[3].y + + 22 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ) + .lineTo( + points[2].x, + points[2].y + + 22 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ); + } else { + right + .lineTo( + points[3].x, + points[3].y + + 115 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ) + .lineTo( + points[2].x, + points[2].y + + 115 + + this.room.floorThickness + + this.room.tileMap.maxZ * 32 - + this._position.z * 32 + + this._height * 64 + ); + } + + right.lineTo(points[2].x, points[2].y).lineTo(points[3].x, points[3].y).endFill(); + + /** And we combine everything */ + this.addChild(top); + this.addChild(left); + this.addChild(right); + + /** Positionate the wall */ + this.x = 32 * this._position.x - 32 * this._position.y; + this.y = 16 * this._position.x + 16 * this._position.y - 32 * this._position.z; + + /** Set the zIndex */ + this.zIndex = ZOrder.wall(this._position); + } +} diff --git a/src/objects/rooms/parts/index.ts b/src/objects/rooms/parts/index.ts new file mode 100644 index 0000000..c50b9c8 --- /dev/null +++ b/src/objects/rooms/parts/index.ts @@ -0,0 +1,4 @@ +export * from './Cursor'; +export * from './Stair'; +export * from './Tile'; +export * from './Wall'; diff --git a/src/types/Avatar.d.ts b/src/types/Avatar.d.ts new file mode 100644 index 0000000..e9fd291 --- /dev/null +++ b/src/types/Avatar.d.ts @@ -0,0 +1,93 @@ +import type { Direction } from '../enums/Direction'; +import type { AvatarAction } from '../objects/avatars/actions/AvatarAction'; +import type { Dimension } from './Dimension'; +import type { IRoomObjectConfig } from './Room'; + +export type IAvatarPosition = Dimension.IPosition3D; + +export type Figure = Map; + +export interface IAvatarConfig extends IRoomObjectConfig { + figure: string; + bodyDirection: Direction; + headDirection: Direction; + actions: AvatarAction[]; + handItem?: number; +} + +export interface IAvatarPart { + colorable: number; + colorindex: number; + id: number; + index: number; + lib: { id: string; revision: string }; + type: string; +} + +export interface IActionDefinition { + state: string; + precedence: string; + main: string; + geometrytype: string; + activepartset: string; + assetpartdefinition: string; + prevents: string; + params: string[]; +} + +export interface IAnimationDefinition { + desc: string; + frames: Array<{ + bodyparts: object; + }>; +} + +export interface IAnimationFrameData { + assetpartdefinition: string; + repeats: number; + frame: number; +} + +export interface IBodyPartConfiguration { + type: string; + setId: number; + colors: number[]; + parts: IAvatarPart[]; + actions: AvatarAction[]; +} + +export interface IAvatarPartSets { + partSets: object; + activePartSets: { + figure: string[]; + head: string[]; + speak: string[]; + gesture: string[]; + eye: string[]; + handRight: string[]; + handRightAndHead: string[]; + handLeft: string[]; + walk: string[]; + sit: string[]; + itemRight: string[]; + swim: string[]; + }; +} + +export interface IAvatarLayerConfiguration { + type: string; + part: IAvatarPart; + gesture: string; + tint?: number; + z: number; + flip: boolean; + direction: Direction; + frame: number; + alpha?: number; +} + +export interface LayerFrame { + action: AvatarAction; + frame: number; + repeat: number; +} diff --git a/src/types/Configuration.d.ts b/src/types/Configuration.d.ts new file mode 100644 index 0000000..090135c --- /dev/null +++ b/src/types/Configuration.d.ts @@ -0,0 +1,14 @@ +import type { Spritesheet } from 'pixi.js'; + +export interface IRendererConfiguration { + canvas: HTMLElement; + width: number; + height: number; + resources: string; +} + +export interface ScutiSpritesheet extends Omit { + data: { + partsType: { [key: string]: { gestures: string[] } }; + }; +} diff --git a/src/types/Dimension.d.ts b/src/types/Dimension.d.ts new file mode 100644 index 0000000..0151303 --- /dev/null +++ b/src/types/Dimension.d.ts @@ -0,0 +1,15 @@ +export namespace Dimension { + export type IPosition2D = Omit; + export interface IPosition3D { + x: number; + y: number; + z: number; + } + + export type IOffsets2D = Omit; + export interface IOffsets3D { + offsetX: number; + offsetY: number; + offsetZ: number; + } +} diff --git a/src/types/Figure.d.ts b/src/types/Figure.d.ts new file mode 100644 index 0000000..bf6c700 --- /dev/null +++ b/src/types/Figure.d.ts @@ -0,0 +1,52 @@ +export interface IFigureData { + palette: Record>; + settype: Record; +} + +export interface IFigureDataPalette { + id: number; + color: IFigureDataColor[]; +} + +export interface IFigureMap { + libs: Array<{ id: string; revision: string }>; + parts: Record>; +} + +export interface IFigureDataSetType { + type: string; + paletteid: string; + mand_f_0: boolean; // has been changed to boolean, can be either 1, 0 + mand_f_1: boolean; // has been changed to boolean, can be either 1, 0 + mand_m_0: boolean; // has been changed to boolean, can be either 1, 0 + mand_m_1: boolean; // has been changed to boolean, can be either 1, 0 + set: Record; +} + +export interface IFigureDataColor { + id: number; + index: number; + club: number; // must be changed to something, either 0, 1, 2 + selectable: boolean; // has been changed to boolean, can be either 1, 0 + hexCode: string; +} + +export interface IFigureDataSet { + id: number; + gender: 'M' | 'F' | 'U'; // has been changed + club: number; // must be changed to something, either 0, 1, 2 + colorable: boolean; // has been changed to boolean, can be either 1, 0 + selectable: boolean; // has been changed to boolean, can be either 1, 0 + preselectable: boolean; // has been changed to boolean, can be either 1, 0 + sellable?: boolean; // has been changed to boolean, can be either 1, 0, null + parts: IFigureDataPart[]; + hiddenLayers: Array<{ partType: string }>; // !! can be empty +} + +export interface IFigureDataPart { + id: number; + type: string; // must be changed (i guess) + colorable: boolean; // has been changed to boolean, can be either 1, 0 + index: number; + colorindex: number; +} diff --git a/src/types/Furniture.d.ts b/src/types/Furniture.d.ts new file mode 100644 index 0000000..95d37bc --- /dev/null +++ b/src/types/Furniture.d.ts @@ -0,0 +1,100 @@ +import type { BLEND_MODES } from 'pixi.js'; + +import type { Direction } from '../enums/Direction'; +import type { Dimension } from './Dimension'; +import type { IRoomObjectConfig } from './Room'; + +export type IFloorPosition = Dimension.IPosition3D; +export type IWallPosition = Dimension.IPosition2D & Dimension.IOffsets2D; + +export interface IFloorFurnitureConfiguration { + id: number; + position: IFloorPosition; + direction: Direction; + state?: number; +} + +export interface IWallFurniConfig extends IRoomObjectConfig { + id: number; + state?: number; +} + +export interface IFurnitureData { + floorItems: IFloorItem[]; + wallItems: IWallItem[]; +} + +export interface ISharedFurniData { + id: number; + className: string; +} + +export interface IFloorItem extends ISharedFurniData { + name: string; + description: string; + furniLine: string; + offerId: number; + adUrl: string; + excludeDynamic: false; + specialType: number; + customParams: null; +} + +export interface IWallItem extends ISharedFurniData { + name: string; + description: string; + furniLine: string; + offerId: number; + adUrl: string; + excludeDynamic: boolean; + specialType: number; + customParams: string; + dimensions: { + x: number; + y: number; + defaultDirection: number; + }; + canStandOn: boolean; + canSitOn: boolean; + canLayOn: boolean; +} + +export interface IFurnitureProperty { + dimensions: Dimension.IPosition2D; // not sure { x: .., y: .. } + infos: { logic: string; visualization: string }; + visualization: IFurnitureVisualization; +} + +export interface IFurnitureVisualization { + layerCount: number; + directions: Direction[]; + colors: Record>; + layers: Array<{ z: number; ignoreMouse: boolean; tag: string; alpha: number; ink: keyof typeof BLEND_MODES }>; // wrong types here + animation: Record>; +} + +export interface IFurnitureLayerConfiguration { + layer: number | string; + alpha: number; + tint?: number | undefined; + z: number; + blendMode: BLEND_MODES; + flip: boolean; + frame: number; + ignoreMouse: boolean; + direction: Direction; + tag?: string; +} + +export interface IFurnitureLayerData { + layer: number | string; + alpha: number; + tint?: number | undefined; + z: number; + blendMode: BLEND_MODES; + flip: boolean; + frame: number; + ignoreMouse: boolean; + direction: Direction; + tag?: string; +} diff --git a/src/types/Global.d.ts b/src/types/Global.d.ts new file mode 100644 index 0000000..6d89395 --- /dev/null +++ b/src/types/Global.d.ts @@ -0,0 +1 @@ +export type Nullable = T | undefined | null; diff --git a/src/types/Interaction.d.ts b/src/types/Interaction.d.ts new file mode 100644 index 0000000..16a668f --- /dev/null +++ b/src/types/Interaction.d.ts @@ -0,0 +1,10 @@ +import type { FederatedPointerEvent } from 'pixi.js'; + +import type { IAvatarPosition } from './Avatar'; +import type { IFloorPosition, IWallPosition } from './Furniture'; + +export interface IInteractionEvent { + tag?: string; + event: FederatedPointerEvent; + position?: IWallPosition | IFloorPosition | IAvatarPosition; +} diff --git a/src/types/Room.d.ts b/src/types/Room.d.ts new file mode 100644 index 0000000..7c0d79b --- /dev/null +++ b/src/types/Room.d.ts @@ -0,0 +1,108 @@ +import type { StairType } from '../enums/StairType'; +import type { Direction } from '../enums/Direction'; +import type { WallType } from '../enums/WallType'; +import type { Material } from '../objects/rooms/materials/Material'; +import type { IFloorPosition, IWallPosition } from './Furniture'; +import type { IAvatarPosition } from './Avatar'; + +export type TileMap = string[][]; + +export interface IRoomConfig { + tileMap: string; + floorMaterial?: Material; + floorThickness?: number; + wallMaterial?: Material; + wallHeight?: number; + wallThickness?: number; +} + +export interface IRoomObjectConfig { + position: IWallPosition | IFloorPosition | IAvatarPosition; + direction: Direction; +} + +export interface ITileConfiguration { + material?: Material; + thickness?: number; + position: IPosition3D; +} + +export interface IStairConfiguration { + material?: Material; + thickness?: number; + type: StairType; + position: IPosition3D; +} + +export interface IWallConfiguration { + material?: Material; + thickness?: number; + height?: number; + position: IPosition3D; + type: WallType; + door?: boolean; +} + +export interface ICursorConfiguration { + position: IPosition3D; +} + +export interface IPosition3D { + x: number; + y: number; + z: number; + direction?: number; +} + +export interface IPosition2D { + x: number; + y: number; +} + +export interface ITileInfo { + tile: boolean; + door: boolean; + height: number; + stairType?: { type: StairType; direction: Direction }; + wallType?: WallType; +} + +// missing types here +export interface RoomMaterial { + assets: { x: string; y: string; source?: string; flipH?: boolean }; + floorData: { + floors: Array<{ + id: string; + visualizations: Array<{ size: number; layers: Array<{ color: number; materialId: string }> }>; + }>; + }; + materials: Array<{ + id: string; + matrices: Array<{ columns: Array<{ width: number; cells: Array<{ textureId: string }> }> }>; + }>; + textures: Array<{ id: string; bitmaps: Array<{ assetName: string }> }>; + wallData: Array<{ + materials: Array<{ + id: string; + matrices: Array<{ columns: Array<{ width: number; cells: Array<{ textureId: string }> }> }>; + }>; + textures: Array<{ id: string; bitmaps: Array<{ assetName: string }> }>; + walls: Array<{ + id: string; + visualizations: Array<{ size: number; layers: Array<{ color: number; materialId: string }> }>; + }>; + }>; + landscapeData: object; + // ... + visualizationType: string; + type: string; + logicType: string; + spritesheet: string; + name: string; + maskData: { + masks: Array<{ + id: string; + visualizations: Array<{ size: number; layers: Array<{ color: number; materialId: string }> }>; + }>; + }; +} diff --git a/src/types/RoomMaterial.d.ts b/src/types/RoomMaterial.d.ts new file mode 100644 index 0000000..412b3d2 --- /dev/null +++ b/src/types/RoomMaterial.d.ts @@ -0,0 +1,54 @@ +export interface RoomMaterial { + type: string; + name: string; + visualizationType: string; + logicType: string; + spritesheet: string; + assets: Record; + wallData: WallData; + floorData: FloorData; + landscapeData: LandscapeData; + maskData: MaskData; +} + +export interface Assets { + x: number; + y: number; + source?: string; + flipH?: boolean; +} + +export interface FloorDataMaterial { + id: string; + matrices: PurpleMatrix[]; +} + +export interface PurpleMatrix { + columns: PurpleColumn[]; +} + +export interface FloorData { + floors: Floor[]; + materials: FloorDataMaterial[]; + textures: Texture[]; +} + +export interface WallData { + walls: Floor[]; + materials: FloorDataMaterial[]; + textures: Texture[]; +} + +export interface Floor { + id: string; + visualizations: FloorVisualization[]; +} + +export interface Texture { + id: string; + bitmaps: Bitmap[]; +} + +export interface Bitmap { + assetName: string; +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000..0be0568 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,8 @@ +export * from './Avatar'; +export * from './Configuration'; +export * from './Dimension'; +export * from './Figure'; +export * from './Furniture'; +export * from './Global'; +export * from './Interaction'; +export * from './Room'; diff --git a/src/utilities/AssetLoader.ts b/src/utilities/AssetLoader.ts new file mode 100644 index 0000000..06dea87 --- /dev/null +++ b/src/utilities/AssetLoader.ts @@ -0,0 +1,26 @@ +import { Assets, Cache } from 'pixi.js'; + +const domain = 'http://localhost:8081/'; + +interface LoadedKeys { + [key: string]: Promise; +} +const loadedKeys: LoadedKeys = {}; + +const load = async (key: string, url: string, onUncached?: () => void): Promise => { + if (loadedKeys[key] !== undefined) return await loadedKeys[key]; + + if (!Cache.has(key)) { + if (onUncached != null) onUncached(); + Assets.add(key, AssetLoader.domain + url); + loadedKeys[key] = Assets.load(key); + await loadedKeys[key]; + } +}; + +/** + * Loads assets from a certain domain + * + * @memberof Scuti + */ +export const AssetLoader = { domain, load }; diff --git a/src/utilities/Logger.ts b/src/utilities/Logger.ts new file mode 100644 index 0000000..b42f666 --- /dev/null +++ b/src/utilities/Logger.ts @@ -0,0 +1,107 @@ +/** + * Material class that regroup the methods to make beautiful logs. + * + * @class + * @memberof Scuti + */ +export class Logger { + /** + * The logger name (the name shown on the left of the log). + * + * @member {string} + * @private + */ + private readonly _name: string; + + /** + * @param {string} [name] - The logger name. + */ + constructor(name: string) { + this._name = name; + } + + /** + * Send a normal log message into the console. + * + * @param {string} [message] - The message that will be logged into the console. + * @return {void} + * @public + */ + public log(message: string): void { + this._log('#078000', '#FFFFFF', message); + } + + /** + * Send an error log message in the console. + * + * @param {string} [message] - The message that will be logged into the console. + * @return {void} + * @public + */ + public error(message: string): void { + this._log('#E86C5D', '#FFFFFF', message); + } + + /** + * Send a warning log message in the console. + * + * @param {string} [message] - The message that will be logged into the console. + * @return {void} + * @public + */ + public warn(message: string): void { + this._log('#FFD100', '#000000', message); + } + + /** + * Send an info log message in the console. + * + * @param {string} [message] - The message that will be logged into the console. + * @return {void} + * @public + */ + public info(message: string): void { + this._log('#EC3262', '#FFFFFF', message); + } + + /** + * Send a stylized message in the console. + * + * @param {string} [backgroundColor] - The color of the background of the message. + * @param {string} [textColor] - The color of the message. + * @param {string} [message] - The message that will be logged into the console. + * @return {void} + * @public + */ + private _log(backgroundColor: string, textColor: string, message: string): void { + console.log( + `%c ${this.time} %c ${this._name} %c ${message} `, + `background: #FFFFFF; color: #000000;`, + `background: #000000; color: #FFFFFF`, + `background: ${backgroundColor}; color: ${textColor};` + ); + } + + /** + * Reference of the current time of the day formatted in a string. + * + * @member {string} + * @readonly + * @public + */ + public get time(): string { + const date = new Date(); + return String(date.getHours()) + ':' + String(date.getMinutes()) + ':' + String(date.getSeconds()); + } + + /** + * Reference of the logger name. + * + * @member {string} + * @readonly + * @public + */ + public get name(): string { + return this._name; + } +} diff --git a/src/utilities/ZOrder.ts b/src/utilities/ZOrder.ts new file mode 100644 index 0000000..1608611 --- /dev/null +++ b/src/utilities/ZOrder.ts @@ -0,0 +1,102 @@ +import type { IPosition2D, IPosition3D } from '../types/Room'; +import type { IFloorPosition, IWallPosition } from '../types/Furniture'; + +/** + * Priority values + */ +const PRIORITY_WALL = 6; +const PRIORITY_FLOOR = 7; +const PRIORITY_TILE_CURSOR = 11; +const PRIORITY_ROOM_AVATAR = 11; +const PRIORITY_ROOM_ITEM = 11; +const PRIORITY_WALL_ITEM = 9; +const PRIORITY_MULTIPLIER = 10000000; + +/** + * Comparable values + */ +const COMPARABLE_X_Y = 1000000; +const COMPARABLE_Z = 10000; + +/** + * Return the zOrder of an avatar by it's position in the room. + * + * @param {IPosition3D} [position] - The avatar position in the room. + * @return {number} + * @private + */ +const avatar = function (position: IPosition3D, z: number): number { + return ( + (Math.floor(position.x) + Math.floor(position.y)) * COMPARABLE_X_Y + + (position.z + 0.001 * COMPARABLE_Z) + + PRIORITY_MULTIPLIER * PRIORITY_ROOM_AVATAR + + z + ); +}; + +/** + * Return the zOrder of the floor by it's position in the room. + * + * @param {IPosition2D} [position] - The floor position in the room. + * @return {number} + * @private + */ +const floor = function (position: IPosition2D): number { + return (position.x + position.y) * COMPARABLE_X_Y + PRIORITY_MULTIPLIER * PRIORITY_WALL; +}; + +/** + * Return the zOrder of the wall by it's position in the room. + * + * @param {IPosition2D} [position] - The wall position in the room. + * @return {number} + * @private + */ +const wall = function (position: IPosition2D): number { + return (position.x + position.y) * COMPARABLE_X_Y + PRIORITY_MULTIPLIER * PRIORITY_WALL; +}; + +/** + * Return the zOrder of the tile cursor by it's position in the room. + * + * @param {IPosition2D} [position] - The tile cursor position in the room. + * @return {number} + * @private + */ +const tileCursor = function (position: IPosition2D): number { + return (position.x + position.y) * COMPARABLE_X_Y + PRIORITY_MULTIPLIER * PRIORITY_TILE_CURSOR; +}; + +/** + * Return the zOrder of a floor item by it's position in the room. + * + * @param {IFloorPosition} [position] - The floor item position in the room. + * @param {number} [z] - The z value of the layer. + * @return {number} + * @private + */ +const floorItem = function (position: IFloorPosition, z: number): number { + const compareY = Math.trunc(z / 100) / 10; + return ( + (position.x + position.y + compareY) * COMPARABLE_X_Y + + position.z * COMPARABLE_Z + + PRIORITY_MULTIPLIER * PRIORITY_ROOM_ITEM + ); +}; + +/** + * Return the zOrder of a wall item by it's position in the room. + * + * @param {IPosition3D} [position] - The wall item position in the room. + * @param {number} [z] - The z value of the layer. + * @return {number} + * @private + */ +const wallItem = function (position: IWallPosition, z: number): number { + return (position.x + position.y) * COMPARABLE_X_Y + z * COMPARABLE_Z + PRIORITY_MULTIPLIER * PRIORITY_WALL_ITEM; +}; + +/** + * ZOrder variable that manage the z ordering of room objects. + */ +export const ZOrder = { avatar, tileCursor, floorItem, wallItem, floor, wall }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3e050e9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "moduleResolution": "node", + "declaration": true, + "declarationDir": "dist/types", + "noImplicitAny": true, + "allowJs": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "sourceMap": false, + "removeComments": true + }, + "include": ["src", "public"] +}