From ae5943fd3ba8fcba60056cbf53475cf786e4ff4e Mon Sep 17 00:00:00 2001 From: 2767mr <2767mr@gmx.at> Date: Fri, 3 Oct 2025 20:52:34 +0200 Subject: [PATCH] Update eslint --- backend/.eslintrc.json | 33 - backend/eslint.config.mjs | 41 + backend/package-lock.json | 3220 ++++++++-------- backend/package.json | 12 +- backend/prettier.config.mjs | 2 + backend/src/config.ts | 4 +- backend/src/server.ts | 103 +- common/.eslintrc.json | 32 - common/eslint.config.mjs | 41 + common/package-lock.json | 3312 +++++++++-------- common/package.json | 12 +- common/prettier.config.mjs | 2 + common/src/controllers/api.ts | 646 ++-- common/src/controllers/saveFile.ts | 21 +- common/src/main.ts | 2 +- common/src/require.ts | 98 +- webapp/.eslintrc.json | 113 - webapp/angular.json | 13 +- webapp/eslint.config.mjs | 139 + webapp/karma.conf.js | 58 +- webapp/main.js | 39 +- webapp/package-lock.json | 379 +- webapp/package.json | 12 +- webapp/src/app/app-routing.ts | 16 +- webapp/src/app/app.component.ts | 20 +- webapp/src/app/app.module.ts | 22 +- .../components/babylon/babylon.component.ts | 47 +- .../captions/captions.component.html | 6 +- .../components/captions/captions.component.ts | 26 +- .../confirm-close.component.html | 19 +- .../confirm-close/confirm-close.component.ts | 14 +- .../floating-window.component.html | 52 +- .../floating-window.component.ts | 43 +- .../history/history.component.html | 25 +- .../history/history.component.ts | 90 +- .../history/state-history.service.ts | 44 +- .../tile-selector.component.html | 12 +- .../tile-selector/tile-selector.component.ts | 37 +- .../tile-selector/tile-selector.scene.ts | 156 +- .../list-search-overlay.component.html | 46 +- .../list-search-overlay.component.ts | 70 +- .../dialogs/load-map/load-map.component.html | 96 +- .../dialogs/load-map/load-map.component.ts | 134 +- .../dialogs/load-map/virtualMapNode.model.ts | 18 +- .../map-content-settings.component.html | 80 +- .../map-content-settings.component.ts | 29 +- .../map-settings/map-settings.component.html | 5 +- .../map-settings/map-settings.component.ts | 55 +- .../dialogs/new-map/new-map.component.html | 5 +- .../dialogs/new-map/new-map.component.ts | 40 +- .../offset-map/offset-map.component.html | 27 +- .../offset-map/offset-map.component.ts | 33 +- .../overlay-panel.component.html | 24 +- .../overlay-panel/overlay-panel.component.ts | 15 +- .../dialogs/overlay/overlay-ref-control.ts | 10 +- .../dialogs/overlay/overlay.service.ts | 52 +- .../dialogs/settings/settings.component.html | 31 +- .../dialogs/settings/settings.component.ts | 90 +- .../components/editor/editor.component.html | 14 +- .../app/components/editor/editor.component.ts | 39 +- .../entities/entities.component.html | 40 +- .../components/entities/entities.component.ts | 72 +- .../json-editor/json-editor.component.html | 15 +- .../json-editor/json-editor.component.ts | 41 +- .../components/layers/layers.component.html | 146 +- .../app/components/layers/layers.component.ts | 126 +- .../components/phaser/phaser.component.html | 2 +- .../app/components/phaser/phaser.component.ts | 74 +- .../components/sidenav/sidenav.component.html | 5 +- .../components/sidenav/sidenav.component.ts | 28 +- .../split-pane/split-pane.component.html | 1 - .../split-pane/split-pane.component.ts | 45 +- .../grid-menu/grid-menu.component.html | 20 +- .../toolbar/grid-menu/grid-menu.component.ts | 65 +- .../toolbar-divider.component.ts | 5 +- .../components/toolbar/toolbar.component.html | 79 +- .../components/toolbar/toolbar.component.ts | 87 +- .../vec-input/point-input.component.html | 37 +- .../vec-input/point-input.component.ts | 115 +- .../app/components/widgets/abstract-widget.ts | 46 +- .../array-widget/array-widget.component.html | 6 +- .../array-widget/array-widget.component.ts | 55 +- .../boolean-widget.component.html | 17 +- .../boolean-widget.component.ts | 5 +- .../character-widget.component.ts | 138 +- .../custom-des-type-widget.component.ts | 109 +- .../enemy-single-type-widget.component.ts | 121 +- .../enemy-type-overlay.component.html | 82 +- .../enemy-type-overlay.component.ts | 35 +- .../enemy-type-widget.component.html | 22 +- .../enemy-type-widget.component.ts | 15 +- .../event-editor/add/add-event.service.ts | 83 +- .../detail/event-detail.component.html | 3 +- .../detail/event-detail.component.ts | 98 +- .../editor/event-editor.component.html | 27 +- .../editor/event-editor.component.ts | 324 +- .../event-editor/editor/event-history.ts | 77 +- .../event-editor/event-display.model.ts | 14 +- .../event-editor/event-helper.service.ts | 69 +- .../row-text/row-text.component.html | 5 +- .../row-text/row-text.component.ts | 20 +- .../event-registry/abstract-event.ts | 80 +- .../event-registry/add-msg-person.ts | 32 +- .../event-registry/clear-screen-blur.ts | 10 +- .../event-registry/clear-slow-motion.ts | 16 +- .../event-registry/default-event.ts | 48 +- .../event-widget/event-registry/do-action.ts | 19 +- .../event-registry/event-registry.service.ts | 21 +- .../event-widget/event-registry/goto-label.ts | 16 +- .../widgets/event-widget/event-registry/if.ts | 26 +- .../event-widget/event-registry/label.ts | 16 +- .../event-registry/open-quest-dialogue.ts | 39 +- .../event-registry/set-camera-between.ts | 30 +- .../event-registry/set-camera-pos.ts | 30 +- .../event-registry/set-camera-target.ts | 37 +- .../event-registry/set-camera-zoom.ts | 16 +- .../event-registry/set-msg-expression.ts | 21 +- .../event-registry/set-overlay.ts | 22 +- .../event-registry/set-player-core.ts | 18 +- .../event-registry/show-choice.ts | 49 +- .../event-registry/show-modal-choice.ts | 103 +- .../event-widget/event-registry/show-msg.ts | 26 +- .../event-registry/show-side-msg.ts | 14 +- .../event-registry/start-npc-trade-menu.ts | 27 +- .../event-widget/event-registry/wait.ts | 14 +- .../event-widget/event-widget.component.html | 21 +- .../event-widget/event-widget.component.ts | 51 +- .../event-window/event-window.component.html | 2 +- .../event-window/event-window.component.ts | 30 +- .../input-with-button.component.html | 19 +- .../input-with-button.component.ts | 2 +- .../json-widget/json-widget.component.html | 14 +- .../json-widget/json-widget.component.ts | 21 +- .../langlabel-widget.component.html | 32 +- .../langlabel-widget.component.ts | 17 +- .../level-widget/level-widget.component.html | 22 +- .../level-widget/level-widget.component.ts | 34 +- .../npc-states-widget.component.html | 15 +- .../npc-states-widget.component.ts | 53 +- .../npc-states/npc-states.component.html | 264 +- .../npc-states/npc-states.component.ts | 108 +- .../number-widget.component.html | 17 +- .../number-widget/number-widget.component.ts | 8 +- .../app/components/widgets/overlay-widget.ts | 59 +- .../custom-expression-widget.component.html | 28 +- .../custom-expression-widget.component.ts | 150 +- .../expression-renderer-entity.ts | 16 +- .../person-expression-widget.component.ts | 12 +- .../prop-type-widget.component.html | 17 +- .../prop-type-widget.component.ts | 197 +- ...scalable-prop-config-widget.component.html | 17 +- .../scalable-prop-config-widget.component.ts | 171 +- .../image-select-card.component.html | 2 +- .../image-select-card.component.ts | 6 +- .../image-select-list.component.html | 28 +- .../image-select-list.component.ts | 21 +- .../image-select-list/list-filter.pipe.ts | 16 +- .../image-select-overlay.component.html | 46 +- .../image-select-overlay.component.ts | 43 +- .../autocompleted-textbox.component.html | 14 +- .../autocompleted-textbox.component.ts | 42 +- .../string-widget.component.html | 38 +- .../string-widget/string-widget.component.ts | 15 +- .../vec2-widget/vec2-widget.component.html | 46 +- .../vec2-widget/vec2-widget.component.ts | 24 +- .../widgets/widget-registry.service.ts | 16 +- .../src/app/directives/autofocus.directive.ts | 25 +- .../app/directives/colored-text.directive.ts | 15 +- .../src/app/directives/highlight.directive.ts | 33 +- webapp/src/app/directives/host.directive.ts | 8 +- webapp/src/app/directives/modal.directive.ts | 6 +- .../src/app/directives/resized.directive.ts | 32 +- .../app/external-modules/material.module.ts | 9 +- webapp/src/app/models/cross-code-map.ts | 4 +- webapp/src/app/models/enemy.ts | 82 +- webapp/src/app/models/events.ts | 76 +- webapp/src/app/models/file-infos.ts | 4 +- webapp/src/app/models/map-styles.ts | 6 +- webapp/src/app/models/multi-dir-animation.ts | 14 +- .../src/app/models/multi-entity-animation.ts | 72 +- webapp/src/app/models/single-dir-animation.ts | 22 +- webapp/src/app/models/tile-sheet.ts | 12 +- webapp/src/app/pipes/combined-tooltip.pipe.ts | 6 +- webapp/src/app/pipes/keep-html.pipe.ts | 11 +- .../app/services/3d/babylon-viewer.service.ts | 158 +- .../services/3d/camera/custom-free-camera.ts | 3 - .../app/services/3d/camera/wasd-cam-input.ts | 47 +- webapp/src/app/services/3d/debug/show-axis.ts | 90 +- .../src/app/services/3d/debug/toggle-mesh.ts | 18 +- .../services/3d/entities/entity-generator.ts | 373 +- .../services/3d/entities/entity-manager-3d.ts | 28 +- .../boundary-tracing/boundary-tracer.spec.ts | 1196 +++--- .../boundary-tracing/node-grid.ts | 169 +- .../layer-mesh-generator.spec.ts | 127 +- .../layer-generation/layer-mesh-generator.ts | 185 +- .../3d/layer-generation/offset-helper.ts | 11 +- .../layer-generation/side-mesh-generator.ts | 118 +- .../3d/layer-generation/simple-tile-layer.ts | 67 +- .../3d/layer-generation/texture-generator.ts | 74 +- webapp/src/app/services/3d/ui/wireframe.ts | 2 +- .../app/services/add-entity-menu.service.ts | 78 +- .../services/autotile/autotile.constants.ts | 215 +- .../app/services/autotile/autotile.service.ts | 168 +- .../src/app/services/autotile/gfx-mapper.ts | 140 +- webapp/src/app/services/browser.service.ts | 18 +- webapp/src/app/services/color.service.ts | 4 +- webapp/src/app/services/electron.service.ts | 38 +- .../src/app/services/global-events.service.ts | 8 +- webapp/src/app/services/globals.ts | 8 +- .../gfx-mapper/gfx-mapper.constants.ts | 403 +- .../height-map/gfx-mapper/gfx-mapper.ts | 146 +- .../services/height-map/height-map.service.ts | 622 +++- .../height-map/heightmap.constants.ts | 197 +- .../src/app/services/http-client.service.ts | 82 +- .../src/app/services/json-loader.service.ts | 46 +- webapp/src/app/services/map-loader.service.ts | 46 +- webapp/src/app/services/path-resolver.ts | 11 +- .../src/app/services/phaser/BaseTileDrawer.ts | 268 +- webapp/src/app/services/phaser/base-object.ts | 43 +- .../app/services/phaser/coords-reporter.ts | 37 +- .../app/services/phaser/entities/cc-entity.ts | 577 +-- .../phaser/entities/entity-manager.ts | 323 +- .../entities/registry/default-entity.ts | 75 +- .../phaser/entities/registry/destructible.ts | 58 +- .../phaser/entities/registry/enemy.ts | 228 +- .../registry/entity-registry.service.ts | 12 +- .../phaser/entities/registry/event-trigger.ts | 36 +- .../phaser/entities/registry/item-destruct.ts | 54 +- .../phaser/entities/registry/npc-templates.ts | 348 +- .../services/phaser/entities/registry/npc.ts | 149 +- .../services/phaser/entities/registry/prop.ts | 108 +- .../phaser/entities/registry/scalable-prop.ts | 60 +- .../services/phaser/entities/selection-box.ts | 32 +- webapp/src/app/services/phaser/entity-grid.ts | 28 +- .../app/services/phaser/global-settings.d.ts | 17 +- webapp/src/app/services/phaser/helper.ts | 142 +- .../src/app/services/phaser/ingame-preview.ts | 25 +- .../src/app/services/phaser/layer-parallax.ts | 35 +- webapp/src/app/services/phaser/main-scene.ts | 65 +- webapp/src/app/services/phaser/map-pan.ts | 81 +- .../services/phaser/phaser-events.service.ts | 3 +- .../src/app/services/phaser/sheet-parser.ts | 115 +- .../services/phaser/tilemap/cc-map-layer.ts | 116 +- .../src/app/services/phaser/tilemap/cc-map.ts | 173 +- .../src/app/services/phaser/tilemap/fill.ts | 16 +- .../services/phaser/tilemap/layer-helper.ts | 26 +- .../services/phaser/tilemap/points-in-line.ts | 8 +- .../services/phaser/tilemap/tile-drawer.ts | 180 +- webapp/src/app/services/phaser/vec2.ts | 104 +- webapp/src/app/services/save.service.ts | 69 +- .../src/app/services/search-filter.service.ts | 29 +- webapp/src/app/services/settings.service.ts | 28 +- webapp/src/app/services/shared-service.ts | 8 +- webapp/src/app/tests/entities.spec.ts | 86 +- webapp/src/app/tests/layers.spec.ts | 63 +- webapp/src/app/tests/map-loading.spec.ts | 73 +- webapp/src/app/tests/test-helper.ts | 36 +- webapp/src/environments/environment.prod.ts | 4 +- webapp/src/environments/environment.ts | 4 +- webapp/src/main.ts | 11 +- webapp/src/polyfills.ts | 45 - webapp/src/test.ts | 4 +- webapp/src/typings.d.ts | 2 - webapp/tailwind.config.js | 15 +- 264 files changed, 13740 insertions(+), 10346 deletions(-) delete mode 100644 backend/.eslintrc.json create mode 100644 backend/eslint.config.mjs create mode 100644 backend/prettier.config.mjs delete mode 100644 common/.eslintrc.json create mode 100644 common/eslint.config.mjs create mode 100644 common/prettier.config.mjs delete mode 100644 webapp/.eslintrc.json create mode 100644 webapp/eslint.config.mjs delete mode 100644 webapp/src/polyfills.ts diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json deleted file mode 100644 index 0beb0161..00000000 --- a/backend/.eslintrc.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "env": { - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "prefer-const": "warn", - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ] - } -} diff --git a/backend/eslint.config.mjs b/backend/eslint.config.mjs new file mode 100644 index 00000000..44ce5fd8 --- /dev/null +++ b/backend/eslint.config.mjs @@ -0,0 +1,41 @@ +// @ts-check + +import eslint from '@eslint/js'; +import { defineConfig } from 'eslint/config'; +import tseslint from 'typescript-eslint'; +import globals from 'globals'; +import stylistic from '@stylistic/eslint-plugin'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; + +export default defineConfig( + { + languageOptions: { + globals: globals['shared-node-browser'], + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylistic, + ], + plugins: { + '@stylistic': stylistic, + }, + rules: { + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ] + }, + }, + eslintPluginPrettierRecommended, +); diff --git a/backend/package-lock.json b/backend/package-lock.json index 927851d3..7412efe5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -16,17 +16,21 @@ "morgan": "^1.10.0" }, "devDependencies": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/body-parser": "^1.19.2", "@types/cors": "^2.8.13", "@types/errorhandler": "1.5.0", "@types/express": "^4.17.17", "@types/morgan": "^1.9.4", "@types/node": "^18.16.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint": "^8.39.0", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", "nodemon": "^2.0.22", - "typescript": "~4.9.4" + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" } }, "../common": { @@ -34,11 +38,15 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.9.0", - "@typescript-eslint/parser": "^4.9.0", - "eslint": "^7.14.0", - "typescript": "^3.9.7" + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" } }, "../common/node_modules/@babel/code-frame": { @@ -91,151 +99,18 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "../common/node_modules/@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "../common/node_modules/@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "../common/node_modules/@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, "../common/node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "../common/node_modules/@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, "../common/node_modules/@types/node": { "version": "14.6.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==", "dev": true }, - "../common/node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.1.tgz", - "integrity": "sha512-pQZtXupCn11O4AwpYVUX4PDFfmIJl90ZgrEBg0CEcqlwvPiG0uY81fimr1oMFblZnpKAq6prrT9a59pj1x58rw==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.0.1", - "@typescript-eslint/scope-manager": "4.0.1", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - } - }, - "../common/node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - } - }, - "../common/node_modules/@typescript-eslint/experimental-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.1.tgz", - "integrity": "sha512-gAqOjLiHoED79iYTt3F4uSHrYmg/GPz/zGezdB0jAdr6S6gwNiR/j7cTZ8nREKVzMVKLd9G3xbg1sV9GClW3sw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.0.1", - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/typescript-estree": "4.0.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "../common/node_modules/@typescript-eslint/parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.0.1.tgz", - "integrity": "sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "4.0.1", - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/typescript-estree": "4.0.1", - "debug": "^4.1.1" - } - }, - "../common/node_modules/@typescript-eslint/scope-manager": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.0.1.tgz", - "integrity": "sha512-u3YEXVJ8jsj7QCJk3om0Y457fy2euEOkkzxIB/LKU3MdyI+FJ2gI0M4aKEaXzwCSfNDiZ13a3lDo5DVozc+XLQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/visitor-keys": "4.0.1" - } - }, - "../common/node_modules/@typescript-eslint/types": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.0.1.tgz", - "integrity": "sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg==", - "dev": true - }, - "../common/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.1.tgz", - "integrity": "sha512-zGzleORFXrRWRJAMLTB2iJD1IZbCPkg4hsI8mGdpYlKaqzvKYSEWVAYh14eauaR+qIoZVWrXgYSXqLtTlxotiw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/visitor-keys": "4.0.1", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - } - }, - "../common/node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - } - }, - "../common/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz", - "integrity": "sha512-yBSqd6FjnTzbg5RUy9J+9kJEyQjTI34JdGMJz+9ttlJzLCnGkBikxw+N5n2VDcc3CesbIEJ0MnZc5uRYnrEnCw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.0.1", - "eslint-visitor-keys": "^2.0.0" - } - }, "../common/node_modules/acorn": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", @@ -284,12 +159,6 @@ "sprintf-js": "~1.0.2" } }, - "../common/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "../common/node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -312,15 +181,6 @@ "concat-map": "0.0.1" } }, - "../common/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - } - }, "../common/node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -385,15 +245,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "../common/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - } - }, "../common/node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -488,12 +339,6 @@ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, - "../common/node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, "../common/node_modules/eslint/node_modules/ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -636,20 +481,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "../common/node_modules/fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, "../common/node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -662,15 +493,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "../common/node_modules/fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, "../common/node_modules/file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -680,15 +502,6 @@ "flat-cache": "^2.0.1" } }, - "../common/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - } - }, "../common/node_modules/flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -756,26 +569,6 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, - "../common/node_modules/globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "../common/node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, "../common/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -841,12 +634,6 @@ "is-extglob": "^2.1.1" } }, - "../common/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "../common/node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -897,22 +684,6 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, - "../common/node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "../common/node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, "../common/node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -993,18 +764,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "../common/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "../common/node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, "../common/node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1035,12 +794,6 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "../common/node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, "../common/node_modules/rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -1050,12 +803,6 @@ "glob": "^7.1.3" } }, - "../common/node_modules/run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, "../common/node_modules/semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", @@ -1077,12 +824,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "../common/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "../common/node_modules/slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -1165,21 +906,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "../common/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - } - }, - "../common/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, "../common/node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1241,39 +967,112 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1281,19 +1080,19 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1304,58 +1103,80 @@ } } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/@eslint/js": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", - "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" }, "engines": { - "node": ">=10.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1369,11 +1190,18 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1410,25 +1238,94 @@ "node": ">= 8" } }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" } }, - "node_modules/@types/connect": { - "version": "3.4.33", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", - "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "node_modules/@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", "dev": true, "dependencies": { - "@types/node": "*" - } - }, + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cors": { "version": "2.8.13", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", @@ -1447,6 +1344,12 @@ "@types/express": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", @@ -1471,9 +1374,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/mime": { @@ -1509,12 +1412,6 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, "node_modules/@types/serve-static": { "version": "1.13.8", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", @@ -1525,97 +1422,47 @@ "@types/node": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz", - "integrity": "sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/type-utils": "5.59.0", - "@typescript-eslint/utils": "5.59.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.0.tgz", - "integrity": "sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==", + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/typescript-estree": "5.59.0", - "debug": "^4.3.4" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@typescript-eslint/project-service/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1626,126 +1473,142 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/@typescript-eslint/project-service/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz", - "integrity": "sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/visitor-keys": "5.59.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz", - "integrity": "sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==", + "node_modules/@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.0", - "@typescript-eslint/utils": "5.59.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/types": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.0.tgz", - "integrity": "sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz", - "integrity": "sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/visitor-keys": "5.59.0", + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "dev": true, "dependencies": { - "ms": "2.1.2" + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1756,55 +1619,39 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.0.tgz", - "integrity": "sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==", + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/typescript-estree": "5.59.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz", - "integrity": "sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==", + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.0", - "eslint-visitor-keys": "^3.3.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1824,9 +1671,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1860,15 +1707,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1908,15 +1746,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1988,12 +1817,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2196,9 +2025,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -2240,30 +2069,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2307,127 +2112,153 @@ } }, "node_modules/eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", - "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.39.0", - "@humanwhocodes/config-array": "^0.11.8", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "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.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "url": "https://eslint.org/donate" }, - "engines": { - "node": ">=8.0.0" + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" }, "engines": { - "node": ">=6.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { "optional": true } } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">=4.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ms": { @@ -2437,17 +2268,29 @@ "dev": true }, "node_modules/espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2465,15 +2308,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -2486,7 +2320,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -2495,15 +2329,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2635,17 +2460,23 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -2685,21 +2516,21 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -2753,22 +2584,22 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "node_modules/forwarded": { @@ -2787,12 +2618,6 @@ "node": ">= 0.6" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -2825,26 +2650,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2858,44 +2663,21 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/has": { @@ -2971,9 +2753,9 @@ "dev": true }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { "parent-module": "^1.0.0", @@ -2995,16 +2777,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3060,31 +2832,12 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3097,6 +2850,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3109,6 +2868,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3143,18 +2911,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3186,12 +2942,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3266,12 +3022,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3391,19 +3141,10 @@ "node": ">= 0.8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -3411,7 +3152,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -3476,15 +3217,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -3499,15 +3231,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3529,6 +3252,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -3548,9 +3299,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -3643,21 +3394,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -3692,13 +3428,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3819,15 +3552,6 @@ "semver": "bin/semver.js" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3836,18 +3560,6 @@ "node": ">= 0.8" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3872,11 +3584,20 @@ "node": ">=4" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -3910,25 +3631,16 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, "engines": { - "node": ">= 6" + "node": ">=18.12" }, "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" + "typescript": ">=4.8.4" } }, "node_modules/type-check": { @@ -3943,18 +3655,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3968,18 +3668,261 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.45.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "dependencies": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript-eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -4035,26 +3978,14 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -4070,98 +4001,159 @@ }, "dependencies": { "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, "@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true }, - "@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, - "@eslint/js": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", - "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", + "@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@types/json-schema": "^7.0.15" + } + }, + "@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, + "@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true + }, + "@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "requires": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + } + }, + "@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true + }, + "@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "requires": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + } + }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true }, "@nodelib/fs.scandir": { @@ -4190,6 +4182,46 @@ "fastq": "^1.6.0" } }, + "@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true + }, + "@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true + } + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -4227,6 +4259,12 @@ "@types/express": "*" } }, + "@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, "@types/express": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", @@ -4251,9 +4289,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/mime": { @@ -4289,12 +4327,6 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, "@types/serve-static": { "version": "1.13.8", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", @@ -4305,173 +4337,144 @@ "@types/node": "*" } }, - "@typescript-eslint/eslint-plugin": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz", - "integrity": "sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==", + "@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/type-utils": "5.59.0", - "@typescript-eslint/utils": "5.59.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" }, "dependencies": { + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, - "@typescript-eslint/parser": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.0.tgz", - "integrity": "sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==", + "@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/typescript-estree": "5.59.0", - "debug": "^4.3.4" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "dev": true, "requires": { - "ms": "2.1.2" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz", - "integrity": "sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/visitor-keys": "5.59.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz", - "integrity": "sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.59.0", - "@typescript-eslint/utils": "5.59.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "dependencies": { + }, + "@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true - } - } - }, - "@typescript-eslint/types": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.0.tgz", - "integrity": "sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz", - "integrity": "sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/visitor-keys": "5.59.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "requires": { - "ms": "2.1.2" + "brace-expansion": "^2.0.1" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, - "@typescript-eslint/utils": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.0.tgz", - "integrity": "sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.0", - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/typescript-estree": "5.59.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz", - "integrity": "sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.0", - "eslint-visitor-keys": "^3.3.0" - } - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4488,9 +4491,9 @@ } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true }, "acorn-jsx": { @@ -4512,12 +4515,6 @@ "uri-js": "^4.2.2" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4548,12 +4545,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4614,12 +4605,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "bytes": { @@ -4645,11 +4636,15 @@ "cc-map-editor-common": { "version": "file:../common", "requires": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.9.0", - "@typescript-eslint/parser": "^4.9.0", - "eslint": "^7.14.0", - "typescript": "^3.9.7" + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" }, "dependencies": { "@babel/code-frame": { @@ -4704,152 +4699,17 @@ } } }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, "@types/node": { "version": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==", "dev": true }, - "@typescript-eslint/eslint-plugin": { - "version": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.1.tgz", - "integrity": "sha512-pQZtXupCn11O4AwpYVUX4PDFfmIJl90ZgrEBg0CEcqlwvPiG0uY81fimr1oMFblZnpKAq6prrT9a59pj1x58rw==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.0.1", - "@typescript-eslint/scope-manager": "4.0.1", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.1.tgz", - "integrity": "sha512-gAqOjLiHoED79iYTt3F4uSHrYmg/GPz/zGezdB0jAdr6S6gwNiR/j7cTZ8nREKVzMVKLd9G3xbg1sV9GClW3sw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.0.1", - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/typescript-estree": "4.0.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.0.1.tgz", - "integrity": "sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.0.1", - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/typescript-estree": "4.0.1", - "debug": "^4.1.1" - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.0.1.tgz", - "integrity": "sha512-u3YEXVJ8jsj7QCJk3om0Y457fy2euEOkkzxIB/LKU3MdyI+FJ2gI0M4aKEaXzwCSfNDiZ13a3lDo5DVozc+XLQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/visitor-keys": "4.0.1" - } - }, - "@typescript-eslint/types": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.0.1.tgz", - "integrity": "sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.1.tgz", - "integrity": "sha512-zGzleORFXrRWRJAMLTB2iJD1IZbCPkg4hsI8mGdpYlKaqzvKYSEWVAYh14eauaR+qIoZVWrXgYSXqLtTlxotiw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.0.1", - "@typescript-eslint/visitor-keys": "4.0.1", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz", - "integrity": "sha512-yBSqd6FjnTzbg5RUy9J+9kJEyQjTI34JdGMJz+9ttlJzLCnGkBikxw+N5n2VDcc3CesbIEJ0MnZc5uRYnrEnCw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.0.1", - "eslint-visitor-keys": "^2.0.0" - } - }, "acorn": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", @@ -4898,12 +4758,6 @@ "sprintf-js": "~1.0.2" } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -4926,15 +4780,6 @@ "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4999,15 +4844,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -5176,12 +5012,6 @@ } } }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, "espree": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", @@ -5259,20 +5089,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5285,15 +5101,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -5303,15 +5110,6 @@ "flat-cache": "^2.0.1" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -5381,28 +5179,6 @@ } } }, - "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -5468,12 +5244,6 @@ "is-extglob": "^2.1.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5524,22 +5294,6 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -5620,18 +5374,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5662,12 +5404,6 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -5677,12 +5413,6 @@ "glob": "^7.1.3" } }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", @@ -5704,12 +5434,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -5794,21 +5518,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5819,8 +5528,7 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "version": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, @@ -5986,9 +5694,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -6013,30 +5721,12 @@ "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "ee-first": { "version": "1.1.1", @@ -6069,51 +5759,46 @@ "dev": true }, "eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", - "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.39.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "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.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "dependencies": { "debug": { @@ -6125,20 +5810,10 @@ "ms": "2.1.2" } }, - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true }, "ms": { @@ -6149,31 +5824,50 @@ } } }, + "eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + } + }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, - "eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", - "dev": true - }, "espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" + "eslint-visitor-keys": "^4.2.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + } } }, "esquery": { @@ -6183,14 +5877,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -6200,20 +5886,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -6316,17 +5994,23 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "dependencies": { "glob-parent": { @@ -6362,18 +6046,18 @@ } }, "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -6414,19 +6098,19 @@ } }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "forwarded": { @@ -6439,12 +6123,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -6467,20 +6145,6 @@ "has-symbols": "^1.0.3" } }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6491,32 +6155,15 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "has": { @@ -6571,9 +6218,9 @@ "dev": true }, "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -6586,16 +6233,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6636,24 +6273,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -6663,6 +6288,12 @@ "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6675,6 +6306,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6700,15 +6340,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6731,12 +6362,12 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -6790,12 +6421,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -6880,19 +6505,10 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -6900,7 +6516,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" } }, "p-limit": { @@ -6941,12 +6557,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -6958,12 +6568,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -6976,6 +6580,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "peer": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6992,9 +6612,9 @@ "dev": true }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -7048,15 +6668,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7077,13 +6688,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true }, "send": { "version": "0.18.0", @@ -7178,26 +6786,11 @@ } } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7213,11 +6806,14 @@ "has-flag": "^3.0.0" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "requires": { + "@pkgr/core": "^0.2.9" + } }, "to-regex-range": { "version": "5.0.1", @@ -7242,20 +6838,12 @@ "nopt": "~1.0.10" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "requires": { - "tslib": "^1.8.1" - } + "requires": {} }, "type-check": { "version": "0.4.0", @@ -7266,12 +6854,6 @@ "prelude-ls": "^1.2.1" } }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -7282,11 +6864,157 @@ } }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, + "typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7327,21 +7055,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "yocto-queue": { diff --git a/backend/package.json b/backend/package.json index c130aa68..38a69aa4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -21,16 +21,20 @@ "morgan": "^1.10.0" }, "devDependencies": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/body-parser": "^1.19.2", "@types/cors": "^2.8.13", "@types/errorhandler": "1.5.0", "@types/express": "^4.17.17", "@types/morgan": "^1.9.4", "@types/node": "^18.16.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint": "^8.39.0", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", "nodemon": "^2.0.22", - "typescript": "~4.9.4" + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" } } diff --git a/backend/prettier.config.mjs b/backend/prettier.config.mjs new file mode 100644 index 00000000..b4063d36 --- /dev/null +++ b/backend/prettier.config.mjs @@ -0,0 +1,2 @@ +export const editorconfig = true; +export const singleQuote = true; diff --git a/backend/src/config.ts b/backend/src/config.ts index 60956262..589f8349 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -1,4 +1,4 @@ export const config = { - pathToCrosscode: 'C:/Program Files (x86)/Steam/steamapps/common/CrossCode/assets/' + pathToCrosscode: + "C:/Program Files (x86)/Steam/steamapps/common/CrossCode/assets/", }; - diff --git a/backend/src/server.ts b/backend/src/server.ts index ff871986..59e6bec2 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,55 +1,78 @@ -import bodyParser from 'body-parser'; -import { api } from 'cc-map-editor-common'; -import cors from 'cors'; -import errorHandler from 'errorhandler'; -import express from 'express'; -import logger from 'morgan'; -import { config } from './config.js'; +import bodyParser from "body-parser"; +import { api } from "cc-map-editor-common"; +import cors from "cors"; +import errorHandler from "errorhandler"; +import express from "express"; +import logger from "morgan"; +import { config } from "./config.js"; export const app = express(); /** * Express configuration. */ -app.set('port', process.env['PORT'] || 8080); +app.set("port", process.env["PORT"] || 8080); app.use(cors()); app.use(express.static(config.pathToCrosscode, { maxAge: 0 })); // app.use(compression()); -app.use(logger('dev')); -app.use(express.json({ limit: '20mb' })); +app.use(logger("dev")); +app.use(express.json({ limit: "20mb" })); app.use(bodyParser.urlencoded({ extended: true })); /** * Primary app routes. */ -app.get('/api/allFiles', async (_, res) => res.json(await api.getAllFiles(config.pathToCrosscode))); -app.get('/api/allTilesets', async (_, res) => res.json(await api.getAllTilesets(config.pathToCrosscode))); -app.get('/api/allMaps', async (req, res) => res.json(await api.getAllMaps(config.pathToCrosscode, req.query['includeVanillaMaps'] == 'true'))); -app.get('/api/allFilesInFolder', async (req, res) => res.json(await api.getAllFilesInFolder(config.pathToCrosscode, req.query['folder'] as string, req.query['extension'] as string))); -app.get('/api/allMods', async (_, res) => res.json(await api.getAllMods(config.pathToCrosscode))); -app.get('/api/allModMapEditorConfigs', async (_, res) => res.json(await api.getAllModMapEditorConfigs(config.pathToCrosscode))); -app.post('/api/get', async (req, res) => { - res.json(await api.get(config.pathToCrosscode, req.body.path)); +app.get("/api/allFiles", async (_, res) => + res.json(await api.getAllFiles(config.pathToCrosscode)), +); +app.get("/api/allTilesets", async (_, res) => + res.json(await api.getAllTilesets(config.pathToCrosscode)), +); +app.get("/api/allMaps", async (req, res) => + res.json( + await api.getAllMaps( + config.pathToCrosscode, + req.query["includeVanillaMaps"] == "true", + ), + ), +); +app.get("/api/allFilesInFolder", async (req, res) => + res.json( + await api.getAllFilesInFolder( + config.pathToCrosscode, + req.query["folder"] as string, + req.query["extension"] as string, + ), + ), +); +app.get("/api/allMods", async (_, res) => + res.json(await api.getAllMods(config.pathToCrosscode)), +); +app.get("/api/allModMapEditorConfigs", async (_, res) => + res.json(await api.getAllModMapEditorConfigs(config.pathToCrosscode)), +); +app.post("/api/get", async (req, res) => { + res.json(await api.get(config.pathToCrosscode, req.body.path)); }); -app.post('/api/resolve', async (req, res) => { - try { - res.json(await api.resolve(config.pathToCrosscode, req.body.path)); - } catch (err) { - res.status(404).json(err); - } +app.post("/api/resolve", async (req, res) => { + try { + res.json(await api.resolve(config.pathToCrosscode, req.body.path)); + } catch (err) { + res.status(404).json(err); + } }); -app.post('/api/select', async (req, res) => { - await api.selectedMod(config.pathToCrosscode, req.body.mod); - res.json({ success: true }); +app.post("/api/select", async (req, res) => { + await api.selectedMod(config.pathToCrosscode, req.body.mod); + res.json({ success: true }); }); -app.post('/api/saveFile', async (req, res) => { - try { - const msg = await api.saveFile(config.pathToCrosscode, req.body); - res.status(200).json(msg); - } catch (e) { - console.error(e); - res.status(400).json({ error: e }); - } +app.post("/api/saveFile", async (req, res) => { + try { + const msg = await api.saveFile(config.pathToCrosscode, req.body); + res.status(200).json(msg); + } catch (e) { + console.error(e); + res.status(400).json({ error: e }); + } }); /** @@ -60,7 +83,11 @@ app.use(errorHandler()); /** * Start Express server. */ -app.listen(app.get('port'), () => { - console.log((' App is running at http://localhost:%d in %s mode'), app.get('port'), app.get('env')); - console.log(' Press CTRL-C to stop\n'); +app.listen(app.get("port"), () => { + console.log( + " App is running at http://localhost:%d in %s mode", + app.get("port"), + app.get("env"), + ); + console.log(" Press CTRL-C to stop\n"); }); diff --git a/common/.eslintrc.json b/common/.eslintrc.json deleted file mode 100644 index 06be615f..00000000 --- a/common/.eslintrc.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "env": { - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ] - } -} diff --git a/common/eslint.config.mjs b/common/eslint.config.mjs new file mode 100644 index 00000000..44ce5fd8 --- /dev/null +++ b/common/eslint.config.mjs @@ -0,0 +1,41 @@ +// @ts-check + +import eslint from '@eslint/js'; +import { defineConfig } from 'eslint/config'; +import tseslint from 'typescript-eslint'; +import globals from 'globals'; +import stylistic from '@stylistic/eslint-plugin'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; + +export default defineConfig( + { + languageOptions: { + globals: globals['shared-node-browser'], + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylistic, + ], + plugins: { + '@stylistic': stylistic, + }, + rules: { + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ] + }, + }, + eslintPluginPrettierRecommended, +); diff --git a/common/package-lock.json b/common/package-lock.json index e705b9cd..fdba6141 100644 --- a/common/package-lock.json +++ b/common/package-lock.json @@ -9,90 +9,206 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.9.0", - "@typescript-eslint/parser": "^4.9.0", - "eslint": "^7.14.0", - "typescript": "^3.9.7" + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "dependencies": { - "@babel/highlight": "^7.10.4" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=0.8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@nodelib/fs.scandir": { @@ -130,10 +246,94 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, "node_modules/@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/node": { @@ -142,329 +342,417 @@ "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==", "dev": true }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.0.tgz", - "integrity": "sha512-WrVzGMzzCrgrpnQMQm4Tnf+dk+wdl/YbgIgd5hKGa2P+lnJ2MON+nQnbwgbxtN9QDLi8HO+JAq0/krMnjQK6Cw==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.9.0", - "@typescript-eslint/scope-manager": "4.9.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.0.tgz", - "integrity": "sha512-0p8GnDWB3R2oGhmRXlEnCvYOtaBCijtA5uBfH5GxQKsukdSQyI4opC4NGTUb88CagsoNQ4rb/hId2JuMbzWKFQ==", + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.9.0", - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/typescript-estree": "4.9.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" } }, - "node_modules/@typescript-eslint/parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.0.tgz", - "integrity": "sha512-QRSDAV8tGZoQye/ogp28ypb8qpsZPV6FOLD+tbN4ohKUWHD2n/u0Q2tIBnCsGwQCiD94RdtLkcqpdK4vKcLCCw==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "4.9.0", - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/typescript-estree": "4.9.0", - "debug": "^4.1.1" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.0.tgz", - "integrity": "sha512-q/81jtmcDtMRE+nfFt5pWqO0R41k46gpVLnuefqVOXl4QV1GdQoBWfk5REcipoJNQH9+F5l+dwa9Li5fbALjzg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/visitor-keys": "4.9.0" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.0.tgz", - "integrity": "sha512-luzLKmowfiM/IoJL/rus1K9iZpSJK6GlOS/1ezKplb7MkORt2dDcfi8g9B0bsF6JoRGhqn0D3Va55b+vredFHA==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.0.tgz", - "integrity": "sha512-rmDR++PGrIyQzAtt3pPcmKWLr7MA+u/Cmq9b/rON3//t5WofNR4m/Ybft2vOLj0WtUzjn018ekHjTsnIyBsQug==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/visitor-keys": "4.9.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.0.tgz", - "integrity": "sha512-sV45zfdRqQo1A97pOSx3fsjR+3blmwtdCt8LDrXgCX36v4Vmz4KHrhpV6Fo2cRdXmyumxx11AHw0pNJqCNpDyg==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.9.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "balanced-match": "^1.0.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "engines": { - "node": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/chalk/node_modules/color-convert": { @@ -506,31 +794,16 @@ "node": ">=8" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -542,12 +815,12 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -559,202 +832,210 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" + "node": ">=10" }, - "engines": { - "node": ">=8.6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", - "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", - "ajv": "^6.10.0", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" }, "engines": { - "node": ">=8.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=4.0" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, "engines": { - "node": ">= 4" + "node": ">=10.13.0" } }, "node_modules/espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -764,9 +1045,9 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -793,15 +1074,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -817,21 +1089,26 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.8" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -843,7 +1120,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { @@ -856,21 +1133,21 @@ } }, "node_modules/file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^2.0.1" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=16.0.0" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -879,62 +1156,45 @@ "node": ">=8" } }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" @@ -944,127 +1204,70 @@ } }, "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.8.19" } }, - "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -1085,28 +1288,27 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1119,6 +1321,15 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1132,10 +1343,25 @@ "node": ">= 0.8.0" } }, - "node_modules/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/merge2": { @@ -1148,22 +1374,22 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1172,28 +1398,10 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/natural-compare": { @@ -1202,19 +1410,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -1222,12 +1421,42 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1240,13 +1469,13 @@ "node": ">=6" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/path-key": { @@ -1258,19 +1487,10 @@ "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1288,34 +1508,41 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, "engines": { - "node": ">=0.4.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, "engines": { - "node": ">=6" + "node": ">=6.0.0" } }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": ">=6" } }, "node_modules/resolve-from": { @@ -1337,18 +1564,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/run-parallel": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", @@ -1370,9 +1585,9 @@ ] }, "node_modules/semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1402,194 +1617,287 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "@pkgr/core": "^0.2.9" }, "engines": { - "node": ">=6" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" } }, - "node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=6" + "node": ">=8.0" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=6" + "node": ">= 0.8.0" } }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=8" + "node": ">=14.17" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.45.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "dependencies": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" }, "engines": { - "node": ">=8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "dev": true, "dependencies": { - "tslib": "^1.8.1" + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">= 6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "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" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "engines": { - "node": ">=4.2.0" + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1606,105 +1914,152 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "dependencies": { - "mkdirp": "^0.5.1" - }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + } + }, + "@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true + }, + "@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.15" } }, "@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true } } }, + "@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true + }, + "@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "requires": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + } + }, + "@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true + }, + "@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "requires": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -1731,10 +2086,62 @@ "fastq": "^1.6.0" } }, + "@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true + }, + "@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true + } + } + }, + "@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/node": { @@ -1743,99 +2150,197 @@ "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==", "dev": true }, - "@typescript-eslint/eslint-plugin": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.0.tgz", - "integrity": "sha512-WrVzGMzzCrgrpnQMQm4Tnf+dk+wdl/YbgIgd5hKGa2P+lnJ2MON+nQnbwgbxtN9QDLi8HO+JAq0/krMnjQK6Cw==", + "@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.9.0", - "@typescript-eslint/scope-manager": "4.9.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.0.tgz", - "integrity": "sha512-0p8GnDWB3R2oGhmRXlEnCvYOtaBCijtA5uBfH5GxQKsukdSQyI4opC4NGTUb88CagsoNQ4rb/hId2JuMbzWKFQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.9.0", - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/typescript-estree": "4.9.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.0.tgz", - "integrity": "sha512-QRSDAV8tGZoQye/ogp28ypb8qpsZPV6FOLD+tbN4ohKUWHD2n/u0Q2tIBnCsGwQCiD94RdtLkcqpdK4vKcLCCw==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.9.0", - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/typescript-estree": "4.9.0", - "debug": "^4.1.1" + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + } } }, - "@typescript-eslint/scope-manager": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.0.tgz", - "integrity": "sha512-q/81jtmcDtMRE+nfFt5pWqO0R41k46gpVLnuefqVOXl4QV1GdQoBWfk5REcipoJNQH9+F5l+dwa9Li5fbALjzg==", + "@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "dev": true, - "requires": { - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/visitor-keys": "4.9.0" - } - }, - "@typescript-eslint/types": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.0.tgz", - "integrity": "sha512-luzLKmowfiM/IoJL/rus1K9iZpSJK6GlOS/1ezKplb7MkORt2dDcfi8g9B0bsF6JoRGhqn0D3Va55b+vredFHA==", - "dev": true + "requires": {} }, - "@typescript-eslint/typescript-estree": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.0.tgz", - "integrity": "sha512-rmDR++PGrIyQzAtt3pPcmKWLr7MA+u/Cmq9b/rON3//t5WofNR4m/Ybft2vOLj0WtUzjn018ekHjTsnIyBsQug==", + "@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "dev": true, "requires": { - "@typescript-eslint/types": "4.9.0", - "@typescript-eslint/visitor-keys": "4.9.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "@typescript-eslint/visitor-keys": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.0.tgz", - "integrity": "sha512-sV45zfdRqQo1A97pOSx3fsjR+3blmwtdCt8LDrXgCX36v4Vmz4KHrhpV6Fo2cRdXmyumxx11AHw0pNJqCNpDyg==", + "@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.9.0", - "eslint-visitor-keys": "^2.0.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + } + }, + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -1851,66 +2356,22 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - } - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -1918,12 +2379,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "callsites": { @@ -1983,31 +2444,16 @@ } } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -2016,177 +2462,151 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "eslint": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", - "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3" }, "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } } } }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } + "requires": {} }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" } }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, "espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "dependencies": { "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true } } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -2208,12 +2628,6 @@ } } }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2226,18 +2640,23 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.8" } }, "fast-json-stable-stringify": { @@ -2249,7 +2668,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastq": { @@ -2262,122 +2681,80 @@ } }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^4.0.0" } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" } }, + "flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -2390,38 +2767,16 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -2436,25 +2791,24 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2467,6 +2821,15 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2477,10 +2840,19 @@ "type-check": "~0.4.0" } }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "merge2": { @@ -2490,43 +2862,28 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "natural-compare": { @@ -2535,19 +2892,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -2555,7 +2903,25 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" } }, "parent-module": { @@ -2567,10 +2933,10 @@ "callsites": "^3.0.0" } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-key": { @@ -2579,16 +2945,10 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "prelude-ls": { @@ -2597,22 +2957,26 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "peer": true }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "resolve-from": { @@ -2627,15 +2991,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "run-parallel": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", @@ -2643,9 +2998,9 @@ "dev": true }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true }, "shebang-command": { @@ -2663,93 +3018,21 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "@pkgr/core": "^0.2.9" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2759,20 +3042,12 @@ "is-number": "^7.0.0" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "requires": { - "tslib": "^1.8.1" - } + "requires": {} }, "type-check": { "version": "0.4.0", @@ -2784,26 +3059,138 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, + "typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + } + }, + "@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + }, + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, - "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2814,25 +3201,16 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } } } } diff --git a/common/package.json b/common/package.json index 2ce9d168..e7174393 100644 --- a/common/package.json +++ b/common/package.json @@ -22,10 +22,14 @@ }, "homepage": "https://github.com/CCDirectLink/crosscode-map-editor#readme", "devDependencies": { + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.9.0", - "@typescript-eslint/parser": "^4.9.0", - "eslint": "^7.14.0", - "typescript": "^3.9.7" + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" } } diff --git a/common/prettier.config.mjs b/common/prettier.config.mjs new file mode 100644 index 00000000..b4063d36 --- /dev/null +++ b/common/prettier.config.mjs @@ -0,0 +1,2 @@ +export const editorconfig = true; +export const singleQuote = true; diff --git a/common/src/controllers/api.ts b/common/src/controllers/api.ts index 969ce831..e99f0b49 100644 --- a/common/src/controllers/api.ts +++ b/common/src/controllers/api.ts @@ -1,36 +1,48 @@ -import { fsPromise, pathPromise } from '../require.js'; -import { saveFile as save } from './saveFile.js'; +import { fsPromise, pathPromise } from "../require.js"; +import { saveFile as save } from "./saveFile.js"; export interface ModEditorConfig { - filename: string; - mod: string; - file: string; + filename: string; + mod: string; + file: string; } const mods: string[] = []; -let packagesCache: Record }>; - -async function listAllFiles(dir: string, filelist: string[], ending: string, root?: string): Promise { - if (root === undefined) { - root = dir; - } - - const files = await tryReadDir(dir); - const promises: Promise[] = []; - for (const file of files) { - promises.push(searchFile(file, dir, filelist, ending, root)); // CAUTION: Stores data in input variable (filelist) - } - await Promise.all(promises); - return filelist; +let packagesCache: Record< + string, + { + folderName: string; + displayName: string; + ccmodDependencies?: Map; + } +>; + +async function listAllFiles( + dir: string, + filelist: string[], + ending: string, + root?: string, +): Promise { + if (root === undefined) { + root = dir; + } + + const files = await tryReadDir(dir); + const promises: Promise[] = []; + for (const file of files) { + promises.push(searchFile(file, dir, filelist, ending, root)); // CAUTION: Stores data in input variable (filelist) + } + await Promise.all(promises); + return filelist; } async function tryReadDir(dir: string): Promise { - const fs = await fsPromise; - try { - return await fs.promises.readdir(dir); - } catch { - return []; - } + const fs = await fsPromise; + try { + return await fs.promises.readdir(dir); + } catch { + return []; + } } /** @@ -43,23 +55,31 @@ async function tryReadDir(dir: string): Promise { * @param ending The ending of the target files * @param root The root folder */ -async function searchFile(file: string, dir: string, filelist: string[], ending: string, root?: string): Promise { - const fs = await fsPromise; - const path = await pathPromise; - const stat = await fs.promises.stat(path.resolve(dir, file)); - if (stat.isDirectory()) { - await listAllFiles(path.resolve(dir, file), filelist, ending, root); - } else if (!ending || file.toLowerCase().endsWith(ending.toLowerCase())) { - const normalized = path - .resolve(dir, file) - .split(path.normalize(root))[1] - .replace(/\\/g, '/'); - - const result = normalized.startsWith('/') ? normalized.substr(1) : normalized; - if (!filelist.includes(result)) { - filelist.push(result); - } - } +async function searchFile( + file: string, + dir: string, + filelist: string[], + ending: string, + root?: string, +): Promise { + const fs = await fsPromise; + const path = await pathPromise; + const stat = await fs.promises.stat(path.resolve(dir, file)); + if (stat.isDirectory()) { + await listAllFiles(path.resolve(dir, file), filelist, ending, root); + } else if (!ending || file.toLowerCase().endsWith(ending.toLowerCase())) { + const normalized = path + .resolve(dir, file) + .split(path.normalize(root))[1] + .replace(/\\/g, "/"); + + const result = normalized.startsWith("/") + ? normalized.substr(1) + : normalized; + if (!filelist.includes(result)) { + filelist.push(result); + } + } } /** @@ -68,265 +88,361 @@ async function searchFile(file: string, dir: string, filelist: string[], ending: * @param file The file to be searched */ async function searchSubFolder(dir: string, file: string): Promise { - const fs = await fsPromise; - const path = await pathPromise; - const result: string[] = []; - const files = await tryReadDir(dir); - for (const folder of files) { - try { - const stat = await fs.promises.stat(path.join(dir, folder, file)); - if (stat.isFile()) { - result.push(path.join(dir, folder, file)); - } - } catch (e) { - console.error(e); - } - } - return result; + const fs = await fsPromise; + const path = await pathPromise; + const result: string[] = []; + const files = await tryReadDir(dir); + for (const folder of files) { + try { + const stat = await fs.promises.stat(path.join(dir, folder, file)); + if (stat.isFile()) { + result.push(path.join(dir, folder, file)); + } + } catch (e) { + console.error(e); + } + } + return result; } -function selectMod(name: string, packages: Record }>, result: string[]) { - const pkg = packages[name]; - if (!pkg) { - return; - } - - result.push(pkg.folderName); - if (!pkg.ccmodDependencies) { - return; - } - - for (const depName of Object.keys(pkg.ccmodDependencies)) { - selectMod(depName, packages, result); - } +function selectMod( + name: string, + packages: Record< + string, + { folderName: string; ccmodDependencies?: Map } + >, + result: string[], +) { + const pkg = packages[name]; + if (!pkg) { + return; + } + + result.push(pkg.folderName); + if (!pkg.ccmodDependencies) { + return; + } + + for (const depName of Object.keys(pkg.ccmodDependencies)) { + selectMod(depName, packages, result); + } } async function getAsync(file: string): Promise { - const fs = await fsPromise; - try { - const stat = await fs.promises.stat(file); - if (stat.isFile()) { - return fs.promises.readFile(file); - } - } catch (e) { - console.error(e); - } - return null; + const fs = await fsPromise; + try { + const stat = await fs.promises.stat(file); + if (stat.isFile()) { + return fs.promises.readFile(file); + } + } catch (e) { + console.error(e); + } + return null; } async function resolveAsync(file: string): Promise { - const fs = await fsPromise; - try { - const stat = await fs.promises.stat(file); - if (stat.isFile()) { - return file; - } - } catch (e) { - console.error(e); - } - return null; + const fs = await fsPromise; + try { + const stat = await fs.promises.stat(file); + if (stat.isFile()) { + return file; + } + } catch (e) { + console.error(e); + } + return null; } async function readMods(dir: string) { - if (packagesCache) { - return packagesCache; - } - - const fs = await fsPromise; - const path = await pathPromise; - - const modFolder = path.join(dir, 'mods/'); - const files = await searchSubFolder(modFolder, 'package.json'); - const filesCCMod = await searchSubFolder(modFolder, 'ccmod.json'); - - const ccmodFolderNames = new Set(filesCCMod.map(file => path.basename(path.dirname(file)))); - - const promises: Promise<[string, Buffer]>[] = []; - for (const file of files) { - const folderName = path.basename(path.dirname(file)); - // Skip mods that have a ccmod.json file - if (ccmodFolderNames.has(folderName)) { - continue; - } - promises.push((async (): Promise<[string, Buffer]> => [path.basename(path.dirname(file)), await fs.promises.readFile(file)])()); - } - const rawPackages = await Promise.all(promises); - const packages: Record }> = {}; - - for (const [name, pkg] of rawPackages) { - try { - const parsed = JSON.parse(pkg as unknown as string); - packages[parsed.name] = { - folderName: name, - displayName: parsed.displayName ?? parsed.ccmodHumanName ?? parsed.name, - ccmodDependencies: parsed.ccmodDependencies ?? parsed.dependencies ?? {}, - }; - } catch (err) { - console.error('Invalid json data in package.json of mod: ' + name, err); - } - } - - const promisesCCMod: Promise<[string, Buffer]>[] = []; - for (const file of filesCCMod) { - promisesCCMod.push((async (): Promise<[string, Buffer]> => [path.basename(path.dirname(file)), await fs.promises.readFile(file)])()); - } - const rawCCMods = await Promise.all(promisesCCMod); - - for (const [name, pkg] of rawCCMods) { - try { - const parsed = JSON.parse(pkg as unknown as string); - packages[parsed.id] = { - folderName: name, - displayName: parsed.title?.['en_US'] ?? parsed.title ?? parsed.id, - ccmodDependencies: parsed.ccmodDependencies ?? parsed.dependencies ?? {}, - }; - } catch (err) { - console.error('Invalid json data in ccmod.json of mod: ' + name, err); - } - } - - packagesCache = packages; - return packages; + if (packagesCache) { + return packagesCache; + } + + const fs = await fsPromise; + const path = await pathPromise; + + const modFolder = path.join(dir, "mods/"); + const files = await searchSubFolder(modFolder, "package.json"); + const filesCCMod = await searchSubFolder(modFolder, "ccmod.json"); + + const ccmodFolderNames = new Set( + filesCCMod.map((file) => path.basename(path.dirname(file))), + ); + + const promises: Promise<[string, Buffer]>[] = []; + for (const file of files) { + const folderName = path.basename(path.dirname(file)); + // Skip mods that have a ccmod.json file + if (ccmodFolderNames.has(folderName)) { + continue; + } + promises.push( + (async (): Promise<[string, Buffer]> => [ + path.basename(path.dirname(file)), + await fs.promises.readFile(file), + ])(), + ); + } + const rawPackages = await Promise.all(promises); + const packages: Record< + string, + { + folderName: string; + displayName: string; + ccmodDependencies?: Map; + } + > = {}; + + for (const [name, pkg] of rawPackages) { + try { + const parsed = JSON.parse(pkg as unknown as string); + packages[parsed.name] = { + folderName: name, + displayName: parsed.displayName ?? parsed.ccmodHumanName ?? parsed.name, + ccmodDependencies: + parsed.ccmodDependencies ?? parsed.dependencies ?? {}, + }; + } catch (err) { + console.error("Invalid json data in package.json of mod: " + name, err); + } + } + + const promisesCCMod: Promise<[string, Buffer]>[] = []; + for (const file of filesCCMod) { + promisesCCMod.push( + (async (): Promise<[string, Buffer]> => [ + path.basename(path.dirname(file)), + await fs.promises.readFile(file), + ])(), + ); + } + const rawCCMods = await Promise.all(promisesCCMod); + + for (const [name, pkg] of rawCCMods) { + try { + const parsed = JSON.parse(pkg as unknown as string); + packages[parsed.id] = { + folderName: name, + displayName: parsed.title?.["en_US"] ?? parsed.title ?? parsed.id, + ccmodDependencies: + parsed.ccmodDependencies ?? parsed.dependencies ?? {}, + }; + } catch (err) { + console.error("Invalid json data in ccmod.json of mod: " + name, err); + } + } + + packagesCache = packages; + return packages; } export async function getAllFiles(dir: string) { - const path = await pathPromise; - const images = await listAllFiles(path.resolve(dir, 'media/'), [], 'png', path.resolve(dir)); - const data = await listAllFiles(path.resolve(dir, 'data/'), [], 'json', path.resolve(dir)); - - for (const mod of mods) { - const modDir = path.join(dir, 'mods', mod, 'assets'); - await listAllFiles(path.resolve(modDir, 'media/'), images, 'png', path.resolve(modDir)); - await listAllFiles(path.resolve(modDir, 'data/'), data, 'json', path.resolve(modDir)); - } - - images.sort(); - data.sort(); - - return {images, data}; + const path = await pathPromise; + const images = await listAllFiles( + path.resolve(dir, "media/"), + [], + "png", + path.resolve(dir), + ); + const data = await listAllFiles( + path.resolve(dir, "data/"), + [], + "json", + path.resolve(dir), + ); + + for (const mod of mods) { + const modDir = path.join(dir, "mods", mod, "assets"); + await listAllFiles( + path.resolve(modDir, "media/"), + images, + "png", + path.resolve(modDir), + ); + await listAllFiles( + path.resolve(modDir, "data/"), + data, + "json", + path.resolve(modDir), + ); + } + + images.sort(); + data.sort(); + + return { images, data }; } export async function getAllTilesets(dir: string) { - const path = await pathPromise; - const result = await listAllFiles(path.resolve(dir, 'media/map/'), [], 'png', path.resolve(dir)); - - for (const mod of mods) { - const modDir = path.join(dir, 'mods', mod, 'assets'); - await listAllFiles(path.resolve(modDir, 'media/map/'), result, 'png', path.resolve(modDir)); - } - - return result.sort(); + const path = await pathPromise; + const result = await listAllFiles( + path.resolve(dir, "media/map/"), + [], + "png", + path.resolve(dir), + ); + + for (const mod of mods) { + const modDir = path.join(dir, "mods", mod, "assets"); + await listAllFiles( + path.resolve(modDir, "media/map/"), + result, + "png", + path.resolve(modDir), + ); + } + + return result.sort(); } export async function getAllMaps(dir: string, includeVanillaMaps: boolean) { - const path = await pathPromise; - const paths: string[] = []; - - if (mods.length === 0 || includeVanillaMaps) { - await listAllFiles(path.resolve(dir, 'data/maps/'), paths, 'json', path.resolve(dir)); - } - if (mods.length > 0) { - const modDir = path.join(dir, 'mods', mods[0], 'assets'); - await listAllFiles(path.resolve(modDir, 'data/maps/'), paths, 'json', path.resolve(modDir)); - } - - return paths - .sort() - .map(p => p.substring('data/maps/'.length, p.length - '.json'.length)) - .map(p => p.replace(/\//g, '.').replace(/\\/g, '.')); + const path = await pathPromise; + const paths: string[] = []; + + if (mods.length === 0 || includeVanillaMaps) { + await listAllFiles( + path.resolve(dir, "data/maps/"), + paths, + "json", + path.resolve(dir), + ); + } + if (mods.length > 0) { + const modDir = path.join(dir, "mods", mods[0], "assets"); + await listAllFiles( + path.resolve(modDir, "data/maps/"), + paths, + "json", + path.resolve(modDir), + ); + } + + return paths + .sort() + .map((p) => p.substring("data/maps/".length, p.length - ".json".length)) + .map((p) => p.replace(/\//g, ".").replace(/\\/g, ".")); } -export async function getAllFilesInFolder(dir: string, folder: string, extension: string) { - const path = await pathPromise; - const result = await listAllFiles(path.resolve(dir, folder), [], extension, path.resolve(dir)); - - for (const mod of mods) { - const modDir = path.join(dir, 'mods', mod, 'assets'); - await listAllFiles(path.resolve(modDir, folder), result, extension, path.resolve(modDir)); - } - - return result.sort() - .map(p => p.substring(folder.length, p.length - `.${extension}`.length)); +export async function getAllFilesInFolder( + dir: string, + folder: string, + extension: string, +) { + const path = await pathPromise; + const result = await listAllFiles( + path.resolve(dir, folder), + [], + extension, + path.resolve(dir), + ); + + for (const mod of mods) { + const modDir = path.join(dir, "mods", mod, "assets"); + await listAllFiles( + path.resolve(modDir, folder), + result, + extension, + path.resolve(modDir), + ); + } + + return result + .sort() + .map((p) => p.substring(folder.length, p.length - `.${extension}`.length)); } export async function getAllMods(dir: string) { - const packages = await readMods(dir); - return Object.entries(packages) - .map(([id, pkg]) => ({id, displayName: pkg.displayName as string})) - .sort((a, b) => a.displayName.localeCompare(b.displayName)); + const packages = await readMods(dir); + return Object.entries(packages) + .map(([id, pkg]) => ({ id, displayName: pkg.displayName as string })) + .sort((a, b) => a.displayName.localeCompare(b.displayName)); } -export async function getAllModMapEditorConfigs(dir: string): Promise { - const packages = await readMods(dir); - - const fs = await fsPromise; - const path = await pathPromise; - - const configs: ModEditorConfig[] = []; - - for (const mod of Object.values(packages)) { - const modName = mod.folderName; - const mapEditorPath = path.join(dir, 'mods', modName, 'map-editor'); - const files = await listAllFiles(mapEditorPath, [], 'json', path.resolve(dir)); - for (const filename of files) { - const file = await fs.promises.readFile(path.join(dir, filename), 'utf-8'); - configs.push({ - filename: path.basename(filename), - mod: modName, - file: file - }); - } - } - return configs; +export async function getAllModMapEditorConfigs( + dir: string, +): Promise { + const packages = await readMods(dir); + + const fs = await fsPromise; + const path = await pathPromise; + + const configs: ModEditorConfig[] = []; + + for (const mod of Object.values(packages)) { + const modName = mod.folderName; + const mapEditorPath = path.join(dir, "mods", modName, "map-editor"); + const files = await listAllFiles( + mapEditorPath, + [], + "json", + path.resolve(dir), + ); + for (const filename of files) { + const file = await fs.promises.readFile( + path.join(dir, filename), + "utf-8", + ); + configs.push({ + filename: path.basename(filename), + mod: modName, + file: file, + }); + } + } + return configs; } export async function selectedMod(dir: string, modName: string) { - const packages = await readMods(dir); - mods.splice(0); // Clear array - selectMod(modName, packages, mods); + const packages = await readMods(dir); + mods.splice(0); // Clear array + selectMod(modName, packages, mods); } export async function get(dir: string, file: string): Promise { - const path = await pathPromise; - const promises: Promise[] = []; - for (const mod of mods) { - const modFile = path.join(dir, 'mods', mod, 'assets', file); - promises.push(getAsync(modFile)); - } - promises.push(getAsync(path.join(dir, file))); - - const results = await Promise.all(promises); - for (const result of results) { - if (result) { - return JSON.parse(result as unknown as string); - } - } - throw new Error('File not found: ' + file); + const path = await pathPromise; + const promises: Promise[] = []; + for (const mod of mods) { + const modFile = path.join(dir, "mods", mod, "assets", file); + promises.push(getAsync(modFile)); + } + promises.push(getAsync(path.join(dir, file))); + + const results = await Promise.all(promises); + for (const result of results) { + if (result) { + return JSON.parse(result as unknown as string); + } + } + throw new Error("File not found: " + file); } export async function resolve(dir: string, file: string): Promise { - const path = await pathPromise; - const promises: Promise[] = []; - for (const mod of mods) { - const modFile = path.join(dir, 'mods', mod, 'assets', file); - promises.push(resolveAsync(modFile)); - } - promises.push(resolveAsync(path.join(dir, file))); - - const results = await Promise.all(promises); - for (const result of results) { - if (result) { - return path.relative(dir, result); - } - } - throw new Error('File not found: ' + file); + const path = await pathPromise; + const promises: Promise[] = []; + for (const mod of mods) { + const modFile = path.join(dir, "mods", mod, "assets", file); + promises.push(resolveAsync(modFile)); + } + promises.push(resolveAsync(path.join(dir, file))); + + const results = await Promise.all(promises); + for (const result of results) { + if (result) { + return path.relative(dir, result); + } + } + throw new Error("File not found: " + file); } -export async function saveFile(assetsPath: string, file: { content: string, path: string }) { - const path = await pathPromise; - if (mods.length === 0) { - return save(assetsPath, file); - } else { - return save(path.join(assetsPath, 'mods', mods[0], 'assets'), file); - } +export async function saveFile( + assetsPath: string, + file: { content: string; path: string }, +) { + const path = await pathPromise; + if (mods.length === 0) { + return save(assetsPath, file); + } else { + return save(path.join(assetsPath, "mods", mods[0], "assets"), file); + } } diff --git a/common/src/controllers/saveFile.ts b/common/src/controllers/saveFile.ts index 8566963a..b2df2919 100644 --- a/common/src/controllers/saveFile.ts +++ b/common/src/controllers/saveFile.ts @@ -1,11 +1,14 @@ -import { fsPromise, pathPromise } from '../require.js'; +import { fsPromise, pathPromise } from "../require.js"; -export async function saveFile(assetsPath: string, file: { content: string, path: string }) { - const fs = await fsPromise; - const path = await pathPromise; - - const fullPath = path.join(assetsPath, file.path); - fs.promises.mkdir(path.dirname(fullPath), {recursive: true}); - await fs.promises.writeFile(fullPath, file.content); - return 'successfully saved file to "' + fullPath + '"'; +export async function saveFile( + assetsPath: string, + file: { content: string; path: string }, +) { + const fs = await fsPromise; + const path = await pathPromise; + + const fullPath = path.join(assetsPath, file.path); + fs.promises.mkdir(path.dirname(fullPath), { recursive: true }); + await fs.promises.writeFile(fullPath, file.content); + return 'successfully saved file to "' + fullPath + '"'; } diff --git a/common/src/main.ts b/common/src/main.ts index 4ed8f859..88e10d45 100644 --- a/common/src/main.ts +++ b/common/src/main.ts @@ -1,3 +1,3 @@ -import * as API from './controllers/api.js'; +import * as API from "./controllers/api.js"; export const api = API; diff --git a/common/src/require.ts b/common/src/require.ts index c6017dac..1d32dbc4 100644 --- a/common/src/require.ts +++ b/common/src/require.ts @@ -1,47 +1,61 @@ -export let fsPromise: Promise; -export let pathPromise: Promise; +export let fsPromise: Promise; +export let pathPromise: Promise; +if ( + typeof window !== "undefined" && + typeof navigator !== "undefined" && + navigator.userAgent.indexOf("Electron") > -1 +) { + // Use electron require + const remote = window["require"]("@electron/remote"); + fsPromise = remote.require("fs"); + pathPromise = remote.require("path"); +} else if (typeof process !== "undefined" && !!globalThis["require"]) { + // Confuse webpack to not resolve import at compile time + const fsPath = globalThis["doesNotMatter"] ? "fs" : "fs"; + const pathPath = globalThis["doesNotMatter"] ? "path" : "path"; -if (typeof window !== 'undefined' && - typeof navigator !== 'undefined' && - navigator.userAgent.indexOf('Electron') > -1) { + // Regular nodejs + // eslint-disable-next-line @typescript-eslint/no-require-imports + fsPromise = require(/* @vite-ignore */ fsPath); + // eslint-disable-next-line @typescript-eslint/no-require-imports + pathPromise = require(/* @vite-ignore */ pathPath); +} else if (typeof process !== "undefined") { + // Confuse webpack to not resolve import at compile time + const fsPath = globalThis["doesNotMatter"] ? "fs" : "fs"; + const pathPath = globalThis["doesNotMatter"] ? "path" : "path"; - // Use electron require - const remote = window['require']('@electron/remote'); - fsPromise = remote.require('fs'); - pathPromise = remote.require('path'); -} else if (typeof process !== 'undefined' && !!globalThis['require']) { - // Confuse webpack to not resolve import at compile time - const fsPath = globalThis['doesNotMatter'] ? 'fs' : 'fs'; - const pathPath = globalThis['doesNotMatter'] ? 'path' : 'path'; - - // Regular nodejs - fsPromise = require(fsPath); - pathPromise = require(pathPath); -} else if (typeof process !== 'undefined') { - // Confuse webpack to not resolve import at compile time - const fsPath = globalThis['doesNotMatter'] ? 'fs' : 'fs'; - const pathPath = globalThis['doesNotMatter'] ? 'path' : 'path'; - - // Regular nodejs - fsPromise = import( /* @vite-ignore */ fsPath); - pathPromise = import( /* @vite-ignore */ pathPath); + // Regular nodejs + fsPromise = import(/* @vite-ignore */ fsPath); + pathPromise = import(/* @vite-ignore */ pathPath); } else { - // Not a supported plattform. Return dummy, do not fail on execute of the lambda. - fsPromise = Promise.resolve((() => new Proxy({}, { - get() { - throw new Error('This platform is not supported'); - }, - set() { - throw new Error('This platform is not supported'); - } - })) as unknown as typeof import('fs')); - pathPromise = Promise.resolve((() => new Proxy({}, { - get() { - throw new Error('This platform is not supported'); - }, - set() { - throw new Error('This platform is not supported'); - } - })) as unknown as typeof import('path')); + // Not a supported plattform. Return dummy, do not fail on execute of the lambda. + fsPromise = Promise.resolve( + (() => + new Proxy( + {}, + { + get() { + throw new Error("This platform is not supported"); + }, + set() { + throw new Error("This platform is not supported"); + }, + }, + )) as unknown as typeof import("fs"), + ); + pathPromise = Promise.resolve( + (() => + new Proxy( + {}, + { + get() { + throw new Error("This platform is not supported"); + }, + set() { + throw new Error("This platform is not supported"); + }, + }, + )) as unknown as typeof import("path"), + ); } diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json deleted file mode 100644 index be1297d7..00000000 --- a/webapp/.eslintrc.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "env": { - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@angular-eslint/recommended" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "project": "./tsconfig.json" - }, - "plugins": [ - "@typescript-eslint", - "@angular-eslint" - ], - "root": true, - "rules": { - "@angular-eslint/component-class-suffix": "error", - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": ["app", "cc"], - "style": "kebab-case" - } - ], - "@angular-eslint/directive-class-suffix": "error", - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": ["app", "cc"], - "style": "camelCase" - } - ], - "@angular-eslint/no-input-rename": "error", - "@angular-eslint/no-output-rename": "error", - "@angular-eslint/use-lifecycle-interface": "error", - "@angular-eslint/use-pipe-transform-interface": "error", - "@angular-eslint/no-output-on-prefix": "off", - "@angular-eslint/no-output-native": "off", - "@typescript-eslint/consistent-type-definitions": "error", - "@typescript-eslint/dot-notation": "off", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/explicit-member-accessibility": [ - "off", - { - "accessibility": "explicit" - } - ], - "@typescript-eslint/member-delimiter-style": [ - "error", - { - "multiline": { - "requireLast": true - }, - "singleline": { - "delimiter": "comma", - "requireLast": false - } - } - ], - "@typescript-eslint/member-ordering": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-inferrable-types": "warn", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-unused-expressions": "error", - "@typescript-eslint/prefer-function-type": "error", - "@typescript-eslint/quotes": ["error", "single"], - "@typescript-eslint/semi": ["error", "always"], - "@typescript-eslint/type-annotation-spacing": "error", - "@typescript-eslint/unified-signatures": "error", - "brace-style": ["error", "1tbs"], - "camelcase": "off", - "constructor-super": "error", - "curly": "error", - "eol-last": "error", - "eqeqeq": ["error", "smart"], - "guard-for-in": "error", - "id-blacklist": "off", - "id-match": "off", - "no-bitwise": "error", - "no-caller": "error", - "no-debugger": "error", - "no-empty": "off", - "no-eval": "error", - "no-fallthrough": "error", - "no-new-wrappers": "error", - "no-restricted-imports": "error", - "no-throw-literal": "error", - "no-trailing-spaces": "off", - "no-undef-init": "error", - "no-underscore-dangle": "off", - "no-unused-labels": "error", - "no-unused-vars": "off", - "no-var": "error", - "no-constant-condition": "off", - "prefer-const": "error", - "radix": "error", - "quote-props": "off", - "max-len": ["error", 180] - } -} diff --git a/webapp/angular.json b/webapp/angular.json index 3326005c..2e732050 100644 --- a/webapp/angular.json +++ b/webapp/angular.json @@ -24,7 +24,8 @@ "index": "src/index.html", "tsConfig": "src/tsconfig.app.json", "polyfills": [ - "src/polyfills.ts" + "zone.js", + "phaser/dist/phaser" ], "assets": [ "src/assets", @@ -85,7 +86,10 @@ "options": { "main": "src/test.ts", "karmaConfig": "./karma.conf.js", - "polyfills": "src/polyfills.ts", + "polyfills": [ + "zone.js", + "phaser/dist/phaser" + ], "tsConfig": "src/tsconfig.spec.json", "sourceMap": true, "scripts": [], @@ -107,9 +111,10 @@ "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ - "src/**/*.ts" + "src/**/*.ts", + "src/**/*.html" ], - "eslintConfig": "./.eslintrc.json" + "eslintConfig": "./eslint.config.mjs" } } } diff --git a/webapp/eslint.config.mjs b/webapp/eslint.config.mjs new file mode 100644 index 00000000..6a52270b --- /dev/null +++ b/webapp/eslint.config.mjs @@ -0,0 +1,139 @@ +// @ts-check + +import eslint from '@eslint/js'; +import { defineConfig } from 'eslint/config'; +import tseslint from 'typescript-eslint'; +import globals from 'globals'; +import angular from 'angular-eslint'; +import stylistic from '@stylistic/eslint-plugin'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; + +export default defineConfig( + { + languageOptions: { + globals: globals['shared-node-browser'], + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.ts'], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylistic, + ...angular.configs.tsRecommended, + ], + processor: angular.processInlineTemplates, + plugins: { + '@stylistic': stylistic, + }, + rules: { + '@angular-eslint/component-class-suffix': 'error', + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: ['app', 'cc'], + style: 'kebab-case', + }, + ], + '@angular-eslint/directive-class-suffix': 'error', + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: ['app', 'cc'], + style: 'camelCase', + }, + ], + '@angular-eslint/no-input-rename': 'error', + '@angular-eslint/no-output-rename': 'error', + '@angular-eslint/use-lifecycle-interface': 'error', + '@angular-eslint/use-pipe-transform-interface': 'error', + '@angular-eslint/no-output-on-prefix': 'off', + '@angular-eslint/no-output-native': 'off', + '@angular-eslint/prefer-standalone': 'warn', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/dot-notation': 'off', + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/explicit-member-accessibility': [ + 'off', + { + accessibility: 'explicit', + }, + ], + '@stylistic/member-delimiter-style': [ + 'error', + { + multiline: { + requireLast: true, + }, + singleline: { + delimiter: 'comma', + requireLast: false, + }, + }, + ], + '@typescript-eslint/member-ordering': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-inferrable-types': 'warn', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-unused-expressions': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/no-unsafe-assignment': 'warn', + '@typescript-eslint/no-unsafe-return': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + '@typescript-eslint/no-misused-promises': 'warn', + '@typescript-eslint/require-await': 'warn', + '@typescript-eslint/no-unused-vars': 'warn', + '@typescript-eslint/consistent-indexed-object-style': 'off', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'warn', + '@stylistic/quotes': ['error', 'single'], + '@stylistic/semi': ['error', 'always'], + '@stylistic/type-annotation-spacing': 'error', + '@typescript-eslint/unified-signatures': 'error', + 'brace-style': ['error', '1tbs'], + camelcase: 'off', + 'constructor-super': 'error', + curly: 'error', + 'eol-last': 'error', + eqeqeq: ['error', 'smart'], + 'guard-for-in': 'error', + 'id-blacklist': 'off', + 'id-match': 'off', + 'no-bitwise': 'error', + 'no-caller': 'error', + 'no-debugger': 'error', + 'no-empty': 'off', + 'no-eval': 'error', + 'no-fallthrough': 'error', + 'no-new-wrappers': 'error', + 'no-restricted-imports': 'error', + 'no-throw-literal': 'error', + 'no-trailing-spaces': 'off', + 'no-undef-init': 'error', + 'no-underscore-dangle': 'off', + 'no-unused-labels': 'error', + 'no-unused-vars': 'off', + 'no-var': 'error', + 'no-constant-condition': 'off', + 'prefer-const': 'error', + radix: 'error', + 'quote-props': 'off', + 'max-len': ['error', 180], + }, + }, + { + files: ['src/app/**/*.html'], + extends: [...angular.configs.templateRecommended], + rules: {}, + }, + eslintPluginPrettierRecommended, +); diff --git a/webapp/karma.conf.js b/webapp/karma.conf.js index 46c14360..ef0d2559 100644 --- a/webapp/karma.conf.js +++ b/webapp/karma.conf.js @@ -2,33 +2,33 @@ // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-phantomjs-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), - - ], - client:{ - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], - fixWebpackSourcePaths: true - }, - angularCli: { - environment: 'dev' - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false - }); + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-phantomjs-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + ], + client: { + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, 'coverage'), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true, + }, + angularCli: { + environment: 'dev', + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + }); }; diff --git a/webapp/main.js b/webapp/main.js index 42bee964..ebb8b925 100644 --- a/webapp/main.js +++ b/webapp/main.js @@ -1,18 +1,18 @@ // @ts-check 'use strict'; -const {app, protocol, BrowserWindow} = require('electron'); +const { app, protocol, BrowserWindow } = require('electron'); const electronRemote = require('@electron/remote/main'); const windowStateKeeper = require('electron-window-state'); const path = require('path'); const url = require('url'); -const {autoUpdater} = require('electron-updater'); +const { autoUpdater } = require('electron-updater'); const contextMenu = require('electron-context-menu').default; -const IPC = require("@achrinza/node-ipc").default; +const IPC = require('@achrinza/node-ipc').default; // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. const args = process.argv.slice(1); -const dev = args.some(val => val === '--dev'); +const dev = args.some((val) => val === '--dev'); process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = '1'; electronRemote.initialize(); @@ -21,14 +21,13 @@ let openWindows = 0; function openWindow() { let win; - + function createWindow() { - const mainWindowState = windowStateKeeper({ defaultWidth: 1000, - defaultHeight: 800 + defaultHeight: 800, }); - + win = new BrowserWindow({ x: mainWindowState.x, y: mainWindowState.y, @@ -37,15 +36,15 @@ function openWindow() { webPreferences: { webSecurity: false, nodeIntegration: true, - contextIsolation: false - } + contextIsolation: false, + }, }); electronRemote.enable(win.webContents); - + if (dev) { console.log('dev'); contextMenu({ - showInspectElement: true + showInspectElement: true, }); win.loadURL('http://localhost:4200/index.html'); win.webContents.openDevTools(); @@ -54,11 +53,11 @@ function openWindow() { const indexPath = url.format({ pathname: path.join(__dirname, 'distAngular', 'index.html'), protocol: 'file', - slashes: true + slashes: true, }); console.log('path', indexPath); win.loadURL(indexPath); - + const log = require('electron-log'); log.transports.file.level = 'debug'; autoUpdater.logger = log; @@ -66,7 +65,7 @@ function openWindow() { // win.webContents.openDevTools(); win.setMenu(null); } - + openWindows++; win.on('closed', () => { win = null; @@ -75,10 +74,10 @@ function openWindow() { app.quit(); } }); - + mainWindowState.manage(win); } - + app.on('activate', () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. @@ -86,7 +85,7 @@ function openWindow() { createWindow(); } }); - + createWindow(); } @@ -108,7 +107,6 @@ app.on('window-all-closed', () => { } }); - const sub = new IPC.IPC(); sub.config.silent = true; sub.config.maxRetries = 1; @@ -119,7 +117,7 @@ sub.connectTo('crosscode-map-editor', () => { }); sub.of['crosscode-map-editor'].on('error', () => { sub.disconnect('crosscode-map-editor'); - + const master = new IPC.IPC(); master.config.silent = true; master.config.id = 'crosscode-map-editor'; @@ -129,4 +127,3 @@ sub.connectTo('crosscode-map-editor', () => { master.server.start(); }); }); - diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 24be2ea8..0c6a3e46 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -36,14 +36,19 @@ "@angular/router": "^20.3.3", "@babylonjs/core": "^8.30.1", "@babylonjs/gui": "^8.30.1", + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/earcut": "^3.0.0", "@types/jasmine": "~5.1.9", "@types/jsoneditor": "^9.9.0", - "@typescript-eslint/eslint-plugin": "^8.45.0", + "angular-eslint": "^20.3.0", "cc-map-editor-common": "file:../common", "earcut": "^3.0.2", "electron": "^38.2.0", "electron-builder": "^26.0.12", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "jasmine-core": "~4.5.0", "jsoneditor": "^9.10.0", "karma": "~6.4.0", @@ -56,6 +61,7 @@ "tailwindcss": "^3.3.1", "tslib": "^2.3.0", "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", "zone.js": "~0.15.1" } }, @@ -455,12 +461,59 @@ "typescript": "*" } }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.3.0.tgz", + "integrity": "sha512-WMJDJfybOLCiN4QrOyrLl+Zt5F+A/xoDYMWTdn+LgACheLs2tguVQiwf+oCgHnHGcsTsulPYlRHldKBGZMgs4w==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "20.3.0", + "@angular-eslint/utils": "20.3.0", + "aria-query": "5.3.2", + "axobject-query": "4.1.0" + }, + "peerDependencies": { + "@angular-eslint/template-parser": "20.3.0", + "@typescript-eslint/types": "^7.11.0 || ^8.0.0", + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/schematics": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.3.0.tgz", + "integrity": "sha512-4n92tHKIJm1PP+FjhnmO7AMpvKdRIoF+YgF38oUU7aMJqfZ3RXIhazMMxw2u3VU1MisKH766KSll++c4LgarVA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": ">= 20.0.0 < 21.0.0", + "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", + "@angular-eslint/eslint-plugin": "20.3.0", + "@angular-eslint/eslint-plugin-template": "20.3.0", + "ignore": "7.0.5", + "semver": "7.7.2", + "strip-json-comments": "3.1.1" + } + }, + "node_modules/@angular-eslint/template-parser": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.3.0.tgz", + "integrity": "sha512-gB564h/kZ7siWvgHDETU++sk5e25qFfVaizLaa6KoBEYFP6dOCiedz15LTcA0TsXp0rGu6Z6zkl291iSM1qzDA==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "20.3.0", + "eslint-scope": "^8.0.2" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, "node_modules/@angular-eslint/utils": { "version": "20.3.0", "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.3.0.tgz", "integrity": "sha512-7XOQeNXgyhznDwoP1TwPrCMq/uXKJHQgCVPFREkJGKbNf/jzNldB7iV1eqpBzUQIPEQFgfcDG67dexpMAq3N4g==", "dev": true, - "license": "MIT", "dependencies": { "@angular-eslint/bundled-angular-compiler": "20.3.0" }, @@ -2465,8 +2518,6 @@ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, - "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", @@ -2481,8 +2532,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2493,8 +2542,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2507,8 +2554,6 @@ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -2518,8 +2563,6 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, - "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -2532,8 +2575,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2557,8 +2598,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2575,8 +2614,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2587,8 +2624,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } @@ -2597,17 +2632,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true + "dev": true }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2620,8 +2651,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2634,8 +2663,6 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -2645,8 +2672,6 @@ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, - "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" @@ -2667,8 +2692,6 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18.0" } @@ -2678,8 +2701,6 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -2693,8 +2714,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12.22" }, @@ -2708,8 +2727,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18" }, @@ -4655,6 +4672,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-beta.38", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.38.tgz", @@ -5585,6 +5614,38 @@ "dev": true, "license": "ISC" }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -5722,9 +5783,7 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT", - "peer": true + "dev": true }, "node_modules/@types/jsoneditor": { "version": "9.9.6", @@ -5830,7 +5889,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.45.0", @@ -5860,8 +5918,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -5943,7 +5999,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", @@ -6011,7 +6066,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.45.0", @@ -6131,8 +6185,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6145,8 +6197,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -6249,6 +6299,28 @@ "node": ">= 14.0.0" } }, + "node_modules/angular-eslint": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.3.0.tgz", + "integrity": "sha512-MvmeFuPmJHRmfL1A9IMtZJEYaU6sF++saJgpsU7aOD6YDZCGJ0J6HxlJ/q7YRbWYuI1q+gF/qALxdnuwHYadSg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": ">= 20.0.0 < 21.0.0", + "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", + "@angular-eslint/builder": "20.3.0", + "@angular-eslint/eslint-plugin": "20.3.0", + "@angular-eslint/eslint-plugin-template": "20.3.0", + "@angular-eslint/schematics": "20.3.0", + "@angular-eslint/template-parser": "20.3.0", + "@typescript-eslint/types": "^8.0.0", + "@typescript-eslint/utils": "^8.0.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*", + "typescript-eslint": "^8.0.0" + } + }, "node_modules/ansi-escapes": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", @@ -6460,6 +6532,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -6516,6 +6597,15 @@ "node": ">= 4.0.0" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -7018,8 +7108,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -7667,8 +7755,7 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/defaults": { "version": "1.0.4", @@ -8911,8 +8998,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8968,13 +9053,56 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9005,7 +9133,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9023,7 +9150,6 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9034,8 +9160,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9048,8 +9172,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } @@ -9059,8 +9181,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", @@ -9068,7 +9189,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9081,8 +9201,6 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -9100,8 +9218,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9115,7 +9231,6 @@ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -9128,8 +9243,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -9143,7 +9256,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -9153,8 +9265,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9335,6 +9445,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -9377,8 +9493,7 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.0", @@ -9439,8 +9554,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -9507,8 +9620,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9525,8 +9636,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -9874,8 +9983,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -9948,8 +10055,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/has-flag": { "version": "4.0.0", @@ -10343,7 +10449,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -10389,8 +10494,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -10878,8 +10981,7 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -11518,7 +11620,6 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -11644,8 +11745,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -11681,8 +11780,7 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/log-symbols": { "version": "6.0.0", @@ -13373,7 +13471,6 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -13461,8 +13558,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -13775,8 +13870,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -13866,8 +13959,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14273,11 +14364,38 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/proc-log": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", @@ -14570,8 +14688,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -15615,8 +15731,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -15726,6 +15840,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/tailwindcss": { "version": "3.4.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", @@ -16376,7 +16505,6 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -16417,7 +16545,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16426,6 +16553,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/ua-parser-js": { "version": "0.7.41", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", @@ -16837,7 +16987,6 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/webapp/package.json b/webapp/package.json index 15bdfea0..81151bdb 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -70,14 +70,19 @@ "@angular/router": "^20.3.3", "@babylonjs/core": "^8.30.1", "@babylonjs/gui": "^8.30.1", + "@eslint/js": "^9.36.0", + "@stylistic/eslint-plugin": "^5.4.0", "@types/earcut": "^3.0.0", "@types/jasmine": "~5.1.9", "@types/jsoneditor": "^9.9.0", - "@typescript-eslint/eslint-plugin": "^8.45.0", + "angular-eslint": "^20.3.0", "cc-map-editor-common": "file:../common", "earcut": "^3.0.2", "electron": "^38.2.0", "electron-builder": "^26.0.12", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "jasmine-core": "~4.5.0", "jsoneditor": "^9.10.0", "karma": "~6.4.0", @@ -90,14 +95,15 @@ "tailwindcss": "^3.3.1", "tslib": "^2.3.0", "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", "zone.js": "~0.15.1" }, "dependencies": { + "@achrinza/node-ipc": "^10.1.11", "@electron/remote": "^2.1.3", "electron-context-menu": "^4.1.1", "electron-log": "^5.4.3", "electron-updater": "^6.6.2", - "electron-window-state": "^5.0.3", - "@achrinza/node-ipc": "^10.1.11" + "electron-window-state": "^5.0.3" } } diff --git a/webapp/src/app/app-routing.ts b/webapp/src/app/app-routing.ts index 285b9000..aea0866a 100644 --- a/webapp/src/app/app-routing.ts +++ b/webapp/src/app/app-routing.ts @@ -1,6 +1,6 @@ -import {Routes} from '@angular/router'; -import {EditorComponent} from './components/editor/editor.component'; -import {BabylonComponent} from './components/babylon/babylon.component'; +import { Routes } from '@angular/router'; +import { EditorComponent } from './components/editor/editor.component'; +import { BabylonComponent } from './components/babylon/babylon.component'; export const routes: Routes = [ { @@ -10,12 +10,12 @@ export const routes: Routes = [ { path: '', pathMatch: 'full', - redirectTo: '' + redirectTo: '', }, { path: '3d', - component: BabylonComponent - } - ] - } + component: BabylonComponent, + }, + ], + }, ]; diff --git a/webapp/src/app/app.component.ts b/webapp/src/app/app.component.ts index f2b1bb80..514a28de 100644 --- a/webapp/src/app/app.component.ts +++ b/webapp/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, HostListener } from '@angular/core'; +import { Component, HostListener, inject } from '@angular/core'; import { ConfirmCloseComponent } from './components/dialogs/confirm-close/confirm-close.component'; import { OverlayService } from './components/dialogs/overlay/overlay.service'; import { GlobalEventsService } from './services/global-events.service'; @@ -7,25 +7,23 @@ import { GlobalEventsService } from './services/global-events.service'; selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], - standalone: false + standalone: false, }) export class AppComponent { - constructor( - private readonly eventsService: GlobalEventsService, - private readonly overlayService: OverlayService, - ) { - } - + private readonly eventsService = inject(GlobalEventsService); + private readonly overlayService = inject(OverlayService); + @HostListener('window:beforeunload', ['$event']) onUnload($event: any) { if (this.eventsService.hasUnsavedChanges.getValue()) { - $event.returnValue = 'Are you sure you want to discard your changes?'; - + $event.returnValue = + 'Are you sure you want to discard your changes?'; + const dialogRef = this.overlayService.open(ConfirmCloseComponent, { hasBackdrop: true, }); dialogRef.instance.showDevMode = true; - dialogRef.ref.onClose.subscribe(result => { + dialogRef.ref.onClose.subscribe((result) => { if (result) { this.eventsService.hasUnsavedChanges.next(false); window.close(); diff --git a/webapp/src/app/app.module.ts b/webapp/src/app/app.module.ts index c6350c00..b8ee1b33 100644 --- a/webapp/src/app/app.module.ts +++ b/webapp/src/app/app.module.ts @@ -1,7 +1,10 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { OverlayModule } from '@angular/cdk/overlay'; import { ScrollingModule } from '@angular/cdk/scrolling'; -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { + provideHttpClient, + withInterceptorsFromDi, +} from '@angular/common/http'; import { NgModule } from '@angular/core'; import { FlexLayoutModule } from '@angular/flex-layout'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -117,11 +120,8 @@ const WIDGETS = [ SettingsComponent, HistoryComponent, OverlayPanelComponent, - HostDirective, - ModalDirective, JsonEditorComponent, FloatingWindowComponent, - KeepHtmlPipe, ListSearchOverlayComponent, EventWindowComponent, RowTextComponent, @@ -129,7 +129,6 @@ const WIDGETS = [ EnemyTypeWidgetOverlayComponent, ImageSelectOverlayComponent, ConfirmCloseComponent, - ResizedDirective, ...WIDGETS, ], imports: [ @@ -151,12 +150,13 @@ const WIDGETS = [ CombinedTooltipPipe, InputWithButtonComponent, ToolbarDividerComponent, - GridMenuComponent - ], - providers: [ - provideHttpClient(withInterceptorsFromDi()) + GridMenuComponent, + KeepHtmlPipe, + ResizedDirective, + ModalDirective, + HostDirective, ], + providers: [provideHttpClient(withInterceptorsFromDi())], bootstrap: [AppComponent], }) -export class AppModule { -} +export class AppModule {} diff --git a/webapp/src/app/components/babylon/babylon.component.ts b/webapp/src/app/components/babylon/babylon.component.ts index 9f37f0b0..3620dfcc 100644 --- a/webapp/src/app/components/babylon/babylon.component.ts +++ b/webapp/src/app/components/babylon/babylon.component.ts @@ -1,4 +1,12 @@ -import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + OnDestroy, + OnInit, + ViewChild, + inject, +} from '@angular/core'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { BabylonViewerService } from '../../services/3d/babylon-viewer.service'; @@ -9,34 +17,38 @@ import { Globals } from '../../services/globals'; selector: 'app-babylon', templateUrl: './babylon.component.html', styleUrls: ['./babylon.component.scss'], - standalone: false + standalone: false, }) export class BabylonComponent implements OnInit, AfterViewInit, OnDestroy { - - @ViewChild('renderCanvas', {static: true}) canvas!: ElementRef; - + private router = inject(Router); + private globalEvents = inject(GlobalEventsService); + private viewer = inject(BabylonViewerService); + + @ViewChild('renderCanvas', { static: true }) + canvas!: ElementRef; + private sub: Subscription; - + loading = false; - - constructor( - private router: Router, - private globalEvents: GlobalEventsService, - private viewer: BabylonViewerService, - ) { - this.sub = globalEvents.babylonLoading.subscribe(val => this.loading = val); + + constructor() { + const globalEvents = this.globalEvents; + + this.sub = globalEvents.babylonLoading.subscribe( + (val) => (this.loading = val), + ); } - + ngOnInit() { // if phaser is not initialized, move away from 3d if (!Globals.scene.cameras) { - this.router.navigate(['/']); + void this.router.navigate(['/']); return; } this.globalEvents.babylonLoading.next(true); this.globalEvents.is3D.next(true); } - + ngAfterViewInit() { if (Globals.scene.cameras) { // timeout needed because babylon would initialize with wrong width/height and would need a resize @@ -45,11 +57,10 @@ export class BabylonComponent implements OnInit, AfterViewInit, OnDestroy { }, 0); } } - + ngOnDestroy() { this.viewer.onDestroy(); this.sub.unsubscribe(); this.globalEvents.is3D.next(false); } - } diff --git a/webapp/src/app/components/captions/captions.component.html b/webapp/src/app/components/captions/captions.component.html index 6f095fdf..6556c114 100644 --- a/webapp/src/app/components/captions/captions.component.html +++ b/webapp/src/app/components/captions/captions.component.html @@ -1,12 +1,10 @@

{{ version }}

@for (el of uiElements; track el) { - @if (el.active) { - {{ el.text }} - + {{ el.text }} + } - }

diff --git a/webapp/src/app/components/captions/captions.component.ts b/webapp/src/app/components/captions/captions.component.ts index e067def2..e721a1c1 100644 --- a/webapp/src/app/components/captions/captions.component.ts +++ b/webapp/src/app/components/captions/captions.component.ts @@ -11,34 +11,36 @@ export interface BottomUiElement { selector: 'app-captions', templateUrl: './captions.component.html', styleUrls: ['./captions.component.scss'], - standalone: false + standalone: false, }) export class CaptionsComponent implements OnInit { version = environment.version; coords: BottomUiElement = {}; selectionSize: BottomUiElement = {}; autotile: BottomUiElement = { - text: 'Autotile' + text: 'Autotile', }; - + uiElements: BottomUiElement[] = [ this.coords, this.selectionSize, this.autotile, ]; - + ngOnInit(): void { - Globals.globalEventsService.updateCoords.subscribe(coords => { + Globals.globalEventsService.updateCoords.subscribe((coords) => { this.coords.text = `(${coords?.x}, ${coords?.y}, ${coords?.z})`; this.coords.active = !!coords; }); - - Globals.globalEventsService.updateTileSelectionSize.subscribe(size => { - this.selectionSize.text = `${size?.x}x${size?.y}`; - this.selectionSize.active = !!size; - }); - - Globals.globalEventsService.isAutotile.subscribe(show => { + + Globals.globalEventsService.updateTileSelectionSize.subscribe( + (size) => { + this.selectionSize.text = `${size?.x}x${size?.y}`; + this.selectionSize.active = !!size; + }, + ); + + Globals.globalEventsService.isAutotile.subscribe((show) => { this.autotile.active = show; }); } diff --git a/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.html b/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.html index e21379f4..4ae88a3a 100644 --- a/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.html +++ b/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.html @@ -1,10 +1,12 @@ +> +
Are you sure you want to discard your changes? @@ -12,10 +14,15 @@ @if (showDevMode && devMode) { - } - - + + diff --git a/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.ts b/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.ts index 52edf798..658bfee0 100644 --- a/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.ts +++ b/webapp/src/app/components/dialogs/confirm-close/confirm-close.component.ts @@ -1,21 +1,19 @@ -import { Component, isDevMode } from '@angular/core'; +import { Component, isDevMode, inject } from '@angular/core'; import { OverlayRefControl } from '../overlay/overlay-ref-control'; @Component({ selector: 'app-confirm-close', templateUrl: './confirm-close.component.html', styleUrls: ['./confirm-close.component.scss'], - standalone: false + standalone: false, }) export class ConfirmCloseComponent { + private readonly ref = + inject>(OverlayRefControl); + protected readonly devMode = isDevMode(); public showDevMode = false; - - constructor( - private readonly ref: OverlayRefControl, - ) { - } - + protected close(result: boolean | null) { this.ref.close(result); } diff --git a/webapp/src/app/components/dialogs/floating-window/floating-window.component.html b/webapp/src/app/components/dialogs/floating-window/floating-window.component.html index 9d673447..26bfdfbd 100644 --- a/webapp/src/app/components/dialogs/floating-window/floating-window.component.html +++ b/webapp/src/app/components/dialogs/floating-window/floating-window.component.html @@ -1,23 +1,45 @@ -
- {{ title }} + {{ title }} @if (showMin) { - - } @if (showClose) { - - } + } + @if (showClose) { + + }
- +
diff --git a/webapp/src/app/components/dialogs/floating-window/floating-window.component.ts b/webapp/src/app/components/dialogs/floating-window/floating-window.component.ts index 64ae27a8..b0d517bb 100644 --- a/webapp/src/app/components/dialogs/floating-window/floating-window.component.ts +++ b/webapp/src/app/components/dialogs/floating-window/floating-window.component.ts @@ -1,63 +1,68 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, +} from '@angular/core'; @Component({ selector: 'app-floating-window', templateUrl: './floating-window.component.html', styleUrls: ['./floating-window.component.scss'], - standalone: false + standalone: false, }) export class FloatingWindowComponent implements OnInit, OnChanges { - @Input() visible = true; @Input() title = 'no title'; - + @Input() height = ''; @Input() width = ''; @Input() top: string | number = 0; @Input() right: string | number = 0; - + @Input() showClose = false; @Input() showMin = true; - + @Output() close = new EventEmitter(); @Output() dragEnd = new EventEmitter(); - + contentStyle = { height: this.height, - display: '' + display: '', }; - - constructor() { - } - + + constructor() {} + ngOnInit() { this.updateContentStyle(); } - + ngOnChanges(changes: SimpleChanges): void { if (changes['visible']) { this.updateContentStyle(); } } - + private updateContentStyle() { this.contentStyle = { height: this.visible ? this.height : '0', - display: this.visible ? 'inherit' : 'none' + display: this.visible ? 'inherit' : 'none', }; } - + onDragEnd() { this.dragEnd.emit(); } - + toggle() { this.visible = !this.visible; this.updateContentStyle(); } - + onClose() { this.close.emit(); } - } diff --git a/webapp/src/app/components/dialogs/floating-window/history/history.component.html b/webapp/src/app/components/dialogs/floating-window/history/history.component.html index 44dc8bba..49223224 100644 --- a/webapp/src/app/components/dialogs/floating-window/history/history.component.html +++ b/webapp/src/app/components/dialogs/floating-window/history/history.component.html @@ -9,15 +9,24 @@
@for (state of states; track state; let i = $index) { - }
diff --git a/webapp/src/app/components/dialogs/floating-window/history/history.component.ts b/webapp/src/app/components/dialogs/floating-window/history/history.component.ts index 364ff7a4..516362e8 100644 --- a/webapp/src/app/components/dialogs/floating-window/history/history.component.ts +++ b/webapp/src/app/components/dialogs/floating-window/history/history.component.ts @@ -1,33 +1,46 @@ -import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { + Component, + ElementRef, + OnDestroy, + OnInit, + ViewChild, + inject, +} from '@angular/core'; import { EventManager } from '@angular/platform-browser'; import { NavigationStart, Router } from '@angular/router'; import { Helper } from '../../../../services/phaser/helper'; -import { HistoryState, HistoryStateContainer, StateHistoryService } from './state-history.service'; +import { + HistoryState, + HistoryStateContainer, + StateHistoryService, +} from './state-history.service'; @Component({ selector: 'app-history', templateUrl: './history.component.html', styleUrls: ['./history.component.scss'], - standalone: false + standalone: false, }) export class HistoryComponent implements OnInit, OnDestroy { - - @ViewChild('listContainer', {static: false}) list?: ElementRef; - + private stateHistory = inject(StateHistoryService); + private eventManager = inject(EventManager); + + @ViewChild('listContainer', { static: false }) list?: ElementRef; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type private eventHandler?: Function; - + states: HistoryState[] = []; selected?: HistoryStateContainer; selectedIndex = 0; hide = false; - - constructor( - private stateHistory: StateHistoryService, - private eventManager: EventManager, - router: Router, - ) { - stateHistory.states.subscribe(states => { + + constructor() { + const stateHistory = this.stateHistory; + const router = inject(Router); + + stateHistory.states.subscribe((states) => { this.states = states; this.updateSelected(this.selected); setTimeout(() => { @@ -38,41 +51,45 @@ export class HistoryComponent implements OnInit, OnDestroy { el.scrollTop = el.scrollHeight * 2; }, 0); }); - stateHistory.selectedState.subscribe(container => { + stateHistory.selectedState.subscribe((container) => { this.updateSelected(container); }); - + // TODO: floating windows should be handled globally - router.events.subscribe(event => { + router.events.subscribe((event) => { if (event instanceof NavigationStart) { this.hide = event.url === '/3d'; } }); } - + ngOnInit() { - this.eventHandler = this.eventManager.addEventListener(document as any, 'keydown', (event: KeyboardEvent) => { - if (Helper.isInputFocused()) { - return; - } - if (event.ctrlKey && event.key.toLowerCase() === 'z') { - event.preventDefault(); - if (event.shiftKey) { - this.redo(); - } else { - this.undo(); + this.eventHandler = this.eventManager.addEventListener( + document as any, + 'keydown', + (event: KeyboardEvent) => { + if (Helper.isInputFocused()) { + return; } - } - }); + if (event.ctrlKey && event.key.toLowerCase() === 'z') { + event.preventDefault(); + if (event.shiftKey) { + this.redo(); + } else { + this.undo(); + } + } + }, + ); } - + ngOnDestroy(): void { if (this.eventHandler) { this.eventHandler(); this.eventHandler = undefined; } } - + updateSelected(container?: HistoryStateContainer) { if (!container) { return; @@ -80,17 +97,16 @@ export class HistoryComponent implements OnInit, OnDestroy { this.selected = container; this.selectedIndex = this.states.indexOf(container.state!); } - + undo() { this.stateHistory.undo(); } - + redo() { this.stateHistory.redo(); } - + selectState(state: HistoryState) { - this.stateHistory.selectedState.next({state: state}); + this.stateHistory.selectedState.next({ state: state }); } - } diff --git a/webapp/src/app/components/dialogs/floating-window/history/state-history.service.ts b/webapp/src/app/components/dialogs/floating-window/history/state-history.service.ts index b55137d8..8d8151f2 100644 --- a/webapp/src/app/components/dialogs/floating-window/history/state-history.service.ts +++ b/webapp/src/app/components/dialogs/floating-window/history/state-history.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { GlobalEventsService } from '../../../../services/global-events.service'; @@ -15,29 +15,31 @@ export interface HistoryState { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class StateHistoryService { + private readonly eventsService = inject(GlobalEventsService); + maxStates = 100; - + states = new BehaviorSubject([]); - selectedState = new BehaviorSubject({state: undefined}); - - constructor( - private readonly eventsService: GlobalEventsService - ) { - } - + selectedState = new BehaviorSubject({ + state: undefined, + }); + init(state: HistoryState) { this.selectedState.value.state = state; this.states.next([state]); } - - saveState(state: { - icon: string; - name: string; - json?: string; - }, ignoreCheck = false) { + + saveState( + state: { + icon: string; + name: string; + json?: string; + }, + ignoreCheck = false, + ) { if (!state.json) { const newState = Globals.map.exportMap(); const stateJson = JSON.stringify(newState); @@ -51,7 +53,7 @@ export class StateHistoryService { } this.eventsService.hasUnsavedChanges.next(true); - + const states = this.states.getValue(); const selected = this.selectedState.getValue(); const i = states.indexOf(selected.state!); @@ -63,7 +65,7 @@ export class StateHistoryService { states.push(selected.state); this.states.next(states); } - + undo() { const states = this.states.getValue(); const selected = this.selectedState.getValue(); @@ -72,9 +74,9 @@ export class StateHistoryService { return; } i--; - this.selectedState.next({state: states[i]}); + this.selectedState.next({ state: states[i] }); } - + redo() { const states = this.states.getValue(); const selected = this.selectedState.getValue(); @@ -83,6 +85,6 @@ export class StateHistoryService { return; } i++; - this.selectedState.next({state: states[i]}); + this.selectedState.next({ state: states[i] }); } } diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.html b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.html index e4cb3fad..1b1a4ecc 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.html +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.html @@ -1,8 +1,10 @@ - +
diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.ts index 2fc3b08c..a0176ea8 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, HostListener } from '@angular/core'; +import { AfterViewInit, Component, HostListener, inject } from '@angular/core'; import { NavigationStart, Router } from '@angular/router'; import * as Phaser from 'phaser'; @@ -6,32 +6,33 @@ import { EditorView } from '../../../../models/editor-view'; import { GlobalEventsService } from '../../../../services/global-events.service'; import { TileSelectorScene } from './tile-selector.scene'; - @Component({ selector: 'app-tile-selector', templateUrl: './tile-selector.component.html', styleUrls: ['./tile-selector.component.scss'], - standalone: false + standalone: false, }) export class TileSelectorComponent implements AfterViewInit { private display?: Phaser.Game; private scene?: TileSelectorScene; hide = false; - - constructor( - globalEvents: GlobalEventsService, - router: Router - ) { - globalEvents.currentView.subscribe(view => this.hide = view !== EditorView.Layers); - + + constructor() { + const globalEvents = inject(GlobalEventsService); + const router = inject(Router); + + globalEvents.currentView.subscribe( + (view) => (this.hide = view !== EditorView.Layers), + ); + // TODO: floating windows should be handled globally - router.events.subscribe(event => { + router.events.subscribe((event) => { if (event instanceof NavigationStart) { this.hide = event.url === '/3d'; } }); } - + ngAfterViewInit() { this.scene = new TileSelectorScene(); this.display = new Phaser.Game({ @@ -41,16 +42,16 @@ export class TileSelectorComponent implements AfterViewInit { parent: 'tile-selector-content', scale: { mode: Phaser.Scale.ScaleModes.NONE, - zoom: 1 / window.devicePixelRatio + zoom: 1 / window.devicePixelRatio, }, render: { - pixelArt: true + pixelArt: true, }, zoom: 1, - scene: [this.scene] + scene: [this.scene], }); } - + @HostListener('window:resize', ['$event']) onResize(event: Event) { if (!this.display) { @@ -58,12 +59,12 @@ export class TileSelectorComponent implements AfterViewInit { } this.display.scale.refresh(); } - + onDragEnd() { if (!this.display || !this.scene) { return; } - + this.scene.resize(); } } diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index 3de8984b..96ef8eed 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -8,72 +8,91 @@ import { customPutTilesAt } from '../../../../services/phaser/tilemap/layer-help import { BaseTileDrawer } from '../../../../services/phaser/BaseTileDrawer'; export class TileSelectorScene extends Phaser.Scene { - private tileMap!: Phaser.Tilemaps.Tilemap; private subs: Subscription[] = []; - + private baseDrawer!: BaseTileDrawer; private tilesetWidth = 0; - + private tilemapLayer!: CCMapLayer; - + constructor() { - super({key: 'main'}); + super({ key: 'main' }); } - + async create() { - this.baseDrawer = new BaseTileDrawer(this, true); this.baseDrawer.resetSelectedTiles(); this.add.existing(this.baseDrawer); - + this.cameras.main.setBackgroundColor('#616161'); - + this.game.canvas.oncontextmenu = function (e) { e.preventDefault(); }; - - this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(async layer => { - const success = await this.drawTileset(layer); - if (!success) { - this.tileMap.removeAllLayers(); - await this.baseDrawer.setLayer(); - } - })); - - this.subs.push(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => { - this.baseDrawer.drawRect(0, 0); - if (tiles.length === 0) { - return; - } - const baseTile = tiles[0]; - - if (baseTile.id === 0) { - return; - } - let width = 0; - let height = 0; - - // If the selection is a continuous rectangle in the tile selector, highlight it - for (const tile of tiles) { - const id = tile.id - tile.offset.x - tile.offset.y * this.tilesetWidth; - if (baseTile.id !== id) { - return; + + this.subs.push( + Globals.mapLoaderService.selectedLayer.subscribe(async (layer) => { + const success = await this.drawTileset(layer); + if (!success) { + this.tileMap.removeAllLayers(); + await this.baseDrawer.setLayer(); } - width = Math.max(width, tile.offset.x); - height = Math.max(height, tile.offset.y); - } - - const start = Helper.indexToPoint(baseTile.id, this.tilesetWidth); - this.baseDrawer.drawRect(width + 1, height + 1, start.x * Globals.TILE_SIZE, start.y * Globals.TILE_SIZE); - })); - + }), + ); + + this.subs.push( + Globals.phaserEventsService.changeSelectedTiles.subscribe( + (tiles) => { + this.baseDrawer.drawRect(0, 0); + if (tiles.length === 0) { + return; + } + const baseTile = tiles[0]; + + if (baseTile.id === 0) { + return; + } + let width = 0; + let height = 0; + + // If the selection is a continuous rectangle in the tile selector, highlight it + for (const tile of tiles) { + const id = + tile.id - + tile.offset.x - + tile.offset.y * this.tilesetWidth; + if (baseTile.id !== id) { + return; + } + width = Math.max(width, tile.offset.x); + height = Math.max(height, tile.offset.y); + } + + const start = Helper.indexToPoint( + baseTile.id, + this.tilesetWidth, + ); + this.baseDrawer.drawRect( + width + 1, + height + 1, + start.x * Globals.TILE_SIZE, + start.y * Globals.TILE_SIZE, + ); + }, + ), + ); + const pan = new MapPan(this, 'mapPan'); this.add.existing(pan); - - this.tileMap = this.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); + + this.tileMap = this.add.tilemap( + undefined, + Globals.TILE_SIZE, + Globals.TILE_SIZE, + ); this.tilemapLayer = new CCMapLayer(this.tileMap); - + await this.tilemapLayer.init({ type: 'Background', name: 'fromPhaser', @@ -85,43 +104,58 @@ export class TileSelectorScene extends Phaser.Scene { repeat: false, distance: 0, tilesize: Globals.TILE_SIZE, - moveSpeed: {x: 0, y: 0}, - data: [] + moveSpeed: { x: 0, y: 0 }, + data: [], }); } - + public resize() { const size = this.scale.gameSize; this.game.scale.resize(size.width, size.height); } - + destroy() { for (const sub of this.subs) { sub.unsubscribe(); } this.subs = []; } - + private async drawTileset(selectedLayer?: CCMapLayer): Promise { if (!selectedLayer?.details.tilesetName) { return false; } - - const exists = await Helper.loadTexture(selectedLayer.details.tilesetName, this); + + const exists = await Helper.loadTexture( + selectedLayer.details.tilesetName, + this, + ); if (!exists) { return false; } - - const tilesetSize = Helper.getTilesetSize(this, selectedLayer.details.tilesetName); + + const tilesetSize = Helper.getTilesetSize( + this, + selectedLayer.details.tilesetName, + ); this.tilesetWidth = tilesetSize.x; this.tileMap.removeAllLayers(); - const tileset = this.tileMap.addTilesetImage(selectedLayer.details.tilesetName); + const tileset = this.tileMap.addTilesetImage( + selectedLayer.details.tilesetName, + ); if (!tileset) { return false; } tileset.firstgid = 1; - const layer = this.tileMap.createBlankLayer('first', tileset, 0, 0, tilesetSize.x, tilesetSize.y)!; - + const layer = this.tileMap.createBlankLayer( + 'first', + tileset, + 0, + 0, + tilesetSize.x, + tilesetSize.y, + )!; + let counter = 1; const data: number[][] = []; for (let y = 0; y < tilesetSize.y; y++) { @@ -131,11 +165,11 @@ export class TileSelectorScene extends Phaser.Scene { counter++; } } - + customPutTilesAt(data, layer); this.tilemapLayer.setPhaserLayer(layer); await this.baseDrawer.setLayer(this.tilemapLayer); - + return true; } } diff --git a/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.html b/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.html index fe00cb49..12b59b18 100644 --- a/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.html +++ b/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.html @@ -1,36 +1,48 @@ - - - + - + - diff --git a/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.ts b/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.ts index fa4ebae7..26199211 100644 --- a/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.ts +++ b/webapp/src/app/components/dialogs/list-search-overlay/list-search-overlay.component.ts @@ -1,5 +1,20 @@ -import { animate, state, style, transition, trigger } from '@angular/animations'; -import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + animate, + state, + style, + transition, + trigger, +} from '@angular/animations'; +import { + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + inject, +} from '@angular/core'; import { SearchFilterService } from '../../../services/search-filter.service'; @@ -18,66 +33,67 @@ const ANIMATION_TIMING = '300ms cubic-bezier(0.25, 0.8, 0.25, 1)'; transition('* => slide', [ style({ transform: 'translate(80px, 0)', - opacity: 0 + opacity: 0, }), - animate(ANIMATION_TIMING) + animate(ANIMATION_TIMING), ]), - state('scale', style({'transform-origin': '0 0 0'})), + state('scale', style({ 'transform-origin': '0 0 0' })), transition('* => scale', [ style({ 'transform-origin': '0 0 0', - transform: 'scale(0, 0)' + transform: 'scale(0, 0)', }), - animate(ANIMATION_TIMING) + animate(ANIMATION_TIMING), ]), - ]) + ]), ], selector: 'app-list-search-overlay', templateUrl: './list-search-overlay.component.html', styleUrls: ['./list-search-overlay.component.scss'], - standalone: false + standalone: false, }) export class ListSearchOverlayComponent implements OnInit { - @ViewChild('filterInput', {static: true}) filterInput!: ElementRef; - + private searchFilterService = inject(SearchFilterService); + + @ViewChild('filterInput', { static: true }) + filterInput!: ElementRef; + @Input() list: string[] = []; @Input() animation: 'slide' | 'scale' | 'none' = 'slide'; @Output() selected = new EventEmitter(); - + currentIndex = 0; filteredList: string[] = []; - + private _filterText = ' '; - + get filterText() { return this._filterText; } - + set filterText(text) { if (text === this._filterText) { return; } - + this.currentIndex = 0; this._filterText = text; - - this.filteredList = this.searchFilterService.filterOptions(this.list, text); - } - - constructor( - private searchFilterService: SearchFilterService, - ) { + + this.filteredList = this.searchFilterService.filterOptions( + this.list, + text, + ); } - + ngOnInit() { this.filterInput.nativeElement.focus(); this.filterText = ''; } - + select(item: string) { this.selected.emit(item); } - + onKeyDown(event: KeyboardEvent) { if (event.key === 'Enter') { const item = this.filteredList[this.currentIndex]; @@ -86,7 +102,7 @@ export class ListSearchOverlayComponent implements OnInit { } return; } - + if (event.key === 'ArrowDown') { this.currentIndex += 1; this.currentIndex %= this.filteredList.length; diff --git a/webapp/src/app/components/dialogs/load-map/load-map.component.html b/webapp/src/app/components/dialogs/load-map/load-map.component.html index 2b55bc6b..ff5d39a6 100644 --- a/webapp/src/app/components/dialogs/load-map/load-map.component.html +++ b/webapp/src/app/components/dialogs/load-map/load-map.component.html @@ -16,50 +16,95 @@
- +
- - + +
- +
@if (!loading) { - +
  • -
  • - - + +
  • - @if (treeControl.isExpanded(node)) { @@ -71,12 +116,9 @@ } @else { - -
    - -
    - - } - +
    + +
    + }
  • diff --git a/webapp/src/app/components/dialogs/load-map/load-map.component.ts b/webapp/src/app/components/dialogs/load-map/load-map.component.ts index 6356fe57..3a9ae975 100644 --- a/webapp/src/app/components/dialogs/load-map/load-map.component.ts +++ b/webapp/src/app/components/dialogs/load-map/load-map.component.ts @@ -1,5 +1,13 @@ import { NestedTreeControl } from '@angular/cdk/tree'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + Input, + ViewChild, + inject, +} from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { MatTreeNestedDataSource } from '@angular/material/tree'; @@ -13,59 +21,59 @@ import { OverlayService } from '../overlay/overlay.service'; import { MapNode, MapNodeRoot } from './mapNode.model'; import { VirtualMapNode } from './virtualMapNode.model'; - @Component({ selector: 'app-load-map', templateUrl: './load-map.component.html', styleUrls: ['./load-map.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false + standalone: false, }) export class LoadMapComponent { - - @ViewChild('fileUpload', {static: true}) + private mapLoader = inject(MapLoaderService); + private http = inject(HttpClientService); + private ref = inject(ChangeDetectorRef); + private searchFilterService = inject(SearchFilterService); + private readonly eventsService = inject(GlobalEventsService); + private readonly overlayService = inject(OverlayService); + + @ViewChild('fileUpload', { static: true }) fileUpload!: ElementRef; - - @ViewChild('filterInput', {static: true}) + + @ViewChild('filterInput', { static: true }) filterInput!: ElementRef; - + @Input() sidenav!: MatSidenav; - + loading = false; - - treeControl = new NestedTreeControl(node => node.children); + + treeControl = new NestedTreeControl( + (node) => node.children, + ); mapsSource = new MatTreeNestedDataSource(); - - root: MapNodeRoot = {name: '', displayed: true, children: []}; // The root itself is never displayed. It is used as a datasource for virtualRoot. + + root: MapNodeRoot = { name: '', displayed: true, children: [] }; // The root itself is never displayed. It is used as a datasource for virtualRoot. virtualRoot = new VirtualMapNode(this.root); // To reuse the children filtering. filter = ''; - - constructor( - private mapLoader: MapLoaderService, - private http: HttpClientService, - private ref: ChangeDetectorRef, - private searchFilterService: SearchFilterService, - private readonly eventsService: GlobalEventsService, - private readonly overlayService: OverlayService - ) { + + constructor() { this.mapsSource.data = []; this.refresh(); } - + focusInput() { this.filterInput.nativeElement.focus(); } - + refresh() { this.loading = true; - this.http.getMaps().subscribe(paths => { + this.http.getMaps().subscribe((paths) => { this.loading = false; this.displayMaps(paths); this.update(); }); } - + update() { for (const node of this.root.children) { this.filterNode(node, this.filter); @@ -74,80 +82,90 @@ export class LoadMapComponent { this.mapsSource.data = this.virtualRoot.children || []; this.ref.detectChanges(); } - + private async showConfirmDialog() { - const hasUnsavedChanges = await firstValueFrom(this.eventsService.hasUnsavedChanges); + const hasUnsavedChanges = await firstValueFrom( + this.eventsService.hasUnsavedChanges, + ); if (!hasUnsavedChanges) { return true; } - + const dialogRef = this.overlayService.open(ConfirmCloseComponent, { hasBackdrop: true, }); - const result = await firstValueFrom(dialogRef.ref.onClose, {defaultValue: false}); + const result = await firstValueFrom(dialogRef.ref.onClose, { + defaultValue: false, + }); if (result) { this.eventsService.hasUnsavedChanges.next(false); } return result; } - + async loadMap(event: Event) { - if (!await this.showConfirmDialog()) { + if (!(await this.showConfirmDialog())) { return; } this.mapLoader.loadMap(event); this.fileUpload.nativeElement.value = ''; } - + async load(name: string) { - if (!await this.showConfirmDialog()) { + if (!(await this.showConfirmDialog())) { return; } this.mapLoader.loadMapByName(name); } - + hasChild(_: number, node: VirtualMapNode) { return node.children !== undefined; } - + close() { return this.sidenav.close(); } - + private displayMaps(paths: string[]) { const data: MapNode[] = []; - + let lastPath = ''; let lastNode = data; for (const path of paths) { const node = this.resolve(data, path, lastNode, lastPath); const name = path.substring(path.lastIndexOf('.') + 1); - - node.push({name, path, displayed: true}); - + + node.push({ name, path, displayed: true }); + lastPath = path; lastNode = node; } - + this.root.children = data; } - - private resolve(data: MapNode[], path: string, lastNode: MapNode[], lastPath: string): MapNode[] { - if (path.substring(0, path.lastIndexOf('.')) === lastPath.substring(0, lastPath.lastIndexOf('.'))) { + + private resolve( + data: MapNode[], + path: string, + lastNode: MapNode[], + lastPath: string, + ): MapNode[] { + if ( + path.substring(0, path.lastIndexOf('.')) === + lastPath.substring(0, lastPath.lastIndexOf('.')) + ) { return lastNode; } - + if (!path.includes('.')) { return data; } - + let node = data; - - const parts = path - .substring(0, path.lastIndexOf('.')) - .split('.'); + + const parts = path.substring(0, path.lastIndexOf('.')).split('.'); for (const name of parts) { - const child = node.find(n => n.name === name); + const child = node.find((n) => n.name === name); if (child && child.children) { node = child.children; } else { @@ -163,35 +181,35 @@ export class LoadMapComponent { } return node; } - + private filterNode(node: MapNode, filter: string): boolean { if (this.searchFilterService.test(node.name, filter)) { node.displayed = true; this.displayChildren(node); return true; } - + if (!node.children) { node.displayed = false; return false; } - + let displayed = false; for (const child of node.children) { if (this.filterNode(child, filter)) { displayed = true; } } - + node.displayed = displayed; return displayed; } - + private displayChildren(node: MapNode) { if (!node.children) { return; } - + for (const child of node.children) { child.displayed = true; this.displayChildren(child); diff --git a/webapp/src/app/components/dialogs/load-map/virtualMapNode.model.ts b/webapp/src/app/components/dialogs/load-map/virtualMapNode.model.ts index f708cbf7..8247c008 100644 --- a/webapp/src/app/components/dialogs/load-map/virtualMapNode.model.ts +++ b/webapp/src/app/components/dialogs/load-map/virtualMapNode.model.ts @@ -6,7 +6,7 @@ import { MapNode } from './mapNode.model'; export class VirtualMapNode { private original: MapNode; private knownChildren = new WeakMap(); - + public constructor(original: MapNode) { this.original = original; @@ -57,10 +57,12 @@ export class VirtualMapNode { private get containsSingleDirectory(): boolean { const realChildren = this.realChildren; - return realChildren !== undefined - && realChildren.length === 1 - && realChildren[0].isDirectory - && !this.isRoot; + return ( + realChildren !== undefined && + realChildren.length === 1 && + realChildren[0].isDirectory && + !this.isRoot + ); } private get realChildren(): VirtualMapNode[] | undefined { @@ -69,9 +71,9 @@ export class VirtualMapNode { } return this.original.children - .filter(n => n.displayed) + .filter((n) => n.displayed) .sort((a, b) => this.sort(a, b)) - .map(n => this.resolve(n)); + .map((n) => this.resolve(n)); } private sort(a: MapNode, b: MapNode): number { @@ -82,6 +84,6 @@ export class VirtualMapNode { return aIsDir ? -1 : 1; } - return a.name.localeCompare(b.name, undefined, {numeric: true}); + return a.name.localeCompare(b.name, undefined, { numeric: true }); } } diff --git a/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.html b/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.html index 3ecdc4ba..05db6243 100644 --- a/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.html +++ b/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.html @@ -2,23 +2,37 @@
    Map Name: - +
    -
    - +
    +
    Map Size: width: - + height: - +
    - -
    - + +
    +
    Levels: @@ -32,26 +46,38 @@ class="default-input grow" [ngModel]="level.height" (ngModelChange)="level.height = +$event" - > + /> @if (settings.levels.length > 1) { - }
    }
    - +
    - + -
    +
    Master Level:
    - + @for (level of settings.levels; track level; let i = $index) { {{ i }} @@ -60,22 +86,28 @@
    -
    - +
    +
    cameraInBounds: - +
    - + @for (prop of mapSettings | keyvalue; track prop) {
    - +
    }
    diff --git a/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.ts b/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.ts index 16815176..62b75ce9 100644 --- a/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.ts +++ b/webapp/src/app/components/dialogs/map-settings/map-content-settings/map-content-settings.component.ts @@ -6,11 +6,11 @@ import { CrossCodeMap } from '../../../../models/cross-code-map'; selector: 'app-map-content-settings', templateUrl: './map-content-settings.component.html', styleUrls: ['./map-content-settings.component.scss'], - standalone: false + standalone: false, }) export class MapContentSettingsComponent implements OnInit { @Input() settings!: CrossCodeMap; - + // TODO: why is there an output? // All other components using this event just set the setting to the object, // this could be handled here without emitting an event @@ -19,50 +19,47 @@ export class MapContentSettingsComponent implements OnInit { value: any; }>(); mapSettings = mapSettingsjson; - - constructor() { - } - + + constructor() {} + ngOnInit() { - if (this.settings.levels.length < 1) { this.settings.levels.push({ - height: 0 + height: 0, }); } } - + onNumberChange(event: Event, property: string): void { const numElement = event.target as HTMLInputElement; if (numElement) { const min = Number(numElement.min || -Infinity); const max = Number(numElement.max || Infinity); let value = Number(numElement.value); - - + if (isNaN(value)) { value = min; } else if (!Number.isInteger(value)) { value = Math.round(value); } - + if (value < min) { value = min; } else if (value > max) { value = max; } - + // Parent won't update field if same value // force update value property numElement.value = value + ''; - + this.onSettingsChange.emit({ property, - value + value, }); } } - + guessHeight(): number { const levels = this.settings.levels; if (levels.length === 0) { diff --git a/webapp/src/app/components/dialogs/map-settings/map-settings.component.html b/webapp/src/app/components/dialogs/map-settings/map-settings.component.html index 268923d7..788a2031 100644 --- a/webapp/src/app/components/dialogs/map-settings/map-settings.component.html +++ b/webapp/src/app/components/dialogs/map-settings/map-settings.component.html @@ -1,6 +1,9 @@ - + diff --git a/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts b/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts index 6a1cfb4e..29296680 100644 --- a/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts +++ b/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { CrossCodeMap } from '../../../models/cross-code-map'; import { MapLoaderService } from '../../../services/map-loader.service'; @@ -10,62 +10,67 @@ import { GlobalEventsService } from '../../../services/global-events.service'; selector: 'app-map-settings', templateUrl: './map-settings.component.html', styleUrls: ['./map-settings.component.scss'], - standalone: false + standalone: false, }) export class MapSettingsComponent { - + ref = inject(OverlayRefControl); + private events = inject(GlobalEventsService); + private readonly tileMap: CCMap; - settings: CrossCodeMap = { - levels: [{height: -32}, {height: 0}, {height: 32}, {height: 64}], + settings: CrossCodeMap = { + levels: [ + { height: -32 }, + { height: 0 }, + { height: 32 }, + { height: 64 }, + ], attributes: {}, - }; - - constructor( - loader: MapLoaderService, - public ref: OverlayRefControl, - private events: GlobalEventsService - ) { + } as any; + + constructor() { + const loader = inject(MapLoaderService); + const tileMap = loader.tileMap.getValue(); - + if (!tileMap) { throw new Error('tilemap not defined'); } - + this.tileMap = tileMap; - + const settings = this.settings; - + settings.name = tileMap.name; - + settings.mapWidth = tileMap.mapWidth; settings.mapHeight = tileMap.mapHeight; settings.levels = tileMap.levels; settings.masterLevel = tileMap.masterLevel; settings.attributes = tileMap.attributes; } - - onSettingsChange(obj: { property: keyof CrossCodeMap, value: any }) { + + onSettingsChange(obj: { property: keyof CrossCodeMap; value: any }) { (this.settings[obj.property] as any) = obj.value; } - + update() { // TODO: add validation const settings = this.settings; const tileMap = this.tileMap; - + tileMap.name = settings.name; tileMap.filename = settings.name; tileMap.levels = settings.levels; tileMap.masterLevel = settings.masterLevel; tileMap.attributes = settings.attributes; - + this.events.resizeMap.next({ x: settings.mapWidth, - y: settings.mapHeight + y: settings.mapHeight, }); - + this.events.updateEntities.next(true); - + this.ref.close(); } } diff --git a/webapp/src/app/components/dialogs/new-map/new-map.component.html b/webapp/src/app/components/dialogs/new-map/new-map.component.html index 4872a968..c74331f3 100644 --- a/webapp/src/app/components/dialogs/new-map/new-map.component.html +++ b/webapp/src/app/components/dialogs/new-map/new-map.component.html @@ -1,6 +1,9 @@ - + diff --git a/webapp/src/app/components/dialogs/new-map/new-map.component.ts b/webapp/src/app/components/dialogs/new-map/new-map.component.ts index 69bbba87..c717b25f 100644 --- a/webapp/src/app/components/dialogs/new-map/new-map.component.ts +++ b/webapp/src/app/components/dialogs/new-map/new-map.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { CrossCodeMap } from '../../../models/cross-code-map'; import { MapLoaderService } from '../../../services/map-loader.service'; @@ -8,27 +8,32 @@ import { OverlayRefControl } from '../overlay/overlay-ref-control'; selector: 'app-new-map', templateUrl: './new-map.component.html', styleUrls: ['./new-map.component.scss'], - standalone: false + standalone: false, }) export class NewMapComponent { + private mapLoader = inject(MapLoaderService); + ref = inject(OverlayRefControl); + map: CrossCodeMap; - - constructor(private mapLoader: MapLoaderService, public ref: OverlayRefControl) { + + constructor() { this.map = this.createDefaultMap(); } - - onSettingsChange(obj: { property: keyof CrossCodeMap, value: any }) { + + onSettingsChange(obj: { property: keyof CrossCodeMap; value: any }) { (this.map[obj.property] as any) = obj.value; } - + createDefaultMap(): CrossCodeMap { return { mapWidth: 1, mapHeight: 1, name: 'Untitled', - levels: [{ - height: 0 - }], + levels: [ + { + height: 0, + }, + ], masterLevel: 0, entities: [], layer: [], @@ -40,19 +45,22 @@ export class NewMapComponent { mapStyle: '', weather: '', npcRunners: '', - area: '' + area: '', }, screen: { x: 0, - y: 0 + y: 0, }, - filename: 'newMap' + filename: 'newMap', }; } - + close() { - this.mapLoader.loadRawMap(this.map, this.map.name, `data/maps/${this.map.name}.json`); + this.mapLoader.loadRawMap( + this.map, + this.map.name, + `data/maps/${this.map.name}.json`, + ); this.ref.close(); } - } diff --git a/webapp/src/app/components/dialogs/offset-map/offset-map.component.html b/webapp/src/app/components/dialogs/offset-map/offset-map.component.html index d165e485..7dcd9d5d 100644 --- a/webapp/src/app/components/dialogs/offset-map/offset-map.component.html +++ b/webapp/src/app/components/dialogs/offset-map/offset-map.component.html @@ -1,37 +1,22 @@
    - + - +
    - + - +
    - + Offset Entities
    diff --git a/webapp/src/app/components/dialogs/offset-map/offset-map.component.ts b/webapp/src/app/components/dialogs/offset-map/offset-map.component.ts index 369e1ce7..de7f12d6 100644 --- a/webapp/src/app/components/dialogs/offset-map/offset-map.component.ts +++ b/webapp/src/app/components/dialogs/offset-map/offset-map.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { Point } from '../../../models/cross-code-map'; import { GlobalEventsService } from '../../../services/global-events.service'; import { OverlayRefControl } from '../overlay/overlay-ref-control'; @@ -9,20 +9,16 @@ import { StateHistoryService } from '../floating-window/history/state-history.se selector: 'app-offset-map', templateUrl: './offset-map.component.html', styleUrls: ['./offset-map.component.scss'], - standalone: false + standalone: false, }) export class OffsetMapComponent { - - offset: Point = {x: 0, y: 0}; + private events = inject(GlobalEventsService); + private history = inject(StateHistoryService); + ref = inject(OverlayRefControl); + + offset: Point = { x: 0, y: 0 }; entities = false; - - constructor( - private events: GlobalEventsService, - private history: StateHistoryService, - public ref: OverlayRefControl, - ) { - } - + update() { if (this.offset.x === 0 && this.offset.y === 0) { return; @@ -34,11 +30,14 @@ export class OffsetMapComponent { y: this.offset.y * Globals.TILE_SIZE, }); } - this.history.saveState({ - name: 'Offset map', - icon: 'open_with' - }, true); - + this.history.saveState( + { + name: 'Offset map', + icon: 'open_with', + }, + true, + ); + this.ref.close(); } } diff --git a/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.html b/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.html index 514a53d4..85b698d8 100644 --- a/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.html +++ b/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.html @@ -9,13 +9,13 @@ [style.margin-left]="left" [style.margin-top]="top" > -
    - @if (title) {

    {{ title }}

    } @@ -26,14 +26,20 @@

    {{ title }}

    - +
    -
    +
    diff --git a/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.ts b/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.ts index a44ccb6b..cdc31acd 100644 --- a/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.ts +++ b/webapp/src/app/components/dialogs/overlay/overlay-panel/overlay-panel.component.ts @@ -23,20 +23,17 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; templateUrl: './overlay-panel.component.html', styleUrls: ['./overlay-panel.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false + standalone: false, }) export class OverlayPanelComponent { - @Input() title = ''; @Input() allowDrag = true; - @Input() customSize?: { width?: string, height?: string }; + @Input() customSize?: { width?: string; height?: string }; @Input() left?: string; @Input() top?: string; @Input() showButtonDivider = false; - - classes: { [key: string]: boolean } = {}; - - constructor() { - } - + + classes: Record = {}; + + constructor() {} } diff --git a/webapp/src/app/components/dialogs/overlay/overlay-ref-control.ts b/webapp/src/app/components/dialogs/overlay/overlay-ref-control.ts index fb280796..8a2fd5f8 100644 --- a/webapp/src/app/components/dialogs/overlay/overlay-ref-control.ts +++ b/webapp/src/app/components/dialogs/overlay/overlay-ref-control.ts @@ -2,18 +2,16 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Subject } from 'rxjs'; export class OverlayRefControl { - onClose = new Subject(); - - constructor(private ref: OverlayRef) { - } - + + constructor(private ref: OverlayRef) {} + close(result?: T) { this.ref.dispose(); this.onClose.next(result); this.onClose.complete(); } - + isOpen() { return this.ref.hasAttached(); } diff --git a/webapp/src/app/components/dialogs/overlay/overlay.service.ts b/webapp/src/app/components/dialogs/overlay/overlay.service.ts index 091b72ff..cc053e56 100644 --- a/webapp/src/app/components/dialogs/overlay/overlay.service.ts +++ b/webapp/src/app/components/dialogs/overlay/overlay.service.ts @@ -1,6 +1,6 @@ import { ComponentType, Overlay, OverlayConfig } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { Globals } from '../../../services/globals'; import { OverlayRefControl } from './overlay-ref-control'; @@ -11,53 +11,61 @@ interface CustomOverlayConfig extends OverlayConfig { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class OverlayService { - + private injector = inject(Injector); + private overlay = inject(Overlay); + private static disablePhaserInputKey = 0; - - constructor( - private injector: Injector, - private overlay: Overlay) { - } - - open(component: ComponentType, options: CustomOverlayConfig = {}): { ref: OverlayRefControl, instance: T } { + + open( + component: ComponentType, + options: CustomOverlayConfig = {}, + ): { ref: OverlayRefControl; instance: T } { const config = this.getOverlayConfig(options); const ref = this.overlay.create(config); const refControl = new OverlayRefControl(ref); - const portal = new ComponentPortal(component, null, this.createInjector(refControl)); - + const portal = new ComponentPortal( + component, + null, + this.createInjector(refControl), + ); + if (options.backdropClickClose) { ref.backdropClick().subscribe(() => refControl.close()); } if (options.disablePhaserInput) { const key = ++OverlayService.disablePhaserInputKey; Globals.disablePhaserInput.add(key); - ref.detachments().subscribe(() => Globals.disablePhaserInput.delete(key)); + ref.detachments().subscribe(() => + Globals.disablePhaserInput.delete(key), + ); } - + const compRef = ref.attach(portal); - return {ref: refControl, instance: compRef.instance}; + return { ref: refControl, instance: compRef.instance }; } - + private createInjector(ref: OverlayRefControl): Injector { return Injector.create({ parent: this.injector, - providers: [ - {provide: OverlayRefControl, useValue: ref}, - ], + providers: [{ provide: OverlayRefControl, useValue: ref }], }); } - + private getOverlayConfig(options: OverlayConfig): OverlayConfig { const config = { width: 0, height: 0, scrollStrategy: this.overlay.scrollStrategies.noop(), - positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically() + positionStrategy: this.overlay + .position() + .global() + .centerHorizontally() + .centerVertically(), }; - const merged = {...config, ...options}; + const merged = { ...config, ...options }; return new OverlayConfig(merged); } } diff --git a/webapp/src/app/components/dialogs/settings/settings.component.html b/webapp/src/app/components/dialogs/settings/settings.component.html index c2819fc0..96ffc893 100644 --- a/webapp/src/app/components/dialogs/settings/settings.component.html +++ b/webapp/src/app/components/dialogs/settings/settings.component.html @@ -5,8 +5,11 @@
    Assets folder - + Path to CrossCode Assets folder @if (folderFormControl.hasError('invalid')) { @@ -15,10 +18,14 @@ }
    - +
    @@ -26,13 +33,21 @@
    Mod - + None @for (mod of mods; track mod) { - + } - Maps will be stored and loaded from the selected mod + Maps will be stored and loaded from the selected + mod
    @@ -53,7 +68,7 @@
    -

    Selection Box Style

    +

    Selection Box Style

    this.mods = mods); + + http.getMods().subscribe((mods) => (this.mods = mods)); this.mod = this.sharedService.getSelectedMod(); this.isIncludeVanillaMapsDisabled = !this.mod; - this.settings = JSON.parse(JSON.stringify(this.settingsService.getSettings())); + this.settings = JSON.parse( + JSON.stringify(this.settingsService.getSettings()), + ); } - + ngOnInit() { if (this.isElectron) { this.folderFormControl.setValue(this.electron.getAssetsPath()); - this.folderFormControl.valueChanges.subscribe(() => this.resetIcon()); + this.folderFormControl.valueChanges.subscribe(() => + this.resetIcon(), + ); } - + this.check(); } - + private resetIcon() { this.icon = 'help_outline'; this.iconCss = 'icon-undefined'; } - + private setIcon(valid: boolean) { if (valid) { this.icon = 'check'; @@ -83,46 +92,51 @@ export class SettingsComponent implements OnInit { this.iconCss = 'icon-invalid'; } } - + select() { const path = this.electron.selectCcFolder(); if (path) { this.folderFormControl.setValue(path); } } - + check() { - const valid = this.electron.checkAssetsPath(this.folderFormControl.value); + const valid = this.electron.checkAssetsPath( + this.folderFormControl.value, + ); this.setIcon(valid); if (valid) { this.folderFormControl.setErrors(null); } else { this.folderFormControl.setErrors({ - invalid: true + invalid: true, }); } } - + modSelectEvent(selectedMod: string) { this.isIncludeVanillaMapsDisabled = !selectedMod; } - + save() { if (this.isElectron) { this.electron.saveAssetsPath(this.folderFormControl.value); } - this.sharedService.saveModSelect(this.mod); + void this.sharedService.saveModSelect(this.mod); this.settingsService.updateSettings(this.settings); this.close(); - const ref = this.snackBar.open('Changing the path requires to restart the editor', 'Restart', { - duration: 6000 - }); - + const ref = this.snackBar.open( + 'Changing the path requires to restart the editor', + 'Restart', + { + duration: 6000, + }, + ); + ref.onAction().subscribe(() => this.sharedService.relaunch()); } - + close() { this.ref.close(); } - } diff --git a/webapp/src/app/components/editor/editor.component.html b/webapp/src/app/components/editor/editor.component.html index f846e008..9d9b2cb6 100644 --- a/webapp/src/app/components/editor/editor.component.html +++ b/webapp/src/app/components/editor/editor.component.html @@ -1,12 +1,22 @@
    - + - + diff --git a/webapp/src/app/components/editor/editor.component.ts b/webapp/src/app/components/editor/editor.component.ts index bb2ec31d..612eab76 100644 --- a/webapp/src/app/components/editor/editor.component.ts +++ b/webapp/src/app/components/editor/editor.component.ts @@ -1,40 +1,39 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, inject } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { AddEntityMenuService } from '../../services/add-entity-menu.service'; -import { LoadMapComponent } from '../dialogs/load-map/load-map.component'; import { JsonLoaderService } from '../../services/json-loader.service'; +import { LoadMapComponent } from '../dialogs/load-map/load-map.component'; @Component({ selector: 'app-editor', templateUrl: './editor.component.html', styleUrls: ['./editor.component.scss'], - standalone: false + standalone: false, }) export class EditorComponent { - @ViewChild('loadmap', {static: true}) + @ViewChild('loadmap', { static: true }) loadmap!: LoadMapComponent; - - @ViewChild('sidenavLoadMap', {static: true}) + + @ViewChild('sidenavLoadMap', { static: true }) sidenavLoadMap!: MatSidenav; - - constructor( - addEntity: AddEntityMenuService, - jsonLoader: JsonLoaderService - ) { - addEntity.init(); - + + constructor() { + const addEntity = inject(AddEntityMenuService); + const jsonLoader = inject(JsonLoaderService); + + void addEntity.init(); + // makes sure they are synchronously available - jsonLoader.loadJsonMerged('actions.json'); - jsonLoader.loadJsonMerged('events.json'); - jsonLoader.loadJsonMerged('map-styles.json'); - + void jsonLoader.loadJsonMerged('actions.json'); + void jsonLoader.loadJsonMerged('events.json'); + void jsonLoader.loadJsonMerged('map-styles.json'); } - + loadMapClicked() { - this.sidenavLoadMap.toggle(); + void this.sidenavLoadMap.toggle(); } - + focusInput() { // has to wait before sidenav renders the content setTimeout(() => this.loadmap.focusInput(), 100); diff --git a/webapp/src/app/components/entities/entities.component.html b/webapp/src/app/components/entities/entities.component.html index 0c74583c..881c5288 100644 --- a/webapp/src/app/components/entities/entities.component.html +++ b/webapp/src/app/components/entities/entities.component.html @@ -1,29 +1,41 @@ @if (!entity && !hideFilter) { }

    {{ entity?.details?.type }}

    - @if (entity) { } - + - + @if (entity) { - - } @if (entity) { - -} - + } + @if (entity) { + + } +
    diff --git a/webapp/src/app/components/entities/entities.component.ts b/webapp/src/app/components/entities/entities.component.ts index 15662d36..86ac1c8d 100644 --- a/webapp/src/app/components/entities/entities.component.ts +++ b/webapp/src/app/components/entities/entities.component.ts @@ -1,8 +1,11 @@ -import { Component, ViewChild, ViewContainerRef } from '@angular/core'; +import { Component, ViewChild, ViewContainerRef, inject } from '@angular/core'; import { HostDirective } from '../../directives/host.directive'; import { GlobalEventsService } from '../../services/global-events.service'; import { MapLoaderService } from '../../services/map-loader.service'; -import { AttributeValue, CCEntity } from '../../services/phaser/entities/cc-entity'; +import { + AttributeValue, + CCEntity, +} from '../../services/phaser/entities/cc-entity'; import { CCMap } from '../../services/phaser/tilemap/cc-map'; import { AbstractWidget } from '../widgets/abstract-widget'; import { Vec2WidgetComponent } from '../widgets/vec2-widget/vec2-widget.component'; @@ -12,56 +15,62 @@ import { WidgetRegistryService } from '../widgets/widget-registry.service'; selector: 'app-entities', templateUrl: './entities.component.html', styleUrls: ['./entities.component.scss'], - standalone: false + standalone: false, }) export class EntitiesComponent { - @ViewChild(HostDirective, {static: false}) appHost?: HostDirective; + private widgetRegistry = inject(WidgetRegistryService); + private events = inject(GlobalEventsService); + + @ViewChild(HostDirective, { static: false }) appHost?: HostDirective; entity?: CCEntity; map?: CCMap; filter = ''; hideFilter = false; - - constructor( - private widgetRegistry: WidgetRegistryService, - private events: GlobalEventsService, - loader: MapLoaderService - ) { - events.selectedEntity.subscribe(e => { + + constructor() { + const events = this.events; + const loader = inject(MapLoaderService); + + events.selectedEntity.subscribe((e) => { // clear focus of input fields to enable phaser inputs again ONLY if not a canvas - if (document.activeElement && document.activeElement.tagName !== 'CANVAS') { - (document.activeElement).blur(); + if ( + document.activeElement && + document.activeElement.tagName !== 'CANVAS' + ) { + (document.activeElement as HTMLElement).blur(); } this.entity = e; this.loadSettings(e); }); - events.is3D.subscribe(is3d => this.hideFilter = is3d); - loader.tileMap.subscribe(map => { + events.is3D.subscribe((is3d) => (this.hideFilter = is3d)); + loader.tileMap.subscribe((map) => { this.map = map; this.filter = ''; }); } - + loadSettings(entity?: CCEntity) { if (!this.appHost) { return; } const ref = this.appHost.viewContainerRef; ref.clear(); - + if (!entity) { return; } - + const def = entity.getScaleSettings(); if (def && (def.scalableX || def.scalableY)) { - const vec2Widget: Vec2WidgetComponent = this.generateWidget( + const vec2Widget: Vec2WidgetComponent = this.generateWidget( entity, - 'size', { + 'size', + { type: 'Vec2', - description: '' + description: '', }, - ref - ); + ref, + ) as Vec2WidgetComponent; vec2Widget.def = def; } const widgets: Record = {}; @@ -70,14 +79,21 @@ export class EntitiesComponent { }); entity.setWidgets(widgets); } - + updateFilter() { this.events.filterEntity.next(this.filter); } - - private generateWidget(entity: CCEntity, key: string, val: AttributeValue, ref: ViewContainerRef) { - const componentRef = ref.createComponent(this.widgetRegistry.getWidget(val.type)); - const instance = componentRef.instance; + + private generateWidget( + entity: CCEntity, + key: string, + val: AttributeValue, + ref: ViewContainerRef, + ) { + const componentRef = ref.createComponent( + this.widgetRegistry.getWidget(val.type), + ); + const instance = componentRef.instance as AbstractWidget; instance.entity = entity; instance.key = key; instance.attribute = val; diff --git a/webapp/src/app/components/json-editor/json-editor.component.html b/webapp/src/app/components/json-editor/json-editor.component.html index 2f6fcf18..aac21be9 100644 --- a/webapp/src/app/components/json-editor/json-editor.component.html +++ b/webapp/src/app/components/json-editor/json-editor.component.html @@ -1,12 +1,19 @@
    -
    -
    +
    diff --git a/webapp/src/app/components/json-editor/json-editor.component.ts b/webapp/src/app/components/json-editor/json-editor.component.ts index f077a34e..7dce08e5 100644 --- a/webapp/src/app/components/json-editor/json-editor.component.ts +++ b/webapp/src/app/components/json-editor/json-editor.component.ts @@ -1,4 +1,10 @@ -import { AfterViewInit, Component, ElementRef, Inject, Optional, ViewChild } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + ViewChild, + inject, +} from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import JSONEditor, { JSONEditorOptions } from 'jsoneditor'; import { Globals } from '../../services/globals'; @@ -7,26 +13,33 @@ import { Globals } from '../../services/globals'; selector: 'app-json-editor', templateUrl: './json-editor.component.html', styleUrls: ['./json-editor.component.scss'], - standalone: false + standalone: false, }) export class JsonEditorComponent implements AfterViewInit { - @ViewChild('editor', {static: false}) container?: ElementRef; - + ref = inject>(MatDialogRef); + + @ViewChild('editor', { static: false }) container?: ElementRef; + private editor?: JSONEditor; private options: JSONEditorOptions = {}; data: any; private key: string; json = JSON; - - constructor(@Optional() @Inject(MAT_DIALOG_DATA) data: { key: string, val: any }, - public ref: MatDialogRef) { - this.data = data.val; - this.key = data.key; + + constructor() { + const data = inject<{ + key: string; + val: any; + }>(MAT_DIALOG_DATA, { optional: true }); + const ref = this.ref; + + this.data = data?.val; + this.key = data?.key!; ref.afterClosed().subscribe(() => { Globals.disablePhaserInput.delete('json'); }); } - + ngAfterViewInit() { Globals.disablePhaserInput.add('json'); this.options = {}; @@ -39,12 +52,15 @@ export class JsonEditorComponent implements AfterViewInit { if (!this.container) { throw new Error('could not find html element "editor"'); } - this.editor = new JSONEditor(this.container.nativeElement, this.options); + this.editor = new JSONEditor( + this.container.nativeElement, + this.options, + ); this.editor.set(this.data); this.editor.setName(this.key); this.editor.expandAll(); } - + setJson(val: string) { if (!this.editor) { throw new Error('no editor defined'); @@ -53,5 +69,4 @@ export class JsonEditorComponent implements AfterViewInit { this.editor.set(this.data); this.editor.expandAll(); } - } diff --git a/webapp/src/app/components/layers/layers.component.html b/webapp/src/app/components/layers/layers.component.html index 31869c73..40563c09 100644 --- a/webapp/src/app/components/layers/layers.component.html +++ b/webapp/src/app/components/layers/layers.component.html @@ -1,17 +1,37 @@
    - + @for (layer of map?.layers; track layer) { - +
    -
    -
    +
    +
    {{ getDisplayName(layer) }}
    -
    @@ -19,22 +39,31 @@
    - +
    - + - Settings {{ selectedLayer ? '- ' + getDisplayName(selectedLayer) : '' }} + Settings + {{ + selectedLayer + ? '- ' + getDisplayName(selectedLayer) + : '' + }} - +
    @@ -47,8 +76,10 @@ refresh
    - -
    + +
    X: + /> - + Y: + />
    - +
    Type: - - Background + + Background Collision - Navigation + Navigation HeightMap Light
    - +
    Name: - +
    - +
    Level: @@ -99,18 +143,28 @@ fxFlex [disabled]="!selectedLayer" floatPlaceholder="never" - [ngModel]="selectedLayer?.details?.levelName ?? selectedLayer?.details?.level" + [ngModel]=" + selectedLayer?.details?.levelName ?? + selectedLayer?.details?.level + " (selectionChange)="updateLevel($event.value)" > - @for (level of map?.levels; track level; let i = $index) { + @for ( + level of map?.levels; + track level; + let i = $index + ) { {{ i }} - } @for (level of map?.specialLevels; track level) { - {{ level }} - } + } + @for (level of map?.specialLevels; track level) { + {{ + level + }} + }
    - +
    Distance: @@ -119,26 +173,30 @@ step="0.1" matInput [disabled]="!selectedLayer" - [(ngModel)]="selectedLayer?.details!.distance" + [(ngModel)]="(selectedLayer?.details)!.distance" (blur)="updateDistance()" - > + />
    - +
    Tileset: - + @for (tileset of tilesets; track tileset) { - {{ getTilesetName(tileset) }} + {{ getTilesetName(tileset) }} }
    - +
    Lighter:
    - +
    Actions: - +
    - -
    diff --git a/webapp/src/app/components/layers/layers.component.ts b/webapp/src/app/components/layers/layers.component.ts index e7c605a3..c4f6c3f0 100644 --- a/webapp/src/app/components/layers/layers.component.ts +++ b/webapp/src/app/components/layers/layers.component.ts @@ -1,5 +1,5 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { firstValueFrom } from 'rxjs'; import { GlobalEventsService } from '../../services/global-events.service'; @@ -15,39 +15,44 @@ import { StateHistoryService } from '../dialogs/floating-window/history/state-hi templateUrl: './layers.component.html', styleUrls: ['./layers.component.scss'], encapsulation: ViewEncapsulation.None, - standalone: false + standalone: false, }) export class LayersComponent implements OnInit { + private mapLoader = inject(MapLoaderService); + private stateHistory = inject(StateHistoryService); + private http = inject(HttpClientService); + static tilesets: string[] = []; //Cache - + selectedLayer?: CCMapLayer; map?: CCMap; newLayerName = ''; tilesets: string[] = []; //Angular view data - + width = 0; height = 0; - - constructor(private mapLoader: MapLoaderService, - private stateHistory: StateHistoryService, - private http: HttpClientService, - events: GlobalEventsService) { + + constructor() { + const events = inject(GlobalEventsService); + events.toggleVisibility.subscribe(() => { if (this.selectedLayer) { - this.toggleVisibility({ - stopPropagation: () => { - } - } as Event, this.selectedLayer); + this.toggleVisibility( + { + stopPropagation: () => {}, + } as Event, + this.selectedLayer, + ); } }); - - this.loadTilesets(); + + void this.loadTilesets(); } - + ngOnInit() { - this.mapLoader.selectedLayer.subscribe(layer => { + this.mapLoader.selectedLayer.subscribe((layer) => { this.selectedLayer = layer; - for (const layer of (this.map?.layers ?? [])) { + for (const layer of this.map?.layers ?? []) { layer.select(false); } if (layer) { @@ -56,13 +61,13 @@ export class LayersComponent implements OnInit { this.height = layer.details.height; } }); - this.mapLoader.tileMap.subscribe(tilemap => this.map = tilemap); + this.mapLoader.tileMap.subscribe((tilemap) => (this.map = tilemap)); } - + getDisplayName(layer: CCMapLayer): string { return `${layer.details.name} (${layer.details.levelName ?? layer.details.level})`; } - + toggleVisibility(event: Event, layer: CCMapLayer) { event.stopPropagation(); layer.visible = !layer.visible; @@ -70,18 +75,18 @@ export class LayersComponent implements OnInit { this.selectLayer(layer); } } - + async addNewLayer() { if (!this.map) { return; } const map = this.map; const tilemap = map.getTilemap(); - + if (!tilemap) { return; } - + const data: number[][] = []; for (let y = 0; y < map.mapHeight; y++) { data[y] = []; @@ -101,18 +106,21 @@ export class LayersComponent implements OnInit { repeat: false, distance: 1, tilesize: Globals.TILE_SIZE, - moveSpeed: {x: 0, y: 0}, + moveSpeed: { x: 0, y: 0 }, data: data, }); console.log(layer); map.addLayer(layer); this.newLayerName = ''; - this.stateHistory.saveState({ - name: 'Layer added', - icon: 'add' - }, true); + this.stateHistory.saveState( + { + name: 'Layer added', + icon: 'add', + }, + true, + ); } - + deleteSelected() { const layer = this.selectedLayer; if (!layer) { @@ -122,62 +130,67 @@ export class LayersComponent implements OnInit { throw new Error('no tilemap defined'); } this.map.removeLayer(layer); - this.stateHistory.saveState({ - name: 'Layer deleted', - icon: 'delete' - }, true); - + this.stateHistory.saveState( + { + name: 'Layer deleted', + icon: 'delete', + }, + true, + ); + this.selectLayer(undefined); } - + selectLayer(layer?: CCMapLayer) { this.mapLoader.selectedLayer.next(layer); } - + updateTilesetName(name: string) { if (!this.selectedLayer) { throw new Error('no layer selected'); } - this.selectedLayer.updateTileset(name); + void this.selectedLayer.updateTileset(name); this.mapLoader.selectedLayer.next(this.selectedLayer); } - + getTilesetName(path: string): string { return path.substring('media/map/'.length, path.length - '.png'.length); } - + private async loadTilesets() { if (LayersComponent.tilesets.length > 0) { this.tilesets = LayersComponent.tilesets; return; } - - LayersComponent.tilesets = await firstValueFrom(this.http.getAllTilesets()); + + LayersComponent.tilesets = await firstValueFrom( + this.http.getAllTilesets(), + ); this.tilesets = LayersComponent.tilesets; } - + updateLevel(level: number | string) { if (!this.selectedLayer) { throw new Error('no layer selected'); } this.selectedLayer.updateLevel(level); } - + updateSize() { this.selectedLayer?.resize(this.width, this.height); this.stateHistory.saveState({ name: 'Layer resized', - icon: 'resize' + icon: 'resize', }); } - + updateDistance() { this.stateHistory.saveState({ name: 'Distance changed', - icon: 'fit_screen' + icon: 'fit_screen', }); } - + drop(event: CdkDragDrop) { if (event.previousIndex === event.currentIndex) { return; @@ -185,11 +198,18 @@ export class LayersComponent implements OnInit { if (!this.map) { throw new Error('tilemap not defined'); } - moveItemInArray(this.map.layers, event.previousIndex, event.currentIndex); + moveItemInArray( + this.map.layers, + event.previousIndex, + event.currentIndex, + ); this.map.updateLayerIndices(); - this.stateHistory.saveState({ - name: 'Layer moved', - icon: 'open_with', - }, true); + this.stateHistory.saveState( + { + name: 'Layer moved', + icon: 'open_with', + }, + true, + ); } } diff --git a/webapp/src/app/components/phaser/phaser.component.html b/webapp/src/app/components/phaser/phaser.component.html index 9ae25b93..9d624727 100644 --- a/webapp/src/app/components/phaser/phaser.component.html +++ b/webapp/src/app/components/phaser/phaser.component.html @@ -1,2 +1,2 @@
    - \ No newline at end of file + diff --git a/webapp/src/app/components/phaser/phaser.component.ts b/webapp/src/app/components/phaser/phaser.component.ts index 919a06cb..1816e207 100644 --- a/webapp/src/app/components/phaser/phaser.component.ts +++ b/webapp/src/app/components/phaser/phaser.component.ts @@ -1,4 +1,11 @@ -import { AfterViewInit, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + HostListener, + ViewChild, + inject, +} from '@angular/core'; import * as Phaser from 'phaser'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -7,38 +14,43 @@ import { GlobalEventsService } from '../../services/global-events.service'; import { Globals } from '../../services/globals'; import { HeightMapService } from '../../services/height-map/height-map.service'; import { HttpClientService } from '../../services/http-client.service'; +import { JsonLoaderService } from '../../services/json-loader.service'; import { MapLoaderService } from '../../services/map-loader.service'; import { EntityRegistryService } from '../../services/phaser/entities/registry/entity-registry.service'; import { MainScene } from '../../services/phaser/main-scene'; import { PhaserEventsService } from '../../services/phaser/phaser-events.service'; import { SettingsService } from '../../services/settings.service'; import { StateHistoryService } from '../dialogs/floating-window/history/state-history.service'; -import { JsonLoaderService } from '../../services/json-loader.service'; @Component({ selector: 'app-phaser', templateUrl: './phaser.component.html', styleUrls: ['./phaser.component.scss'], - standalone: false + standalone: false, }) export class PhaserComponent implements AfterViewInit { - - @ViewChild('content', {static: true}) content!: ElementRef; - - constructor( - private element: ElementRef, - private mapLoader: MapLoaderService, - private globalEvents: GlobalEventsService, - private stateHistory: StateHistoryService, - private phaserEventsService: PhaserEventsService, - private heightMap: HeightMapService, - private http: HttpClientService, - snackbar: MatSnackBar, - registry: EntityRegistryService, - autotile: AutotileService, - settingsService: SettingsService, - jsonLoader: JsonLoaderService, - ) { + private element = inject(ElementRef); + private mapLoader = inject(MapLoaderService); + private globalEvents = inject(GlobalEventsService); + private stateHistory = inject(StateHistoryService); + private phaserEventsService = inject(PhaserEventsService); + private heightMap = inject(HeightMapService); + private http = inject(HttpClientService); + + @ViewChild('content', { static: true }) content!: ElementRef; + + constructor() { + const mapLoader = this.mapLoader; + const globalEvents = this.globalEvents; + const stateHistory = this.stateHistory; + const phaserEventsService = this.phaserEventsService; + const http = this.http; + const snackbar = inject(MatSnackBar); + const registry = inject(EntityRegistryService); + const autotile = inject(AutotileService); + const settingsService = inject(SettingsService); + const jsonLoader = inject(JsonLoaderService); + Globals.stateHistoryService = stateHistory; Globals.mapLoaderService = mapLoader; Globals.phaserEventsService = phaserEventsService; @@ -50,10 +62,9 @@ export class PhaserComponent implements AfterViewInit { Globals.settingsService = settingsService; Globals.jsonLoader = jsonLoader; } - - + ngAfterViewInit() { - this.heightMap.init(); + void this.heightMap.init(); const scene = new MainScene(); const scale = this.getScale(); Globals.game = new Phaser.Game({ @@ -63,36 +74,33 @@ export class PhaserComponent implements AfterViewInit { parent: 'content', scale: { mode: Phaser.Scale.ScaleModes.NONE, - zoom: 1 / window.devicePixelRatio + zoom: 1 / window.devicePixelRatio, }, render: { antialiasGL: false, - pixelArt: true + pixelArt: true, }, zoom: 1, - scene: [scene] + scene: [scene], }); Globals.scene = scene; } - + @HostListener('window:resize', ['$event']) onResize() { if (!Globals.game) { return; } const scale = this.getScale(); - Globals.game.scale.resize( - scale.width, - scale.height - ); + Globals.game.scale.resize(scale.width, scale.height); Globals.game.scale.setZoom(1 / window.devicePixelRatio); } - + private getScale() { const rect = this.content.nativeElement.getBoundingClientRect(); return { width: rect.width * window.devicePixelRatio, - height: rect.height * window.devicePixelRatio + height: rect.height * window.devicePixelRatio, }; } } diff --git a/webapp/src/app/components/sidenav/sidenav.component.html b/webapp/src/app/components/sidenav/sidenav.component.html index 7772abbf..196b6c40 100644 --- a/webapp/src/app/components/sidenav/sidenav.component.html +++ b/webapp/src/app/components/sidenav/sidenav.component.html @@ -1,6 +1,9 @@ @if (tilemap) {
    diff --git a/webapp/src/app/components/split-pane/split-pane.component.ts b/webapp/src/app/components/split-pane/split-pane.component.ts index bee6d9ac..b80560de 100644 --- a/webapp/src/app/components/split-pane/split-pane.component.ts +++ b/webapp/src/app/components/split-pane/split-pane.component.ts @@ -10,7 +10,8 @@ import { OnInit, Output, SimpleChanges, - ViewChild + ViewChild, + inject, } from '@angular/core'; /** values in % [0, 100] */ @@ -24,51 +25,47 @@ export interface Bounds { templateUrl: './split-pane.component.html', styleUrls: ['./split-pane.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false + standalone: false, }) export class SplitPaneComponent implements OnInit, OnChanges { - + private ref = inject(ChangeDetectorRef); + @ViewChild('container') container!: ElementRef; - + @HostListener('document:mousemove', ['$event']) mouseMove(event: MouseEvent) { if (this.isDragging) { this.drag(event); } } - + @HostListener('document:mouseup', ['$event']) mouseUp(event: MouseEvent) { if (this.isDragging) { this.dragEnd(event); } } - + @Input() bounds?: Bounds; @Input() showGutter = false; - + /** * gutter position, range [-100, 100], 0 is center * */ @Input() base = 0; @Output() baseChange = new EventEmitter(); - + @Input() opened = true; - + mouseX = 0; basisLeft = 0; basisRight = 0; - + isDragging = false; noAnims = false; private offset = 0; private scale = 0; - - constructor( - private ref: ChangeDetectorRef - ) { - } - + ngOnInit(): void { // sets initial values without animating to them this.noAnims = true; @@ -77,40 +74,40 @@ export class SplitPaneComponent implements OnInit, OnChanges { this.noAnims = false; }, 0); } - + ngOnChanges(changes: SimpleChanges) { this.valueChanged(this.base); } - + valueChanged(val: number, bounds?: Bounds) { const boundsRight = (bounds?.right ?? 0) * 2; const boundsLeft = (bounds?.left ?? 0) * 2; val = Math.min(Math.max(val, -100 + boundsLeft), 100 - boundsRight); - + this.basisLeft = Math.max(0, val); this.basisRight = Math.max(0, -val); this.base = val; - + this.baseChange.emit(this.base); } - + dragStart(event: MouseEvent) { event.preventDefault(); this.isDragging = true; this.noAnims = true; const el = this.container.nativeElement; const rect = el.getBoundingClientRect(); - + this.offset = rect.left; this.scale = 1 / rect.width; } - + dragEnd(event: MouseEvent) { event.preventDefault(); this.isDragging = false; this.noAnims = false; } - + drag(event: MouseEvent) { let val = (event.clientX - this.offset) * this.scale; val = Math.min(Math.max(0, val), 1); diff --git a/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.html b/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.html index 1b92a568..fca96216 100644 --- a/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.html +++ b/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.html @@ -1,24 +1,19 @@
    @if (gridSettings(); as settings) { - Entity Grid
    -
    @if (settings.showSettings) {
    @@ -29,10 +24,10 @@ Size - + Visible
    } - }
    diff --git a/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.ts b/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.ts index 57c45e8b..ead3a9da 100644 --- a/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.ts +++ b/webapp/src/app/components/toolbar/grid-menu/grid-menu.component.ts @@ -1,4 +1,4 @@ -import { Component, effect, OnInit } from '@angular/core'; +import { Component, effect, OnInit, inject } from '@angular/core'; import { MatCheckbox } from '@angular/material/checkbox'; import { MatIconButton } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; @@ -9,7 +9,13 @@ import { MatFormField, MatLabel } from '@angular/material/form-field'; import { PointInputComponent } from '../vec-input/point-input.component'; import { Helper } from '../../../services/phaser/helper'; import { Point } from '../../../models/cross-code-map'; -import { animate, state, style, transition, trigger } from '@angular/animations'; +import { + animate, + state, + style, + transition, + trigger, +} from '@angular/animations'; import { GlobalEventsService } from '../../../services/global-events.service'; export interface GridSettings { @@ -27,14 +33,15 @@ const gridSettingsKey = 'gridSettingsKey'; selector: 'app-grid-menu', animations: [ trigger('openClose', [ - state('void', style({ - width: '0', - margin: '0' - })), - transition('* <=> *', [ - animate('100ms ease'), - ]), - ]) + state( + 'void', + style({ + width: '0', + margin: '0', + }), + ), + transition('* <=> *', [animate('100ms ease')]), + ]), ], imports: [ MatCheckbox, @@ -43,50 +50,50 @@ const gridSettingsKey = 'gridSettingsKey'; FormsModule, MatFormField, MatLabel, - PointInputComponent + PointInputComponent, ], templateUrl: './grid-menu.component.html', - styleUrl: './grid-menu.component.scss' + styleUrl: './grid-menu.component.scss', }) export class GridMenuComponent implements OnInit { - gridSettings = Globals.gridSettings; - - constructor( - events: GlobalEventsService - ) { + + constructor() { + const events = inject(GlobalEventsService); + effect(() => { const settings = this.gridSettings(); localStorage.setItem(gridSettingsKey, JSON.stringify(settings)); events.gridSettings.next(settings); }); } - + ngOnInit() { try { - const settings = JSON.parse(localStorage.getItem(gridSettingsKey)!) as Partial; - Globals.gridSettings.update(old => ({ + const settings = JSON.parse( + localStorage.getItem(gridSettingsKey)!, + ) as Partial; + Globals.gridSettings.update((old) => ({ ...old, - ...settings + ...settings, })); - } catch (e) { - } + } catch (e) {} } - + update(newSettings: Partial) { - Globals.gridSettings.update(old => { + Globals.gridSettings.update((old) => { const cpy = Helper.copy(old); Object.assign(cpy, newSettings); return cpy; }); } - + protected readonly MatCheckbox = MatCheckbox; - + toggleSettings() { - Globals.gridSettings.update(old => ({ + Globals.gridSettings.update((old) => ({ ...old, - showSettings: !old.showSettings + showSettings: !old.showSettings, })); } } diff --git a/webapp/src/app/components/toolbar/toolbar-divider/toolbar-divider.component.ts b/webapp/src/app/components/toolbar/toolbar-divider/toolbar-divider.component.ts index 8e6f2ea4..2db11a00 100644 --- a/webapp/src/app/components/toolbar/toolbar-divider/toolbar-divider.component.ts +++ b/webapp/src/app/components/toolbar/toolbar-divider/toolbar-divider.component.ts @@ -4,7 +4,6 @@ import { Component } from '@angular/core'; selector: 'app-toolbar-divider', imports: [], template: '', - styleUrl: './toolbar-divider.component.scss' + styleUrl: './toolbar-divider.component.scss', }) -export class ToolbarDividerComponent { -} +export class ToolbarDividerComponent {} diff --git a/webapp/src/app/components/toolbar/toolbar.component.html b/webapp/src/app/components/toolbar/toolbar.component.html index 5ddaa9af..10cc6e5f 100644 --- a/webapp/src/app/components/toolbar/toolbar.component.html +++ b/webapp/src/app/components/toolbar/toolbar.component.html @@ -1,37 +1,58 @@ -
    -
    +
    @if (loaded) { - } @if (loaded) { - - } - - + } + + - +
    - - + - - + - - +
    - +
    - + - +
    {{ map?.name }}
    - -
    + +
    @if (map) { - - } @if (!loaded) { - - {{ error }} - @if (!error) { - + + } + @if (!loaded) { + {{ error }} + @if (!error) { + + } } - - }
    diff --git a/webapp/src/app/components/toolbar/toolbar.component.ts b/webapp/src/app/components/toolbar/toolbar.component.ts index 34ebf455..d4b43052 100644 --- a/webapp/src/app/components/toolbar/toolbar.component.ts +++ b/webapp/src/app/components/toolbar/toolbar.component.ts @@ -1,5 +1,5 @@ import { Overlay } from '@angular/cdk/overlay'; -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; @@ -18,38 +18,37 @@ import { SettingsComponent } from '../dialogs/settings/settings.component'; selector: 'app-toolbar', templateUrl: './toolbar.component.html', styleUrls: ['./toolbar.component.scss'], - standalone: false + standalone: false, }) export class ToolbarComponent implements OnInit { - + private mapLoader = inject(MapLoaderService); + events = inject(GlobalEventsService); + private dialog = inject(MatDialog); + private overlayService = inject(OverlayService); + private overlay = inject(Overlay); + private router = inject(Router); + private save = inject(SaveService); + map?: CCMap; loaded = false; error = ''; is3d = false; is3dLoading = false; - + @Output() public loadMapClicked = new EventEmitter(false); - - constructor(private mapLoader: MapLoaderService, - public events: GlobalEventsService, - private dialog: MatDialog, - private overlayService: OverlayService, - private overlay: Overlay, - private router: Router, - private save: SaveService, - ) { - } - + ngOnInit() { - this.mapLoader.tileMap.subscribe(map => { + this.mapLoader.tileMap.subscribe((map) => { this.map = map; }); this.events.loadComplete.subscribe( - () => this.loaded = true, - err => this.error = 'Error: could not load CrossCode assets. Update path in edit/settings' + () => (this.loaded = true), + (err) => + (this.error = + 'Error: could not load CrossCode assets. Update path in edit/settings'), ); - + // Use this to automatically load a map on startup for faster testing if (!environment.production) { console.warn('DEBUG: auto load map'); @@ -59,7 +58,7 @@ export class ToolbarComponent implements OnInit { // this.mapLoader.loadMapByName('tests/all-overlays'); // this.mapLoader.loadMapByName('tests/rhombus-sqr_beach-sw'); this.mapLoader.loadMapByName('autumn-fall/raid/raid-end'); - + // automatically opens event editor // console.log('after map load'); // await new Promise(r => setTimeout(r, 500)); @@ -74,67 +73,75 @@ export class ToolbarComponent implements OnInit { // el[0].getElementsByTagName('input')[0].click(); }); } - - this.events.babylonLoading.subscribe(val => this.is3dLoading = val); + + this.events.babylonLoading.subscribe((val) => (this.is3dLoading = val)); } - + saveMap(saveAs: boolean) { if (!this.map) { throw new Error('no map loaded'); } - + if (saveAs) { this.save.saveMapAs(this.map); } else { this.save.saveMap(this.map); } } - + newMap() { this.overlayService.open(NewMapComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('23vw') .top('calc(64px + 6vh / 2)'), - hasBackdrop: true + hasBackdrop: true, }); } - + openMapSettings() { this.overlayService.open(MapSettingsComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('23vw') .top('calc(64px + 6vh / 2)'), - hasBackdrop: true + hasBackdrop: true, }); } - + generateHeights(forceAll: boolean) { this.events.generateHeights.next(forceAll); } - + offsetMap() { this.overlayService.open(OffsetMapComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('23vw') .top('calc(64px + 6vh / 2)'), - hasBackdrop: true + hasBackdrop: true, }); } - + showSettings() { this.overlayService.open(SettingsComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('23vw') .top('calc(64px + 6vh / 2)'), - hasBackdrop: true + hasBackdrop: true, }); } - + changeTo3d(checked: boolean) { this.is3d = checked; - this.router.navigate([checked ? '3d' : '']); + void this.router.navigate([checked ? '3d' : '']); } - + toggleIngamePreview(checked: boolean) { this.events.showIngamePreview.next(checked); } diff --git a/webapp/src/app/components/toolbar/vec-input/point-input.component.html b/webapp/src/app/components/toolbar/vec-input/point-input.component.html index 34bb244d..386adccb 100644 --- a/webapp/src/app/components/toolbar/vec-input/point-input.component.html +++ b/webapp/src/app/components/toolbar/vec-input/point-input.component.html @@ -1,19 +1,24 @@ -
    +
    X: - + Y: - +
    diff --git a/webapp/src/app/components/toolbar/vec-input/point-input.component.ts b/webapp/src/app/components/toolbar/vec-input/point-input.component.ts index 38866439..a70b482e 100644 --- a/webapp/src/app/components/toolbar/vec-input/point-input.component.ts +++ b/webapp/src/app/components/toolbar/vec-input/point-input.component.ts @@ -1,5 +1,19 @@ -import { Component, ElementRef, HostBinding, Input, OnDestroy, Optional, Self } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, FormsModule, NgControl, ReactiveFormsModule } from '@angular/forms'; +import { + Component, + ElementRef, + HostBinding, + Input, + OnDestroy, + inject, +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + FormsModule, + NgControl, + ReactiveFormsModule, +} from '@angular/forms'; import { Point } from '../../../models/cross-code-map'; import { MatFormFieldControl } from '@angular/material/form-field'; import { Subject } from 'rxjs'; @@ -7,80 +21,86 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion'; @Component({ selector: 'app-point-input', - imports: [ - FormsModule, - ReactiveFormsModule + imports: [FormsModule, ReactiveFormsModule], + providers: [ + { provide: MatFormFieldControl, useExisting: PointInputComponent }, ], - providers: [{provide: MatFormFieldControl, useExisting: PointInputComponent}], templateUrl: './point-input.component.html', - styleUrl: './point-input.component.scss' + styleUrl: './point-input.component.scss', }) -export class PointInputComponent implements MatFormFieldControl, OnDestroy, ControlValueAccessor { +export class PointInputComponent + implements MatFormFieldControl, OnDestroy, ControlValueAccessor +{ + private elementRef = inject>(ElementRef); + ngControl = inject(NgControl, { optional: true, self: true }); + parts: FormGroup; stateChanges = new Subject(); - + @Input() min = 1; - - onChange = (_: any) => { - }; - + + onChange = (_: any) => {}; + @Input() get value(): Point | null { return this.parts.value; } - + set value(point: Point | null) { - point = point || {x: 1, y: 1}; - this.parts.setValue({...point}); + point = point || { x: 1, y: 1 }; + this.parts.setValue({ ...point }); this.stateChanges.next(); } - + static nextId = 0; @HostBinding() id = `point-input-${PointInputComponent.nextId++}`; - + readonly placeholder = ''; - + focused = false; touched = false; - onTouched = () => { - }; - + onTouched = () => {}; + onFocusIn(event: FocusEvent) { if (!this.focused) { this.focused = true; this.stateChanges.next(); } } - + onFocusOut(event: FocusEvent) { - if (!this.elementRef.nativeElement.contains(event.relatedTarget as Element)) { + if ( + !this.elementRef.nativeElement.contains( + event.relatedTarget as Element, + ) + ) { this.touched = true; this.focused = false; this.onTouched(); this.stateChanges.next(); } } - + get empty() { const p = this.parts.value as Point; return isNaN(p.x) && isNaN(p.y); } - + @HostBinding('class.floating') get shouldLabelFloat() { return this.focused || !this.empty; } - + required = false; - + private _disabled = false; - + @Input() get disabled(): boolean { return this._disabled; } - + set disabled(value: BooleanInput) { this._disabled = coerceBooleanProperty(value); if (this._disabled) { @@ -90,56 +110,53 @@ export class PointInputComponent implements MatFormFieldControl, OnDestro } this.stateChanges.next(); } - + get errorState(): boolean { return this.parts.invalid && this.touched; } - + controlType = 'point-input'; - - setDescribedByIds(ids: string[]): void { - } - + + setDescribedByIds(ids: string[]): void {} + onContainerClick(event: MouseEvent): void { if ((event.target as Element).tagName.toLowerCase() !== 'input') { this.elementRef.nativeElement.querySelector('input')?.focus(); } } - - constructor( - fb: FormBuilder, - private elementRef: ElementRef, - @Optional() @Self() public ngControl: NgControl, - ) { + + constructor() { + const fb = inject(FormBuilder); + this.parts = fb.group({ x: 0, - y: 0 + y: 0, }); if (this.ngControl != null) { this.ngControl.valueAccessor = this; } } - + writeValue(p: Point | null): void { this.value = p; } - + registerOnChange(fn: any): void { this.onChange = fn; } - + registerOnTouched(fn: any): void { this.onTouched = fn; } - + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; } - + ngOnDestroy() { this.stateChanges.complete(); } - + update() { this.onChange(this.value); } diff --git a/webapp/src/app/components/widgets/abstract-widget.ts b/webapp/src/app/components/widgets/abstract-widget.ts index bbf69043..8e52a58c 100644 --- a/webapp/src/app/components/widgets/abstract-widget.ts +++ b/webapp/src/app/components/widgets/abstract-widget.ts @@ -1,25 +1,36 @@ -import { Directive, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { AttributeValue, CCEntity } from '../../services/phaser/entities/cc-entity'; +import { + Directive, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, +} from '@angular/core'; +import { + AttributeValue, + CCEntity, +} from '../../services/phaser/entities/cc-entity'; @Directive() -// eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class AbstractWidget implements OnInit, OnChanges { +export abstract class AbstractWidget + implements OnInit, OnChanges +{ @Input() key!: string; @Input() attribute!: Attr; @Input() entity?: CCEntity; @Input() custom?: T; - + // Cannot subscribe to @Input() changeVal = (val: T) => {}; - + @Output() onChange = new EventEmitter(); - + settings: T = {} as any; - + ngOnInit() { this.ngOnChanges(); } - + ngOnChanges(): void { if (this.custom) { this.settings = this.custom; @@ -29,12 +40,17 @@ export abstract class AbstractWidget implements throw new Error('entity and custom settings not defined'); } } - - setSetting(key: string | string[], value: any, updateType = true, parse = false) { + + setSetting( + key: string | string[], + value: any, + updateType = true, + parse = false, + ) { if (parse) { value = JSON.parse(value); } - + if (typeof key === 'string') { this.settings[key as keyof T] = value; } else { @@ -46,14 +62,14 @@ export abstract class AbstractWidget implements } node[key[key.length - 1]] = value; } - + if (updateType) { this.updateType(value); } } - + updateType(value: any) { - this.entity?.updateType(); + void this.entity?.updateType(); this.onChange.emit(value); this.changeVal(value); } diff --git a/webapp/src/app/components/widgets/array-widget/array-widget.component.html b/webapp/src/app/components/widgets/array-widget/array-widget.component.html index 803b4555..813e7c8b 100644 --- a/webapp/src/app/components/widgets/array-widget/array-widget.component.html +++ b/webapp/src/app/components/widgets/array-widget/array-widget.component.html @@ -1,11 +1,13 @@
    - +
    - +
    @for (comp of dynamicComponents; track $index) {
    diff --git a/webapp/src/app/components/widgets/array-widget/array-widget.component.ts b/webapp/src/app/components/widgets/array-widget/array-widget.component.ts index feb359fd..664358a0 100644 --- a/webapp/src/app/components/widgets/array-widget/array-widget.component.ts +++ b/webapp/src/app/components/widgets/array-widget/array-widget.component.ts @@ -1,4 +1,11 @@ -import { Component, OnChanges, Type, ViewChild, ViewContainerRef } from '@angular/core'; +import { + Component, + OnChanges, + Type, + ViewChild, + ViewContainerRef, + inject, +} from '@angular/core'; import { AbstractWidget } from '../abstract-widget'; import { FlexModule } from '@angular/flex-layout'; import { ReactiveFormsModule } from '@angular/forms'; @@ -19,9 +26,7 @@ export interface ArrayAttributes extends AttributeValue { sub: ArrayWidgetSub; } -interface ArraySettings { - [key: string]: unknown[] | undefined; -} +type ArraySettings = Record; interface DynamicComponent { comp: Type; @@ -36,34 +41,32 @@ interface DynamicComponent { MatTooltip, NgComponentOutlet, MatIconButton, - MatIcon + MatIcon, ], templateUrl: './array-widget.component.html', - styleUrls: ['./array-widget.component.scss', '../widget.scss'] + styleUrls: ['./array-widget.component.scss', '../widget.scss'], }) -export class ArrayWidgetComponent extends AbstractWidget implements OnChanges { - - @ViewChild('compContainer', {read: ViewContainerRef, static: true}) compContainer!: ViewContainerRef; - +export class ArrayWidgetComponent + extends AbstractWidget + implements OnChanges +{ + private widgetRegistry = inject(WidgetRegistryService); + + @ViewChild('compContainer', { read: ViewContainerRef, static: true }) + compContainer!: ViewContainerRef; + dynamicComponents: DynamicComponent[] = []; - - constructor( - private widgetRegistry: WidgetRegistryService, - ) { - super(); - } - - + override ngOnChanges() { super.ngOnChanges(); this.init(); } - + override updateType(value: any) { super.updateType(value); this.init(); } - + init() { this.dynamicComponents = []; const arrayOptions = this.settings[this.key] ?? []; @@ -78,24 +81,24 @@ export class ArrayWidgetComponent extends AbstractWidget this.update() - } + changeVal: (val) => this.update(), + }, }); } } - + remove(index: number) { this.settings[this.key]?.splice(index, 1); this.update(); } - + add() { this.settings[this.key]?.push(undefined); this.update(); } - + private update() { this.updateType(this.settings[this.key]); } diff --git a/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.html b/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.html index 1b314f8e..cfdffadb 100644 --- a/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.html +++ b/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.html @@ -1,12 +1,17 @@ -
    +
    - + + color="primary" + class="checkbox" + [checked]="settings[key]" + (change)="setSetting(key, $event.checked)" + >
    diff --git a/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.ts b/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.ts index 7052a5ac..9afdf634 100644 --- a/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.ts +++ b/webapp/src/app/components/widgets/boolean-widget/boolean-widget.component.ts @@ -5,7 +5,6 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-boolean-widget', templateUrl: './boolean-widget.component.html', styleUrls: ['./boolean-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) -export class BooleanWidgetComponent extends AbstractWidget { -} +export class BooleanWidgetComponent extends AbstractWidget {} diff --git a/webapp/src/app/components/widgets/character-widget/character-widget.component.ts b/webapp/src/app/components/widgets/character-widget/character-widget.component.ts index 335a61cb..d188f4e2 100644 --- a/webapp/src/app/components/widgets/character-widget/character-widget.component.ts +++ b/webapp/src/app/components/widgets/character-widget/character-widget.component.ts @@ -1,82 +1,83 @@ -import { Component, Input } from '@angular/core'; -import { OverlayWidget } from '../overlay-widget'; -import { ImageSelectOverlayComponent, PropListGroup } from '../shared/image-select-overlay/image-select-overlay.component'; -import { HttpClientService } from '../../../services/http-client.service'; -import { OverlayService } from '../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; +import { Component, Input, inject } from '@angular/core'; import { lastValueFrom } from 'rxjs'; -import { CharacterSettings, NPC, NpcAttributes } from '../../../services/phaser/entities/registry/npc'; +import { HttpClientService } from '../../../services/http-client.service'; +import { + CharacterSettings, + NpcAttributes, +} from '../../../services/phaser/entities/registry/npc'; import { Helper } from '../../../services/phaser/helper'; +import { OverlayWidget } from '../overlay-widget'; +import { + ImageSelectOverlayComponent, + PropListGroup, +} from '../shared/image-select-overlay/image-select-overlay.component'; @Component({ selector: 'app-character-widget', templateUrl: './character-widget.component.html', styleUrls: ['./character-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class CharacterWidgetComponent extends OverlayWidget { - + private http = inject(HttpClientService); + @Input() onlyFaces = false; - + private props: { prefix: string; full: string; img: string; }[] = []; - + private rightGroup: PropListGroup = { - props: [] + props: [], }; - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); - - constructor( - private http: HttpClientService, - overlayService: OverlayService, - overlay: Overlay - ) { - super(overlayService, overlay); - } - + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.title = 'Character Name'; this.comp.splitBaseName = CharacterWidgetComponent.name; - + this.comp.rightGroups = [this.rightGroup]; - + this.comp.exit.subscribe(() => { this.close(); }); - - this.comp.leftGroup.click = prop => { + + this.comp.leftGroup.click = (prop) => { const prefix = this.settings[this.key]?.split('.')?.[0]; if (prop === prefix) { return; } - this.setPropType(this.props.find(v => v.prefix === prop)?.full ?? '', true); + this.setPropType( + this.props.find((v) => v.prefix === prop)?.full ?? '', + true, + ); }; - this.rightGroup.click = prop => this.setPropType(prop); - + this.rightGroup.click = (prop) => this.setPropType(prop); + let ogProp = this.settings?.[this.key]; if (typeof ogProp !== 'string') { ogProp = ''; } - + this.setPropType(ogProp); await this.updateProps(); this.updateRightSide(); - + return obj.ref; } - + private setPropType(prop: string, updateRightSide = false) { const first = prop.split('.')?.[0]; this.comp.leftGroup.selected = first; @@ -89,58 +90,71 @@ export class CharacterWidgetComponent extends OverlayWidget { this.updateRightSide(); } } - + private updateRightSide() { const prefix = this.settings[this.key]?.split('.')?.[0]; - this.rightGroup.props = this.props.filter(v => v.prefix === prefix).map(v => ({ - name: v.full, - displayName: v.full.split('.')?.[1], - imgSrc: v.img - })); + this.rightGroup.props = this.props + .filter((v) => v.prefix === prefix) + .map((v) => ({ + name: v.full, + displayName: v.full.split('.')?.[1], + imgSrc: v.img, + })); } - + private async updateProps() { this.props = []; this.comp.loading = true; this.comp.leftGroup.props = []; this.rightGroup.props = []; - - let chars = (await lastValueFrom(this.http.getCharacters())).map(v => v.replaceAll('/', '.')); - + + let chars = (await lastValueFrom(this.http.getCharacters())).map((v) => + v.replaceAll('/', '.'), + ); + if (this.onlyFaces) { - chars = await Helper.asyncFilter(chars, async v => { + chars = await Helper.asyncFilter(chars, async (v) => { const name = v.replaceAll('.', '/'); - const settings = await Helper.getJsonPromise('data/characters/' + name) as CharacterSettings | undefined; + const settings = (await Helper.getJsonPromise( + 'data/characters/' + name, + )) as CharacterSettings | undefined; return !!settings?.face; }); } - - this.props = await Promise.all(chars.map(async char => { - const prop: typeof this.props[0] = { - prefix: char.split('.')[0], - full: char, - img: await this.generateImage({characterName: char}, 'NPC') - }; - return prop; - })); - + + this.props = await Promise.all( + chars.map(async (char) => { + const prop: (typeof this.props)[0] = { + prefix: char.split('.')[0], + full: char, + img: await this.generateImage( + { characterName: char }, + 'NPC', + ), + }; + return prop; + }), + ); + this.props.sort((a, b) => a.prefix.localeCompare(b.prefix)); - + for (const prop of this.props) { - let el = this.comp.leftGroup.props.find(v => v.name === prop.prefix); + let el = this.comp.leftGroup.props.find( + (v) => v.name === prop.prefix, + ); if (!el) { el = { name: prop.prefix, imgSrc: prop.img, searchName: '', - count: 0 + count: 0, }; this.comp.leftGroup.props.push(el); } el.searchName += prop.full; el.count = (el.count ?? 0) + 1; } - + this.comp.loading = false; } } diff --git a/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts b/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts index 3340b660..2e970511 100644 --- a/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts +++ b/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts @@ -1,64 +1,59 @@ -import { Component } from '@angular/core'; -import { ImageSelectOverlayComponent } from '../shared/image-select-overlay/image-select-overlay.component'; -import { HttpClientService } from '../../../services/http-client.service'; -import { OverlayService } from '../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; +import { Component, inject } from '@angular/core'; import { Globals } from '../../../services/globals'; -import { OverlayWidget } from '../overlay-widget'; -import { ItemDestruct, ItemDestructAttributes, ItemDestructTypes } from '../../../services/phaser/entities/registry/item-destruct'; -import { Helper } from '../../../services/phaser/helper'; +import { HttpClientService } from '../../../services/http-client.service'; +import { + ItemDestructAttributes, + ItemDestructTypes, +} from '../../../services/phaser/entities/registry/item-destruct'; import { GlobalSettings } from '../../../services/phaser/global-settings'; +import { Helper } from '../../../services/phaser/helper'; +import { OverlayWidget } from '../overlay-widget'; import { PropListCard } from '../shared/image-select-overlay/image-select-card/image-select-card.component'; +import { ImageSelectOverlayComponent } from '../shared/image-select-overlay/image-select-overlay.component'; @Component({ selector: 'app-custom-des-type-widget', templateUrl: './custom-des-type-widget.component.html', styleUrls: ['./custom-des-type-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class CustomDesTypeWidgetComponent extends OverlayWidget { - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); - - constructor( - private http: HttpClientService, - overlayService: OverlayService, - overlay: Overlay - ) { - super(overlayService, overlay); - } - + private http = inject(HttpClientService); + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.showGlobalCheckbox = true; this.comp.title = 'Item Destruct Type'; this.comp.splitBaseName = CustomDesTypeWidgetComponent.name; this.comp.showRightProps = false; - + this.comp.exit.subscribe(() => { this.close(); }); - - this.comp.leftGroup.click = prop => this.setPropType(prop); - this.comp.globalChange.subscribe(_ => { - this.updateProps(); + + this.comp.leftGroup.click = (prop) => this.setPropType(prop); + this.comp.globalChange.subscribe((_) => { + void this.updateProps(); this.setPropType(this.comp.leftGroup.selected); }); - + this.comp.global = !!this.settings.__GLOBAL__; this.setPropType(this.settings.__GLOBAL__ || this.settings.desType); await this.updateProps(); - + return obj.ref; } - + private setPropType(prop?: string) { this.comp.leftGroup.selected = prop; const key = this.getKey(this.comp.global); @@ -68,39 +63,51 @@ export class CustomDesTypeWidgetComponent extends OverlayWidget ({ - name: v._globalSettingKey, - desType: v.desType - })); + const settings = (await Helper.getJsonPromise( + 'data/global-settings', + )) as GlobalSettings.GlobalSettings; + destructibles = Object.values(settings.ENTITY.ItemDestruct).map( + (v) => ({ + name: v._globalSettingKey, + desType: v.desType, + }), + ); } else { - const json = await Globals.jsonLoader.loadJsonMerged('destructibles.json'); - destructibles = Object.keys(json).map(v => ({ + const json = + await Globals.jsonLoader.loadJsonMerged( + 'destructibles.json', + ); + destructibles = Object.keys(json).map((v) => ({ name: v, - desType: v + desType: v, })); } - - await Promise.all(destructibles.map(async destructible => { - props.push({ - name: destructible.name, - imgSrc: await this.generateImage({desType: destructible.desType}, 'ItemDestruct') - }); - })); + + await Promise.all( + destructibles.map(async (destructible) => { + props.push({ + name: destructible.name, + imgSrc: await this.generateImage( + { desType: destructible.desType }, + 'ItemDestruct', + ), + }); + }), + ); props.sort((a, b) => a.name.localeCompare(b.name)); - + this.comp.leftGroup.props = props; this.comp.loading = false; } diff --git a/webapp/src/app/components/widgets/enemy-single-type-widget/enemy-single-type-widget.component.ts b/webapp/src/app/components/widgets/enemy-single-type-widget/enemy-single-type-widget.component.ts index f4c19bac..1ac7c3d0 100644 --- a/webapp/src/app/components/widgets/enemy-single-type-widget/enemy-single-type-widget.component.ts +++ b/webapp/src/app/components/widgets/enemy-single-type-widget/enemy-single-type-widget.component.ts @@ -1,74 +1,72 @@ -import { Component } from '@angular/core'; -import { OverlayWidget } from '../overlay-widget'; -import { ImageSelectOverlayComponent, PropListGroup } from '../shared/image-select-overlay/image-select-overlay.component'; -import { HttpClientService } from '../../../services/http-client.service'; -import { OverlayService } from '../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; +import { Component, inject } from '@angular/core'; import { lastValueFrom } from 'rxjs'; +import { HttpClientService } from '../../../services/http-client.service'; import { EnemyAttributes } from '../../../services/phaser/entities/registry/enemy'; +import { OverlayWidget } from '../overlay-widget'; +import { + ImageSelectOverlayComponent, + PropListGroup, +} from '../shared/image-select-overlay/image-select-overlay.component'; @Component({ selector: 'app-enemy-single-type-widget', templateUrl: './enemy-single-type-widget.component.html', styleUrls: ['./enemy-single-type-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class EnemySingleTypeWidgetComponent extends OverlayWidget { - + private http = inject(HttpClientService); + private props: { prefix: string; full: string; img: string; }[] = []; - + private rightGroup: PropListGroup = { - props: [] + props: [], }; - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); - - constructor( - private http: HttpClientService, - overlayService: OverlayService, - overlay: Overlay - ) { - super(overlayService, overlay); - } - + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.title = 'Enemy Type'; this.comp.splitBaseName = EnemySingleTypeWidgetComponent.name; - + this.comp.rightGroups = [this.rightGroup]; - + this.comp.exit.subscribe(() => { this.close(); }); - - this.comp.leftGroup.click = prop => { + + this.comp.leftGroup.click = (prop) => { const prefix = this.settings[this.key]?.split('.')?.[0]; if (prop === prefix) { return; } - this.setPropType(this.props.find(v => v.prefix === prop)?.full ?? '', true); + this.setPropType( + this.props.find((v) => v.prefix === prop)?.full ?? '', + true, + ); }; - this.rightGroup.click = prop => this.setPropType(prop); - + this.rightGroup.click = (prop) => this.setPropType(prop); + this.setPropType(this.settings[this.key] ?? ''); await this.updateProps(); this.updateRightSide(); - + return obj.ref; } - + private setPropType(prop: string, updateRightSide = false) { const first = prop.split('.')?.[0]; this.comp.leftGroup.selected = first; @@ -81,50 +79,61 @@ export class EnemySingleTypeWidgetComponent extends OverlayWidget { this.updateRightSide(); } } - + private updateRightSide() { const prefix = this.settings[this.key]?.split('.')?.[0]; - this.rightGroup.props = this.props.filter(v => v.prefix === prefix).map(v => ({ - name: v.full, - displayName: v.full.split('.')?.[1], - imgSrc: v.img - })); + this.rightGroup.props = this.props + .filter((v) => v.prefix === prefix) + .map((v) => ({ + name: v.full, + displayName: v.full.split('.')?.[1], + imgSrc: v.img, + })); } - + private async updateProps() { this.props = []; this.comp.loading = true; this.comp.leftGroup.props = []; this.rightGroup.props = []; - - const enemies = (await lastValueFrom(this.http.getEnemies())).map(v => v.replaceAll('/', '.')); - - this.props = await Promise.all(enemies.map(async enemy => { - const prop: typeof this.props[0] = { - prefix: enemy.split('.')[0], - full: enemy, - img: await this.generateImage({enemyInfo: {type: enemy}}, 'Enemy') - }; - return prop; - })); - + + const enemies = (await lastValueFrom(this.http.getEnemies())).map((v) => + v.replaceAll('/', '.'), + ); + + this.props = await Promise.all( + enemies.map(async (enemy) => { + const prop: (typeof this.props)[0] = { + prefix: enemy.split('.')[0], + full: enemy, + img: await this.generateImage( + { enemyInfo: { type: enemy } }, + 'Enemy', + ), + }; + return prop; + }), + ); + this.props.sort((a, b) => a.prefix.localeCompare(b.prefix)); - + for (const prop of this.props) { - let el = this.comp.leftGroup.props.find(v => v.name === prop.prefix); + let el = this.comp.leftGroup.props.find( + (v) => v.name === prop.prefix, + ); if (!el) { el = { name: prop.prefix, searchName: '', imgSrc: prop.img, - count: 0 + count: 0, }; this.comp.leftGroup.props.push(el); } el.searchName += prop.full; el.count = (el.count ?? 0) + 1; } - + this.comp.loading = false; } } diff --git a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.html b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.html index 257809f8..3962e802 100644 --- a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.html +++ b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.html @@ -1,24 +1,78 @@
    - - - - - - - - - - - - - + + + + + + + + + + + + +
    diff --git a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.ts b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.ts index 795b68db..c56c2c34 100644 --- a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.ts +++ b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-overlay/enemy-type-overlay.component.ts @@ -6,21 +6,20 @@ import { AbstractWidget } from '../../abstract-widget'; selector: 'app-enemy-type-overlay', templateUrl: './enemy-type-overlay.component.html', styleUrls: ['./enemy-type-overlay.component.scss', '../../widget.scss'], - standalone: false + standalone: false, }) -export class EnemyTypeWidgetOverlayComponent extends AbstractWidget implements OnInit { +export class EnemyTypeWidgetOverlayComponent + extends AbstractWidget + implements OnInit +{ @Output() exit = new EventEmitter(); - - readonly partyOptions = [ - 'PLAYER', - 'ENEMY', - 'OTHER' - ]; + + readonly partyOptions = ['PLAYER', 'ENEMY', 'OTHER']; readonly partyAttributes: AttributeValue = { type: 'String', description: '', withNull: true, - options: this.makeOptions(this.partyOptions) + options: this.makeOptions(this.partyOptions), }; readonly faceOptions = [ 'NORTH', @@ -30,42 +29,42 @@ export class EnemyTypeWidgetOverlayComponent extends AbstractWidget implements O 'NORTH_EAST', 'SOUTH_EAST', 'SOUTH_WEST', - 'NORTH_WEST' + 'NORTH_WEST', ]; readonly faceAttributes: AttributeValue = { type: 'String', description: '', withNull: true, - options: this.makeOptions(this.faceOptions) + options: this.makeOptions(this.faceOptions), }; - + private makeOptions(options: string[]) { - const out: { [key: string]: 0 } = {}; + const out: Record = {}; for (const opt of options) { out[opt] = 0; } return out; } - + constructor() { super(); } - + override ngOnInit() { super.ngOnInit(); if (!this.settings[this.key]) { this.settings[this.key] = {}; } } - + close() { this.exit.emit(); } - + stringify(arg: unknown): string { return JSON.stringify(arg); } - + parse(arg: string): unknown { try { return JSON.parse(arg); diff --git a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.html b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.html index 445af00a..ae60a1fe 100644 --- a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.html +++ b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.html @@ -1,10 +1,16 @@ -
    - - + +
    diff --git a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.ts b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.ts index 69c4bd51..dec31fdd 100644 --- a/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.ts +++ b/webapp/src/app/components/widgets/enemy-type-widget/enemy-type-widget.component.ts @@ -6,28 +6,29 @@ import { OverlayWidget } from '../overlay-widget'; selector: 'app-enemy-type-widget', templateUrl: './enemy-type-widget.component.html', styleUrls: ['./enemy-type-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class EnemyTypeWidgetComponent extends OverlayWidget { - override async openInternal() { const obj = this.overlayService.open(EnemyTypeWidgetOverlayComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('330px') .top('calc(64px + 6vh / 2)'), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + obj.instance.entity = this.entity; obj.instance.key = this.key; obj.instance.attribute = this.attribute; obj.instance.custom = this.custom; - + obj.instance.exit.subscribe(() => { this.close(); }); - + return obj.ref; } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts index 1a0d1f60..1a780f89 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts @@ -1,60 +1,67 @@ import { Overlay } from '@angular/cdk/overlay'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { Observable, Subject } from 'rxjs'; +import { JsonLoaderService } from '../../../../../services/json-loader.service'; import { ListSearchOverlayComponent } from '../../../../dialogs/list-search-overlay/list-search-overlay.component'; import { OverlayRefControl } from '../../../../dialogs/overlay/overlay-ref-control'; import { OverlayService } from '../../../../dialogs/overlay/overlay.service'; import { AbstractEvent } from '../../event-registry/abstract-event'; -import { EventRegistryService } from '../../event-registry/event-registry.service'; -import { JsonLoaderService } from '../../../../../services/json-loader.service'; import { ActionsJson, EventsJson } from '../../event-registry/default-event'; +import { EventRegistryService } from '../../event-registry/event-registry.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AddEventService { + private eventRegistry = inject(EventRegistryService); + private overlayService = inject(OverlayService); + private overlay = inject(Overlay); + private domSanitizer = inject(DomSanitizer); + private jsonLoader = inject(JsonLoaderService); + private selectedEvent = new Subject>(); private actionStep = false; - + events: string[] = []; actions: string[] = []; - + private ref?: OverlayRefControl; - - constructor( - private eventRegistry: EventRegistryService, - private overlayService: OverlayService, - private overlay: Overlay, - private domSanitizer: DomSanitizer, - private jsonLoader: JsonLoaderService, - ) { - this.init(); + + constructor() { + void this.init(); } - + private async init() { - const events = await this.jsonLoader.loadJsonMerged('events.json'); - const actions = await this.jsonLoader.loadJsonMerged('actions.json'); - + const events = + await this.jsonLoader.loadJsonMerged('events.json'); + const actions = + await this.jsonLoader.loadJsonMerged('actions.json'); + const eventNames = Object.keys(events); const registry = Object.keys(this.eventRegistry.getAll()); const eventSet = new Set([...registry, ...eventNames]); - + this.events = Array.from(eventSet); this.events.sort(); - + this.actions = Object.keys(actions); this.actions.sort(); } - - showAddEventMenu(pos: { left: string, top: string }, actionStep: boolean): Observable> { + + showAddEventMenu( + pos: { left: string; top: string }, + actionStep: boolean, + ): Observable> { if (this.ref && this.ref.isOpen()) { return this.selectedEvent.asObservable(); } this.actionStep = actionStep; const obj = this.overlayService.open(ListSearchOverlayComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left(pos.left) .top(pos.top), height: '90vh', @@ -63,34 +70,40 @@ export class AddEventService { }); this.ref = obj.ref; this.ref.onClose.subscribe(() => this.resetSubject()); - + obj.instance.list = actionStep ? this.actions : this.events; obj.instance.animation = 'slide'; - obj.instance.selected.subscribe((v: string) => { - this.select(v); - this.close(); - }, () => this.close()); - + obj.instance.selected.subscribe( + (v: string) => { + this.select(v); + this.close(); + }, + () => this.close(), + ); + return this.selectedEvent.asObservable(); } - + private close() { if (this.ref) { this.ref.close(); } } - + select(event: string) { const clss = this.eventRegistry.getEvent(event); - const instance: AbstractEvent = new clss(this.domSanitizer, {type: event}, this.actionStep); + const instance: AbstractEvent = new clss( + this.domSanitizer, + { type: event }, + this.actionStep, + ); instance.generateNewData(); instance.update(); this.selectedEvent.next(instance); } - + private resetSubject() { this.selectedEvent.complete(); this.selectedEvent = new Subject>(); - } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.html b/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.html index e7128a10..417e65fe 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.html +++ b/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.html @@ -8,9 +8,8 @@

    {{ event?.data?.type }}

    @if (warning) {
    WARNING: type cannot be changed -

    +

    }
    - diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.ts b/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.ts index 5c9d3a72..a53a9d57 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/detail/event-detail.component.ts @@ -1,4 +1,14 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, Output, ViewChild, ViewContainerRef } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnDestroy, + Output, + ViewChild, + ViewContainerRef, + inject, +} from '@angular/core'; import { Subscription } from 'rxjs'; import { HostDirective } from '../../../../../directives/host.directive'; import { AttributeValue } from '../../../../../services/phaser/entities/cc-entity'; @@ -14,60 +24,60 @@ export type RefreshType = 'Node' | 'Full'; selector: 'app-event-detail', templateUrl: './event-detail.component.html', styleUrls: ['./event-detail.component.scss'], - standalone: false + standalone: false, }) export class EventDetailComponent implements OnDestroy { - @ViewChild(HostDirective, {static: true}) appHost!: HostDirective; - + private widgetRegistry = inject(WidgetRegistryService); + private helper = inject(EventHelperService); + private ref = inject(ChangeDetectorRef); + + @ViewChild(HostDirective, { static: true }) appHost!: HostDirective; + @Input() event!: AbstractEvent; @Output() close = new EventEmitter(); @Output() refresh = new EventEmitter(); - + newData: any; unknownObj?: { data: any }; warning = false; - + private changeSubscriptions: Subscription[] = []; - - constructor( - private widgetRegistry: WidgetRegistryService, - private helper: EventHelperService, - private ref: ChangeDetectorRef, - ) { - } - + ngOnDestroy(): void { this.clearSubscriptions(); } - + closeDetails(): void { this.close.emit(); } - + public loadEvent(event: AbstractEvent) { if (this.event !== event) { this.event = event; this.loadSettings(); } } - + private clearSubscriptions() { for (const sub of this.changeSubscriptions) { sub.unsubscribe(); } this.changeSubscriptions = []; } - + private loadSettings() { this.clearSubscriptions(); - + const ref = this.appHost.viewContainerRef; - + ref.clear(); - + const exported = this.event.export(); - this.newData = this.helper.getEventFromType(exported, this.event.actionStep).data; - + this.newData = this.helper.getEventFromType( + exported, + this.event.actionStep, + ).data; + if (!this.event.getAttributes) { console.log('wtf', this); } @@ -79,34 +89,50 @@ export class EventDetailComponent implements OnDestroy { }); } else { this.warning = true; - this.unknownObj = {data: this.newData}; - const instance = this.generateWidget(this.unknownObj, 'data', { - type: '', - description: '' - }, ref) as JsonWidgetComponent; + this.unknownObj = { data: this.newData }; + const instance = this.generateWidget( + this.unknownObj, + 'data', + { + type: '', + description: '', + }, + ref, + ) as JsonWidgetComponent; instance.noPropName = true; } - + this.ref.detectChanges(); } - - private generateWidget(data: any, key: string, val: AttributeValue, ref: ViewContainerRef) { - const componentRef = ref.createComponent(this.widgetRegistry.getWidget(val.type)); - const instance = componentRef.instance; + + private generateWidget( + data: any, + key: string, + val: AttributeValue, + ref: ViewContainerRef, + ) { + const componentRef = ref.createComponent( + this.widgetRegistry.getWidget(val.type), + ); + const instance = componentRef.instance as AbstractWidget; instance.custom = data; instance.key = key; instance.attribute = val; const sub = instance.onChange.subscribe(() => this.update()); - + this.changeSubscriptions.push(sub); return instance; } - + private update() { this.event.data = this.unknownObj ? this.unknownObj.data : this.newData; const previousChildCount = this.event.children.length; this.event.update(); const childCount = this.event.children.length; - this.refresh.emit((childCount > 0 || previousChildCount !== childCount) ? 'Full' : 'Node'); + this.refresh.emit( + childCount > 0 || previousChildCount !== childCount + ? 'Full' + : 'Node', + ); } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.html b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.html index 46cf4875..4f537522 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.html +++ b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.html @@ -1,43 +1,44 @@ -
    +
    -
    + (cdkDropListDropped)="drop($event)" + > + tabindex="0" + > + (contextmenu)="openAddMenu($event, node)" + > @@ -45,7 +46,11 @@
    - +
    diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts index f7868947..13c960d5 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts @@ -1,13 +1,33 @@ import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; import { FlatTreeControl } from '@angular/cdk/tree'; -import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'; -import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; -import { destructureEventArray, EventArray } from '../../../../../models/events'; +import { + ChangeDetectionStrategy, + Component, + ElementRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewChild, +} from '@angular/core'; +import { + MatTreeFlatDataSource, + MatTreeFlattener, +} from '@angular/material/tree'; +import { + destructureEventArray, + EventArray, +} from '../../../../../models/events'; import { SettingsService } from '../../../../../services/settings.service'; import { SplitPaneComponent } from '../../../../split-pane/split-pane.component'; import { AbstractEvent, EventType } from '../../event-registry/abstract-event'; import { AddEventService } from '../add/add-event.service'; -import { EventDetailComponent, RefreshType } from '../detail/event-detail.component'; +import { + EventDetailComponent, + RefreshType, +} from '../detail/event-detail.component'; import { EventDisplay } from '../event-display.model'; import { EventHelperService } from '../event-helper.service'; import { EventHistory } from './event-history'; @@ -17,164 +37,193 @@ import { EventHistory } from './event-history'; templateUrl: './event-editor.component.html', styleUrls: ['./event-editor.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false + standalone: false, }) export class EventEditorComponent implements OnChanges, OnInit { + private helper = inject(EventHelperService); + private addEvent = inject(AddEventService); + private settingsService = inject(SettingsService); + private static globalBase = 0; - + @ViewChild('splitpane') splitPane?: SplitPaneComponent; - @ViewChild('eventDetail', {static: true}) eventDetail?: unknown; //EventDetailComponent but it errors for some reason - @ViewChild('eventTree', {read: ElementRef}) eventTree?: ElementRef; - - @Input() eventData: EventArray | unknown = []; + @ViewChild('eventDetail', { static: true }) eventDetail?: unknown; //EventDetailComponent but it errors for some reason + @ViewChild('eventTree', { read: ElementRef }) + eventTree?: ElementRef; + + @Input() eventData: EventArray = []; @Input() actionStep = false; - + @Output() eventsChanged = new EventEmitter(); - + get base() { return EventEditorComponent.globalBase; } - + set base(value: number) { EventEditorComponent.globalBase = value; } - + detailsShown = false; wrapText!: boolean; - - treeControl = new FlatTreeControl(e => e.level, e => e.children != null); + + treeControl = new FlatTreeControl( + (e) => e.level, + (e) => e.children != null, + ); private treeFlattener = new MatTreeFlattener( (node: EventDisplay, level: number) => { node.level = level; return node; }, - e => e.level, - e => e.children != null, - e => this.convertNodes(e.children!)); - dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); - + (e) => e.level, + (e) => e.children != null, + (e) => this.convertNodes(e.children!), + ); + dataSource = new MatTreeFlatDataSource( + this.treeControl, + this.treeFlattener, + ); + private history = new EventHistory(); private workingData: AbstractEvent[] = []; private selectedNode?: EventDisplay; private shownNode?: EventDisplay; private copiedNode?: EventDisplay; - - constructor( - private helper: EventHelperService, - private addEvent: AddEventService, - private settingsService: SettingsService - ) { - } - + ngOnInit() { this.wrapText = this.settingsService.getSettings().wrapEventEditorLines; } - + ngOnChanges() { - const eventCopy: EventArray = JSON.parse(JSON.stringify(this.eventData)); + const eventCopy: EventArray = JSON.parse( + JSON.stringify(this.eventData), + ); try { - const {events} = destructureEventArray(eventCopy); - this.workingData = events?.map((val: EventType) => this.helper.getEventFromType(val, this.actionStep)) ?? []; + const { events } = destructureEventArray(eventCopy); + this.workingData = + events?.map((val: EventType) => + this.helper.getEventFromType(val, this.actionStep), + ) ?? []; } catch (destructuringError) { this.workingData = []; if (destructuringError instanceof TypeError) { - console.error(`Error while reading events, invalid format. Using empty event array as fallback.\n\nException:\n${destructuringError.stack}`); + console.error( + `Error while reading events, invalid format. Using empty event array as fallback.\n\nException:\n${destructuringError.stack}`, + ); } else { - console.error('Unknown exception while reading events. Using empty event array as fallback.\n\nException in message below.'); + console.error( + 'Unknown exception while reading events. Using empty event array as fallback.\n\nException in message below.', + ); console.error(destructuringError); } } this.refreshAll(); } - + show() { this.detailsShown = false; } - - sortPredicate(index: number, item: CdkDrag, drop: CdkDropList) { + + sortPredicate( + index: number, + item: CdkDrag, + drop: CdkDropList, + ) { //TODO: Prevent placeholder if element cannot go there (this.isChildOf(...)) return index < this.treeControl.dataNodes.length - 1; } - + refresh(refreshType: RefreshType) { if (this.shownNode) { this.shownNode.text = this.shownNode.data?.info ?? ' '; this.shownNode.changeDetector?.detectChanges(); - + if (refreshType === 'Full') { this.refreshAll(); } } } - + hideDetails(): void { this.detailsShown = false; } - + export(): EventType[] { - return this.workingData.map(event => event.export()); + return this.workingData.map((event) => event.export()); } - + drop(event: CdkDragDrop) { if (event.currentIndex === event.previousIndex) { return; } - + const moved = event.item.data as EventDisplay; - const belowIndex = event.currentIndex + (event.currentIndex > event.previousIndex ? 1 : 0); //Add 1 to compensate for a missing this.treeControl.dataNodes.splice + const belowIndex = + event.currentIndex + + (event.currentIndex > event.previousIndex ? 1 : 0); //Add 1 to compensate for a missing this.treeControl.dataNodes.splice const below = this.treeControl.dataNodes[belowIndex]; - + if (this.isChildOf(below, moved)) { return; } - + const fromParent = this.getParent(moved); const toParent = this.getParent(below); - + this.history.move(fromParent, toParent); - + fromParent.splice(fromParent.indexOf(moved.data!), 1); - + const toIndex = toParent.indexOf(below?.data!); - toParent.splice(toIndex >= 0 ? toIndex : toParent.length, 0, moved.data!); - + toParent.splice( + toIndex >= 0 ? toIndex : toParent.length, + 0, + moved.data!, + ); + this.refreshAll(); this.focus(); } - + eventClicked(_: MouseEvent, node: EventDisplay | null) { this.select(node); } - + openAddMenu(event: Event, node: EventDisplay | null) { this.select(node); - + event.stopPropagation(); if (event.cancelable) { event.preventDefault(); } - - this.addEvent.showAddEventMenu({ - left: 'calc(18vw)', - top: '6vh' - }, this.actionStep).subscribe(newEvent => { - this.history.add(this.getParent(this.selectedNode)); - - const index = this.getIndex(this.selectedNode); - const parent = this.getParent(this.selectedNode); - parent.splice(index, 0, newEvent); - - this.refreshAll(); - this.selectAbstractEvent(newEvent); - this.focus(); - }); + + this.addEvent + .showAddEventMenu( + { + left: 'calc(18vw)', + top: '6vh', + }, + this.actionStep, + ) + .subscribe((newEvent) => { + this.history.add(this.getParent(this.selectedNode)); + + const index = this.getIndex(this.selectedNode); + const parent = this.getParent(this.selectedNode); + parent.splice(index, 0, newEvent); + + this.refreshAll(); + this.selectAbstractEvent(newEvent); + this.focus(); + }); } - + keyPress(event: KeyboardEvent) { if (event.cancelable) { event.preventDefault(); } - + switch (event.code) { case 'ArrowUp': case 'ArrowLeft': @@ -194,7 +243,7 @@ export class EventEditorComponent implements OnChanges, OnInit { this.deselect(); return; } - + if (event.ctrlKey) { switch (event.key.toLowerCase()) { case 'c': @@ -217,36 +266,38 @@ export class EventEditorComponent implements OnChanges, OnInit { } } } - + private refreshTree() { this.dataSource.data = this.convertNodes(this.workingData); this.treeControl.expandAll(); } - + private focus() { this.eventTree?.nativeElement.focus(); } - - private getParent(node: EventDisplay | null | undefined): AbstractEvent[] { + + private getParent( + node: EventDisplay | null | undefined, + ): AbstractEvent[] { return node?.parent ?? this.workingData; } - + private select(node: EventDisplay | null | undefined) { if (!node) { return; } - + if (this.selectedNode) { this.selectedNode.isSelected = false; this.selectedNode.changeDetector?.detectChanges(); } - + node.isSelected = true; node.changeDetector?.detectChanges(); this.selectedNode = node; this.showEvent(node); } - + private showEvent(node: EventDisplay) { if (node.data) { (this.eventDetail as EventDetailComponent).loadEvent(node.data); @@ -255,102 +306,113 @@ export class EventEditorComponent implements OnChanges, OnInit { this.history.select(node.data); } } - + private selectAbstractEvent(event: AbstractEvent) { - const node = this.treeControl.dataNodes.find(n => n.data === event)!; + const node = this.treeControl.dataNodes.find((n) => n.data === event)!; this.select(node); } - + private selectUp() { const index = this.treeControl.dataNodes.indexOf(this.selectedNode!); - const finalIndex = index <= 0 ? this.treeControl.dataNodes.length - 1 : index - 1; + const finalIndex = + index <= 0 ? this.treeControl.dataNodes.length - 1 : index - 1; this.select(this.treeControl.dataNodes[finalIndex]); } - + private selectDown() { const index = this.treeControl.dataNodes.indexOf(this.selectedNode!); - const finalIndex = index < 0 || index === this.treeControl.dataNodes.length - 1 ? 0 : index + 1; + const finalIndex = + index < 0 || index === this.treeControl.dataNodes.length - 1 + ? 0 + : index + 1; this.select(this.treeControl.dataNodes[finalIndex]); } - + private deselect() { this.detailsShown = false; this.shownNode = undefined; - + if (this.selectedNode) { this.selectedNode.isSelected = false; this.selectedNode.changeDetector?.detectChanges(); this.selectedNode = undefined; } } - + private delete() { if (!this.selectedNode?.data) { return; } - + this.history.delete(this.selectedNode.parent); - - const globalIndex = this.treeControl.dataNodes.findIndex(n => n === this.selectedNode); - + + const globalIndex = this.treeControl.dataNodes.findIndex( + (n) => n === this.selectedNode, + ); + const index = this.getIndex(this.selectedNode); const parent = this.getParent(this.selectedNode); parent.splice(index, 1); - + this.refreshAll(); - + if (this.shownNode === this.selectedNode) { this.detailsShown = false; this.shownNode = undefined; } - - const selectIndex = globalIndex >= this.treeControl.dataNodes.length ? 0 : globalIndex; + + const selectIndex = + globalIndex >= this.treeControl.dataNodes.length ? 0 : globalIndex; this.select(this.treeControl.dataNodes[selectIndex]); - + this.focus(); } - + private copy() { if (this.selectedNode?.data) { this.copiedNode = this.selectedNode; } } - + private paste() { if (this.copiedNode) { this.history.add(this.getParent(this.selectedNode)); - - const cpy = JSON.parse(JSON.stringify(this.copiedNode.data?.export())); + + const cpy = JSON.parse( + JSON.stringify(this.copiedNode.data?.export()), + ); const event = this.helper.getEventFromType(cpy, this.actionStep); - - const index = !this.selectedNode ? 0 : this.getIndex(this.selectedNode!); + + const index = !this.selectedNode + ? 0 + : this.getIndex(this.selectedNode); const parent = this.getParent(this.selectedNode); parent.splice(index, 0, event); - + this.refreshAll(); this.selectAbstractEvent(event); this.focus(); } } - + private undo() { this.history.undo(); this.refreshAll(); this.focus(); } - + private redo() { this.history.redo(); this.refreshAll(); this.focus(); } - + private refreshAll() { const selected = this.selectedNode?.data; const selectedParent = this.selectedNode?.parent; const shown = this.detailsShown; const shownData = this.shownNode?.data; - + this.refreshTree(); this.deselect(); if (shownData) { @@ -358,32 +420,36 @@ export class EventEditorComponent implements OnChanges, OnInit { } if (selectedParent) { //Similar to this.selectAbstractEvent but also handles undefined as data - const node = this.treeControl.dataNodes.find(n => n.parent === selectedParent && n.data === selected); + const node = this.treeControl.dataNodes.find( + (n) => n.parent === selectedParent && n.data === selected, + ); this.select(node); } this.detailsShown = shown; this.eventsChanged.emit(this.export()); } - + private getIndex(event: EventDisplay | null | undefined) { const parent = this.getParent(event); const index = parent.indexOf(event?.data!); return index === -1 ? parent.length : index; } - + private isChildOf(child: EventDisplay, parent: EventDisplay): boolean { let node: EventDisplay | undefined = child; while (node) { if (node === parent || (node.data && node.data === parent.data)) { return true; } - - node = this.treeControl.dataNodes.find(n => n.children === node!.parent); + + node = this.treeControl.dataNodes.find( + (n) => n.children === node!.parent, + ); } - + return false; } - + private convertNodes(nodes: AbstractEvent[]): EventDisplay[] { const result: EventDisplay[] = []; for (const node of nodes) { @@ -396,24 +462,26 @@ export class EventEditorComponent implements OnChanges, OnInit { parent: nodes, level: 0, }; - - if (node.children - && node.children.length > 0 - && node.children[0].title == null) { + + if ( + node.children && + node.children.length > 0 && + node.children[0].title == null + ) { entry.children = node.children[0].events; } - + result.push(entry); - + if (node.children == null) { continue; } - + for (const child of node.children) { if (!child.title) { continue; } - + result.push({ text: child.title, draggable: child.draggable || false, @@ -426,7 +494,7 @@ export class EventEditorComponent implements OnChanges, OnInit { }); } } - + result.push({ text: ' ', draggable: false, @@ -435,7 +503,7 @@ export class EventEditorComponent implements OnChanges, OnInit { level: 0, parent: nodes, }); - + return result; } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-history.ts b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-history.ts index ae2e873c..fed77764 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-history.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-history.ts @@ -61,9 +61,8 @@ export class EventHistory { }); this.undoHistory = []; - } - + public undo(): void { this.commitEdits(); @@ -114,48 +113,52 @@ export class EventHistory { private applyRestorePoint(rp: RestorePoint): void { switch (rp.type) { - case 'edit': - rp.ref.data = JSON.parse(rp.data); - rp.ref.update(); - if (this.selected?.ref === rp.ref) { - this.selected = rp; - } - break; - case 'move': - Object.assign(rp.toRef, rp.to); - Object.assign(rp.fromRef, rp.from); - rp.toRef.length = rp.to.length; - rp.fromRef.length = rp.from.length; - break; - case 'addDelete': - Object.assign(rp.fromRef, rp.from); - rp.fromRef.length = rp.from.length; - break; + case 'edit': + rp.ref.data = JSON.parse(rp.data); + rp.ref.update(); + if (this.selected?.ref === rp.ref) { + this.selected = rp; + } + break; + case 'move': + Object.assign(rp.toRef, rp.to); + Object.assign(rp.fromRef, rp.from); + rp.toRef.length = rp.to.length; + rp.fromRef.length = rp.from.length; + break; + case 'addDelete': + Object.assign(rp.fromRef, rp.from); + rp.fromRef.length = rp.from.length; + break; } } private invertRestorePoint(rp: RestorePoint): RestorePoint { switch (rp.type) { - case 'edit': - return this.createRestorePoint(rp.ref); - case 'move': - return { - type: 'move', - from: Object.assign([], rp.fromRef), - fromRef: rp.fromRef, - to: Object.assign([], rp.toRef), - toRef: rp.toRef, - }; - case 'addDelete': - return { - type: 'addDelete', - from: Object.assign([], rp.fromRef), - fromRef: rp.fromRef, - }; + case 'edit': + return this.createRestorePoint(rp.ref); + case 'move': + return { + type: 'move', + from: Object.assign([], rp.fromRef), + fromRef: rp.fromRef, + to: Object.assign([], rp.toRef), + toRef: rp.toRef, + }; + case 'addDelete': + return { + type: 'addDelete', + from: Object.assign([], rp.fromRef), + fromRef: rp.fromRef, + }; } } - private hasChanged(selected: EditRestorePoint | undefined): selected is EditRestorePoint { - return !!selected && JSON.stringify(selected.ref.data) !== selected.data; + private hasChanged( + selected: EditRestorePoint | undefined, + ): selected is EditRestorePoint { + return ( + !!selected && JSON.stringify(selected.ref.data) !== selected.data + ); } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/event-display.model.ts b/webapp/src/app/components/widgets/event-widget/event-editor/event-display.model.ts index 3dfa84a7..015f34f2 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/event-display.model.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/event-display.model.ts @@ -4,12 +4,12 @@ import { AbstractEvent } from '../event-registry/abstract-event'; export interface EventDisplay { text: string; draggable: boolean; - isActionStep: boolean; - isSelected: boolean; - parent: AbstractEvent[]; + isActionStep: boolean; + isSelected: boolean; + parent: AbstractEvent[]; data?: AbstractEvent; - children?: AbstractEvent[]; - - level: number; - changeDetector?: ChangeDetectorRef; + children?: AbstractEvent[]; + + level: number; + changeDetector?: ChangeDetectorRef; } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts index c36bab1f..d641ebc5 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts @@ -1,51 +1,72 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { BehaviorSubject } from 'rxjs'; import { AbstractEvent, EventType } from '../event-registry/abstract-event'; import { EventRegistryService } from '../event-registry/event-registry.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EventHelperService { - - selectedEvent: BehaviorSubject | null> = new BehaviorSubject | null>(null); - - constructor( - private eventRegistry: EventRegistryService, - private domSanitizer: DomSanitizer, - ) { - } - - public getEventFromType(val: EventType, actionStep: boolean): AbstractEvent { + private eventRegistry = inject(EventRegistryService); + private domSanitizer = inject(DomSanitizer); + + selectedEvent: BehaviorSubject | null> = + new BehaviorSubject | null>(null); + + public getEventFromType( + val: EventType, + actionStep: boolean, + ): AbstractEvent { const eventClass = this.eventRegistry.getEvent(val.type); - const instance: AbstractEvent = new eventClass(this.domSanitizer, val, actionStep); - + const instance: AbstractEvent = new eventClass( + this.domSanitizer, + val, + actionStep, + ); + if (val.type === 'IF') { const valIf = val as any; - valIf.thenStep = valIf.thenStep.map((v: EventType) => this.getEventFromType(v, actionStep)); + valIf.thenStep = valIf.thenStep.map((v: EventType) => + this.getEventFromType(v, actionStep), + ); if (valIf.elseStep) { - valIf.elseStep = valIf.elseStep.map((v: EventType) => this.getEventFromType(v, actionStep)); + valIf.elseStep = valIf.elseStep.map((v: EventType) => + this.getEventFromType(v, actionStep), + ); } - } else if (val.type === 'SHOW_CHOICE' || val.type === 'SHOW_MODAL_CHOICE') { + } else if ( + val.type === 'SHOW_CHOICE' || + val.type === 'SHOW_MODAL_CHOICE' + ) { const valChoice = val as any; valChoice.options.forEach((option: any, index: number) => { - valChoice[index] = valChoice[index].map((v: EventType) => this.getEventFromType(v, actionStep)); + valChoice[index] = valChoice[index].map((v: EventType) => + this.getEventFromType(v, actionStep), + ); }); } else if (val.type === 'START_NPC_TRADE_MENU') { const valTradeMenu = val as any; if (valTradeMenu.withBranches) { - valTradeMenu.traded = (valTradeMenu.traded ?? []).map((v: EventType) => this.getEventFromType(v, actionStep)); - valTradeMenu.canceled = (valTradeMenu.canceled ?? []).map((v: EventType) => this.getEventFromType(v, actionStep)); + valTradeMenu.traded = (valTradeMenu.traded ?? []).map( + (v: EventType) => this.getEventFromType(v, actionStep), + ); + valTradeMenu.canceled = (valTradeMenu.canceled ?? []).map( + (v: EventType) => this.getEventFromType(v, actionStep), + ); } } else if (val.type === 'OPEN_QUEST_DIALOG') { const valQuestDialog = val as any; - valQuestDialog.accepted = valQuestDialog.accepted.map((v: EventType) => this.getEventFromType(v, actionStep)); - valQuestDialog.declined = valQuestDialog.declined.map((v: EventType) => this.getEventFromType(v, actionStep)); + valQuestDialog.accepted = valQuestDialog.accepted.map( + (v: EventType) => this.getEventFromType(v, actionStep), + ); + valQuestDialog.declined = valQuestDialog.declined.map( + (v: EventType) => this.getEventFromType(v, actionStep), + ); } - + instance.update(); - + return instance; } } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/row-text/row-text.component.html b/webapp/src/app/components/widgets/event-widget/event-editor/row-text/row-text.component.html index eebe232c..8ed50424 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/row-text/row-text.component.html +++ b/webapp/src/app/components/widgets/event-widget/event-editor/row-text/row-text.component.html @@ -1,4 +1,7 @@ -
    diff --git a/webapp/src/app/components/widgets/inputs/input-with-button/input-with-button.component.ts b/webapp/src/app/components/widgets/inputs/input-with-button/input-with-button.component.ts index 46436efc..ef37dd34 100644 --- a/webapp/src/app/components/widgets/inputs/input-with-button/input-with-button.component.ts +++ b/webapp/src/app/components/widgets/inputs/input-with-button/input-with-button.component.ts @@ -9,7 +9,7 @@ import { MatIconModule } from '@angular/material/icon'; selector: 'app-input-with-button', imports: [MatTooltipModule, FormsModule, MatButtonModule, MatIconModule], templateUrl: './input-with-button.component.html', - styleUrls: ['./input-with-button.component.scss', '../../widget.scss'] + styleUrls: ['./input-with-button.component.scss', '../../widget.scss'], }) export class InputWithButtonComponent { @Input() description?: string; diff --git a/webapp/src/app/components/widgets/json-widget/json-widget.component.html b/webapp/src/app/components/widgets/json-widget/json-widget.component.html index 37c99e53..0f73549f 100644 --- a/webapp/src/app/components/widgets/json-widget/json-widget.component.html +++ b/webapp/src/app/components/widgets/json-widget/json-widget.component.html @@ -1,10 +1,16 @@ -
    +
    @if (!noPropName) { } -
    diff --git a/webapp/src/app/components/widgets/json-widget/json-widget.component.ts b/webapp/src/app/components/widgets/json-widget/json-widget.component.ts index f0299ad4..e2d875b5 100644 --- a/webapp/src/app/components/widgets/json-widget/json-widget.component.ts +++ b/webapp/src/app/components/widgets/json-widget/json-widget.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { JsonEditorComponent } from '../../json-editor/json-editor.component'; import { AbstractWidget } from '../abstract-widget'; @@ -7,33 +7,30 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-json-widget', templateUrl: './json-widget.component.html', styleUrls: ['./json-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class JsonWidgetComponent extends AbstractWidget { - + private dialog = inject(MatDialog); + @Input() noPropName = false; private timer = -1; json = JSON; - - constructor(private dialog: MatDialog) { - super(); - } - + openJsonEditor() { const ref = this.dialog.open(JsonEditorComponent, { data: { val: this.settings[this.key], - key: this.key - } + key: this.key, + }, }); - + ref.afterClosed().subscribe((res: any) => { if (res) { this.setCustomSetting(this.key, JSON.stringify(res)); } }); } - + setCustomSetting(key: string, value: any) { if (this.timer >= 0) { clearTimeout(this.timer); diff --git a/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.html b/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.html index 71725991..77df527d 100644 --- a/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.html +++ b/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.html @@ -1,12 +1,18 @@ -
    +
    - +
    @@ -18,24 +24,28 @@
    - +
    }
    - + + />
    } diff --git a/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.ts b/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.ts index f9c68290..bdcdac6b 100644 --- a/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.ts +++ b/webapp/src/app/components/widgets/langlabel-widget/langlabel-widget.component.ts @@ -6,23 +6,24 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-langlabel-widget', templateUrl: './langlabel-widget.component.html', styleUrls: ['./langlabel-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class LangLabelWidgetComponent extends AbstractWidget { - @ViewChildren(CdkTextareaAutosize) inputTextareas!: QueryList; + @ViewChildren(CdkTextareaAutosize) + inputTextareas!: QueryList; languages: string[] = [ 'en_US', 'de_DE', 'ja_JP', 'ko_KR', 'zh_CN', - 'zh_TW' + 'zh_TW', ]; - + get hasValue(): boolean { return this.settings[this.key] !== undefined; } - + get defaultValue() { const value: any = {}; for (const language of this.languages) { @@ -30,8 +31,10 @@ export class LangLabelWidgetComponent extends AbstractWidget { } return value; } - + resizeTextareas() { - this.inputTextareas.forEach(autosize => autosize.resizeToFitContent(true)); + this.inputTextareas.forEach((autosize) => + autosize.resizeToFitContent(true), + ); } } diff --git a/webapp/src/app/components/widgets/level-widget/level-widget.component.html b/webapp/src/app/components/widgets/level-widget/level-widget.component.html index 311ea819..ff3a3c13 100644 --- a/webapp/src/app/components/widgets/level-widget/level-widget.component.html +++ b/webapp/src/app/components/widgets/level-widget/level-widget.component.html @@ -1,13 +1,23 @@ @if (entity) {
    {{ displayName || key }}: - + - - + + + - +
    } diff --git a/webapp/src/app/components/widgets/level-widget/level-widget.component.ts b/webapp/src/app/components/widgets/level-widget/level-widget.component.ts index 0071f37f..db2e98a4 100644 --- a/webapp/src/app/components/widgets/level-widget/level-widget.component.ts +++ b/webapp/src/app/components/widgets/level-widget/level-widget.component.ts @@ -1,4 +1,11 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { + Component, + Input, + OnChanges, + OnDestroy, + OnInit, + inject, +} from '@angular/core'; import { Subscription } from 'rxjs'; import { CrossCodeMap } from '../../../models/cross-code-map'; import { MapLoaderService } from '../../../services/map-loader.service'; @@ -8,23 +15,29 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-level-widget', templateUrl: './level-widget.component.html', styleUrls: ['./level-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) -export class LevelWidgetComponent extends AbstractWidget implements OnInit, OnDestroy, OnChanges { - +export class LevelWidgetComponent + extends AbstractWidget + implements OnInit, OnDestroy, OnChanges +{ + private maploader = inject(MapLoaderService); + @Input() displayName = ''; map?: CrossCodeMap; private subscription: Subscription; - - constructor(private maploader: MapLoaderService) { + + constructor() { super(); - this.subscription = this.maploader.map.subscribe(map => this.map = map); + this.subscription = this.maploader.map.subscribe( + (map) => (this.map = map), + ); } - + ngOnDestroy(): void { this.subscription.unsubscribe(); } - + setLevel(level: number) { this.settings[this.key].level = Number(level); if (this.entity) { @@ -32,7 +45,7 @@ export class LevelWidgetComponent extends AbstractWidget implements OnInit, OnDe } this.updateType(level); } - + setOffset(offset: number) { this.settings[this.key].offset = Number(offset); if (this.entity) { @@ -40,5 +53,4 @@ export class LevelWidgetComponent extends AbstractWidget implements OnInit, OnDe } this.updateType(offset); } - } diff --git a/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.html b/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.html index 82a6b098..58e52d57 100644 --- a/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.html +++ b/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.html @@ -1,4 +1,13 @@ -
    - - +
    + +
    diff --git a/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.ts b/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.ts index 2b303b08..8087fe27 100644 --- a/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.ts +++ b/webapp/src/app/components/widgets/npc-states-widget/npc-states-widget.component.ts @@ -1,5 +1,5 @@ import { Overlay } from '@angular/cdk/overlay'; -import { Component, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { EventArray } from '../../../models/events'; import { OverlayRefControl } from '../../dialogs/overlay/overlay-ref-control'; @@ -29,18 +29,18 @@ export interface NPCState { selector: 'app-npcstates-widget', templateUrl: './npc-states-widget.component.html', styleUrls: ['./npc-states-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) -export class NPCStatesWidgetComponent extends AbstractWidget implements OnInit, OnChanges, OnDestroy { - +export class NPCStatesWidgetComponent + extends AbstractWidget + implements OnInit, OnChanges, OnDestroy +{ + private overlayService = inject(OverlayService); + private overlay = inject(Overlay); + npcStates: NPCState[] = []; private ref?: OverlayRefControl; - - constructor(private overlayService: OverlayService, - private overlay: Overlay) { - super(); - } - + override ngOnChanges(): void { super.ngOnChanges(); this.npcStates = this.settings[this.key]; @@ -49,36 +49,39 @@ export class NPCStatesWidgetComponent extends AbstractWidget implements OnInit, this.settings[this.key] = this.npcStates; } } - + ngOnDestroy() { if (this.ref) { this.ref.close(); } } - + open() { if (this.ref && this.ref.isOpen()) { return; } const obj = this.overlayService.open(NpcStatesComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left('23vw') .top('calc(64px + 6vh / 2)'), hasBackdrop: true, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.ref = obj.ref; - + obj.instance.states = JSON.parse(JSON.stringify(this.npcStates)); - - obj.instance.exit.subscribe((v: any) => { - obj.ref.close(); - this.settings[this.key] = v; - this.npcStates = v; - this.updateType(v); - }, () => obj.ref.close()); + + obj.instance.exit.subscribe( + (v: any) => { + obj.ref.close(); + this.settings[this.key] = v; + this.npcStates = v; + this.updateType(v); + }, + () => obj.ref.close(), + ); } - - } diff --git a/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.html b/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.html index bec4dc9c..9f39882b 100644 --- a/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.html +++ b/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.html @@ -3,88 +3,185 @@
    - - + +
    - - + @for (state of states; track state; let index = $index) { - + } - + @if (currentState) {
    -
    - - + +
    -
    -
    - +
    +
    - Position + Position
    @if (currentState.position!.active) {
    -
    - - - - - - - +
    + + + + + + +
    - -
    - - - - - - + +
    + + + + + +
    -

    +

    } - + -
    - Page name: - +
    + Page name: + +
    - + -
    - Event type: - @for (type of eventTypes; track type) {
    - + @if (isTradeEvent) { -
    - Trader: - +
    + Trader: + +
    } - + @for (prop of props | keyvalue; track prop) { -
    - {{ prop.key }}: - - @for (item of $any(prop.value); track item) { +
    + {{ prop.key }}: + + + @for ( + item of $any(prop.value); + track item + ) { {{ item }} @@ -116,20 +237,31 @@
    } - + @for (warning of warnings; track warning) {
    warning - {{ warning }} + {{ + warning + }}
    }
    -
    +
    -
    diff --git a/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.ts b/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.ts index 2aa777dd..551bac6b 100644 --- a/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.ts +++ b/webapp/src/app/components/widgets/npc-states-widget/npc-states/npc-states.component.ts @@ -1,6 +1,17 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; import settingsJson from '../../../../../assets/npc-settings.json'; -import { createEventArray, destructureEventArray, EventArrayType } from '../../../../models/events'; +import { + createEventArray, + destructureEventArray, + EventArrayType, +} from '../../../../models/events'; import { EventEditorComponent } from '../../event-widget/event-editor/editor/event-editor.component'; import { EventType } from '../../event-widget/event-registry/abstract-event'; import { NPCState } from '../npc-states-widget.component'; @@ -9,38 +20,38 @@ import { NPCState } from '../npc-states-widget.component'; selector: 'app-npc-states', templateUrl: './npc-states.component.html', styleUrls: ['./npc-states.component.scss', '../../widget.scss'], - standalone: false + standalone: false, }) export class NpcStatesComponent implements OnInit { - @ViewChild('eventEditor', {static: false}) eventEditor?: EventEditorComponent; - + @ViewChild('eventEditor', { static: false }) + eventEditor?: EventEditorComponent; + @Input() states: NPCState[] = []; @Output() exit: EventEmitter = new EventEmitter(); currentState?: NPCState; index = 0; - + props = settingsJson; eventTypes = Object.values(EventArrayType); warnings: string[] = []; private missingTradeEvent = false; - + eventType: EventArrayType = EventArrayType.Simple; trader?: string; - + // TODO: move to global events service clipboard = ''; - - constructor() { - } - + + constructor() {} + ngOnInit() { - this.states.forEach(state => { + this.states.forEach((state) => { if (!state.position) { state.position = { x: 0, y: 0, lvl: 0, - active: false + active: false, }; } else if (state.position.active !== false) { state.position.active = true; @@ -48,22 +59,28 @@ export class NpcStatesComponent implements OnInit { }); this.selectTab(this.index); } - + selectTab(index: number) { if (this.currentState) { if (!this.eventEditor) { throw new Error('event editor is not defined'); } - this.currentState.event = createEventArray(this.eventEditor.export(), this.eventType, this.trader); + this.currentState.event = createEventArray( + this.eventEditor.export(), + this.eventType, + this.trader, + ); this.eventEditor.show(); } this.currentState = this.states[index]; this.index = index; - if (this.currentState) { //Undefined if this.states.length is 0 - ({type: this.eventType, trader: this.trader} = destructureEventArray(this.currentState.event)); + if (this.currentState) { + //Undefined if this.states.length is 0 + ({ type: this.eventType, trader: this.trader } = + destructureEventArray(this.currentState.event)); } } - + newPage() { this.index++; this.states.splice(this.index, 0, { @@ -72,17 +89,17 @@ export class NpcStatesComponent implements OnInit { x: 0, y: 0, lvl: 0, - active: false + active: false, }, face: 'NORTH', action: [], hidden: false, condition: '', config: 'normal', - event: [] + event: [], }); } - + // TODO: make proper export for copy/paste and export copyPage() { if (!this.currentState) { @@ -91,81 +108,94 @@ export class NpcStatesComponent implements OnInit { if (!this.eventEditor) { throw new Error('event editor is not defined'); } - this.currentState.event = createEventArray(this.eventEditor.export(), this.eventType, this.trader); + this.currentState.event = createEventArray( + this.eventEditor.export(), + this.eventType, + this.trader, + ); this.clipboard = JSON.stringify(this.currentState); console.log(JSON.parse(this.clipboard)); } - + pastePage() { this.index++; this.states.splice(this.index, 0, JSON.parse(this.clipboard)); } - + removePage() { this.states.splice(this.index, 1); this.index = Math.max(0, this.index - 1); } - + export() { if (!this.eventEditor) { throw new Error('event editor is not defined'); } if (this.currentState) { - this.currentState.event = createEventArray(this.eventEditor.export(), this.eventType, this.trader); + this.currentState.event = createEventArray( + this.eventEditor.export(), + this.eventType, + this.trader, + ); } const out = this.states; - out.forEach(state => { + out.forEach((state) => { if (state.position && state.position.active) { state.position.active = undefined; } else { state.position = undefined; } }); - + return out; } - + save() { this.exit.emit(this.export()); } - + cancel() { this.exit.error('cancel'); } - + updateDisplayedWarnings() { this.warnings.length = 0; if (this.isTradeEvent) { if (!this.trader) { this.warnings.push('Missing trader name'); } - + if (this.missingTradeEvent) { - this.warnings.push('START_NPC_TRADE_MENU event is missing, trade popup will not appear'); + this.warnings.push( + 'START_NPC_TRADE_MENU event is missing, trade popup will not appear', + ); } } } - + updateTradeEventWarning(updatedEvents: EventType[]) { function hasEventOfTypeRecursive(object: any, type: string): boolean { if (typeof object !== 'object' || object === null) { return false; } for (const [key, value] of Object.entries(object)) { - if ((key === 'type' && value === type) || hasEventOfTypeRecursive(value, type)) { + if ( + (key === 'type' && value === type) || + hasEventOfTypeRecursive(value, type) + ) { return true; } } return false; } - + this.missingTradeEvent = updatedEvents.length > 0 && !hasEventOfTypeRecursive(updatedEvents, 'START_NPC_TRADE_MENU'); - + this.updateDisplayedWarnings(); } - + get isTradeEvent() { return this.eventType === EventArrayType.Trade; } diff --git a/webapp/src/app/components/widgets/number-widget/number-widget.component.html b/webapp/src/app/components/widgets/number-widget/number-widget.component.html index d6c7f3f3..dae2af92 100644 --- a/webapp/src/app/components/widgets/number-widget/number-widget.component.html +++ b/webapp/src/app/components/widgets/number-widget/number-widget.component.html @@ -1,6 +1,13 @@ -
    - - +
    + +
    diff --git a/webapp/src/app/components/widgets/number-widget/number-widget.component.ts b/webapp/src/app/components/widgets/number-widget/number-widget.component.ts index af355350..25152f82 100644 --- a/webapp/src/app/components/widgets/number-widget/number-widget.component.ts +++ b/webapp/src/app/components/widgets/number-widget/number-widget.component.ts @@ -5,19 +5,17 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-number-widget', templateUrl: './number-widget.component.html', styleUrls: ['./number-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class NumberWidgetComponent extends AbstractWidget implements OnInit { - override setSetting(key: string, value: string) { - const num = parseFloat(value); - + if (isNaN(num)) { console.warn(`Input ${value} is not a valid number.`); return; } - + super.setSetting(key, num); } } diff --git a/webapp/src/app/components/widgets/overlay-widget.ts b/webapp/src/app/components/widgets/overlay-widget.ts index f2553d13..8ffcea76 100644 --- a/webapp/src/app/components/widgets/overlay-widget.ts +++ b/webapp/src/app/components/widgets/overlay-widget.ts @@ -1,66 +1,75 @@ -import { AbstractWidget } from './abstract-widget'; -import { Directive, OnChanges, OnDestroy, OnInit } from '@angular/core'; -import { OverlayRefControl } from '../dialogs/overlay/overlay-ref-control'; -import { OverlayService } from '../dialogs/overlay/overlay.service'; import { Overlay } from '@angular/cdk/overlay'; +import { Directive, inject, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { Globals } from '../../services/globals'; import { CCEntity } from '../../services/phaser/entities/cc-entity'; +import { OverlayRefControl } from '../dialogs/overlay/overlay-ref-control'; +import { OverlayService } from '../dialogs/overlay/overlay.service'; +import { AbstractWidget } from './abstract-widget'; @Directive() -export abstract class OverlayWidget extends AbstractWidget implements OnInit, OnChanges, OnDestroy { +export abstract class OverlayWidget> + extends AbstractWidget + implements OnInit, OnChanges, OnDestroy +{ private ref?: OverlayRefControl; private static imgCache = new Map(); - - constructor( - protected overlayService: OverlayService, - protected overlay: Overlay, - ) { - super(); - } - + + protected overlayService = inject(OverlayService); + protected overlay = inject(Overlay); + ngOnDestroy() { this.ref?.close(); } - + async open() { if (this.ref?.isOpen()) { return; } this.ref = await this.openInternal(); } - - protected async generateImage(settings: T, entity: string | CCEntity, offsetY?: number, entityScale = 1) { - + + protected async generateImage( + settings: T, + entity: string | CCEntity, + offsetY?: number, + entityScale = 1, + ) { const imgKey = JSON.stringify(settings); let img = OverlayWidget.imgCache.get(imgKey); if (img) { return img; } - + if (typeof entity === 'string') { const entityClass = Globals.entityRegistry.getEntity(entity); - entity = new entityClass(Globals.scene, Globals.map, -999999, 0, entity); + entity = new entityClass( + Globals.scene, + Globals.map, + -999999, + 0, + entity, + ); } - + try { await entity.setSettings(settings); } catch (e) { await entity.setSettings({}); } img = (await entity.generateHtmlImage(true, offsetY, entityScale)).src; - + OverlayWidget.imgCache.set(imgKey, img); - + entity.destroy(); - + return img; } - + /** * should use overlayService.open */ abstract openInternal(): Promise; - + protected close() { this.onChange.emit(); this.ref?.close(); diff --git a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.html b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.html index a7284acd..da816ef9 100644 --- a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.html +++ b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.html @@ -1,25 +1,25 @@ -
    - +
    +
    -
    - +
    -
    diff --git a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts index 62935fdc..9b92586b 100644 --- a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts +++ b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts @@ -1,77 +1,81 @@ -import { ChangeDetectorRef, Component, OnChanges } from '@angular/core'; -import { OverlayWidget } from '../../overlay-widget'; -import { ImageSelectOverlayComponent } from '../../shared/image-select-overlay/image-select-overlay.component'; -import { HttpClientService } from '../../../../services/http-client.service'; -import { OverlayService } from '../../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; -import { PropListCard } from '../../shared/image-select-overlay/image-select-card/image-select-card.component'; -import { Helper } from '../../../../services/phaser/helper'; -import { CharacterSettings, Face } from '../../../../services/phaser/entities/registry/npc'; +import { ChangeDetectorRef, Component, OnChanges, inject } from '@angular/core'; import { Person } from '../../../../models/events'; -import { prepareSheet } from '../../../../services/phaser/sheet-parser'; -import { getNPCTemplates } from '../../../../services/phaser/entities/registry/npc-templates'; -import { ExpressionRendererEntity, ExpressionRendererSettings } from './expression-renderer-entity'; import { Globals } from '../../../../services/globals'; +import { HttpClientService } from '../../../../services/http-client.service'; import { JsonLoaderService } from '../../../../services/json-loader.service'; +import { + CharacterSettings, + Face, +} from '../../../../services/phaser/entities/registry/npc'; +import { getNPCTemplates } from '../../../../services/phaser/entities/registry/npc-templates'; +import { Helper } from '../../../../services/phaser/helper'; +import { prepareSheet } from '../../../../services/phaser/sheet-parser'; +import { OverlayWidget } from '../../overlay-widget'; +import { PropListCard } from '../../shared/image-select-overlay/image-select-card/image-select-card.component'; +import { ImageSelectOverlayComponent } from '../../shared/image-select-overlay/image-select-overlay.component'; +import { + ExpressionRendererEntity, + ExpressionRendererSettings, +} from './expression-renderer-entity'; @Component({ selector: 'app-custom-expression-widget', templateUrl: './custom-expression-widget.component.html', - styleUrls: ['./custom-expression-widget.component.scss', '../../widget.scss'], - standalone: false + styleUrls: [ + './custom-expression-widget.component.scss', + '../../widget.scss', + ], + standalone: false, }) -export class CustomExpressionWidgetComponent extends OverlayWidget implements OnChanges { - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); +export class CustomExpressionWidgetComponent + extends OverlayWidget + implements OnChanges +{ + private http = inject(HttpClientService); + private changeDetectorRef = inject(ChangeDetectorRef); + private jsonLoader = inject(JsonLoaderService); + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); preview = ''; - - constructor( - private http: HttpClientService, - private changeDetectorRef: ChangeDetectorRef, - private jsonLoader: JsonLoaderService, - overlayService: OverlayService, - overlay: Overlay, - ) { - super(overlayService, overlay); - } - + override async ngOnChanges() { super.ngOnChanges(); await this.updatePreviewImg(); } - + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.title = 'Expression'; this.comp.splitBaseName = CustomExpressionWidgetComponent.name; this.comp.showRightProps = false; - + this.comp.exit.subscribe(() => { this.close(); - this.updatePreviewImg(); + void this.updatePreviewImg(); }); - - this.comp.leftGroup.click = prop => this.setExpression(prop); - + + this.comp.leftGroup.click = (prop) => this.setExpression(prop); + this.setExpression(this.settings.expression); await this.updateFaces(); - + return obj.ref; } - + async updatePreviewImg() { const face = await this.getFace(); this.preview = await this.makeImage(face, this.settings.expression); this.changeDetectorRef.markForCheck(); } - + private setExpression(prop?: string) { this.comp.leftGroup.selected = prop; if (this.settings.expression === prop) { @@ -79,50 +83,68 @@ export class CustomExpressionWidgetComponent extends OverlayWidget imple } this.setSetting(this.key, prop); } - + private async updateFaces() { const expressions: PropListCard[] = []; this.comp.leftGroup.props = []; this.comp.loading = true; - + const face = await this.getFace(); - + const faceNames = Object.keys(face.expressions ?? {}); - - await Promise.all(faceNames.map(async name => { - expressions.push({ - name: name, - displayName: name.toLowerCase(), - imgSrc: await this.makeImage(face, name) - }); - })); + + await Promise.all( + faceNames.map(async (name) => { + expressions.push({ + name: name, + displayName: name.toLowerCase(), + imgSrc: await this.makeImage(face, name), + }); + }), + ); expressions.sort((a, b) => a.name.localeCompare(b.name)); - + this.comp.leftGroup.props = expressions; this.comp.loading = false; } - + private async getFace() { const person = (this.settings.person ?? '').replaceAll('.', '/'); - let sheet = (await Helper.getJsonPromise('data/characters/' + person) as CharacterSettings | undefined) ?? {}; + let sheet = + ((await Helper.getJsonPromise('data/characters/' + person)) as + | CharacterSettings + | undefined) ?? {}; sheet.jsonTEMPLATES = getNPCTemplates(); sheet = prepareSheet(sheet); - + let face: Face = sheet.face ?? {}; - + if (typeof sheet.face === 'object' && sheet.face?.ABSTRACT) { - const abstractFaces = await this.jsonLoader.loadJsonMerged>('abstract-faces.json'); + const abstractFaces = await this.jsonLoader.loadJsonMerged< + Record + >('abstract-faces.json'); face = abstractFaces[sheet.face.ABSTRACT as string]; } - + return face; } - + private async makeImage(face: Face, name: string): Promise { - const entity = new ExpressionRendererEntity(Globals.scene, Globals.map, -999999, 0, 'Custom'); - return await this.generateImage({ - face: face, - expression: name - }, entity, 32, 1.7); + const entity = new ExpressionRendererEntity( + Globals.scene, + Globals.map, + -999999, + 0, + 'Custom', + ); + return await this.generateImage( + { + face: face, + expression: name, + }, + entity, + 32, + 1.7, + ); } } diff --git a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/expression-renderer-entity.ts b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/expression-renderer-entity.ts index 5bf2ee7d..f6932a02 100644 --- a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/expression-renderer-entity.ts +++ b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/expression-renderer-entity.ts @@ -9,8 +9,9 @@ export interface ExpressionRendererSettings { } export class ExpressionRendererEntity extends DefaultEntity { - - protected override async setupType(settings: ExpressionRendererSettings): Promise { + protected override async setupType( + settings: ExpressionRendererSettings, + ): Promise { if (!settings?.face) { return; } @@ -42,14 +43,15 @@ export class ExpressionRendererEntity extends DefaultEntity { y: part.srcY, w: part.width, h: part.height, - offsetX: part.destX + subX + part.width / 2 - (face.centerX ?? 0), + offsetX: + part.destX + subX + part.width / 2 - (face.centerX ?? 0), offsetY: part.destY + subY + part.height, }; - + if (sheet.offsetY! > renderHeight) { renderHeight = sheet.offsetY!; } - + sheets.push(sheet); subX += part.subX ?? 0; subY += part.subY ?? 0; @@ -59,10 +61,10 @@ export class ExpressionRendererEntity extends DefaultEntity { sheet.offsetY! -= renderHeight; await Helper.loadTexture(sheet.gfx, this.scene); } - + this.entitySettings = { sheets: { - fix: sheets + fix: sheets, }, baseSize: { x: 0, diff --git a/webapp/src/app/components/widgets/person-expression-widget/person-expression-widget.component.ts b/webapp/src/app/components/widgets/person-expression-widget/person-expression-widget.component.ts index 2980e9d2..0f1b42b2 100644 --- a/webapp/src/app/components/widgets/person-expression-widget/person-expression-widget.component.ts +++ b/webapp/src/app/components/widgets/person-expression-widget/person-expression-widget.component.ts @@ -5,20 +5,22 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-person-expression-widget', templateUrl: './person-expression-widget.component.html', styleUrls: ['./person-expression-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) -export class PersonExpressionWidgetComponent extends AbstractWidget implements OnChanges { - +export class PersonExpressionWidgetComponent + extends AbstractWidget + implements OnChanges +{ constructor() { super(); } - + override ngOnChanges(): void { super.ngOnChanges(); if (!this.settings[this.key]) { this.settings[this.key] = { person: '', - expression: '' + expression: '', }; } } diff --git a/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.html b/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.html index a2d34803..7c233ec7 100644 --- a/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.html +++ b/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.html @@ -1,6 +1,13 @@ -
    - - +
    + +
    diff --git a/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.ts b/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.ts index f1b8fb49..204bc4fa 100644 --- a/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.ts +++ b/webapp/src/app/components/widgets/prop-type-widget/prop-type-widget.component.ts @@ -1,85 +1,85 @@ -import { Component } from '@angular/core'; -import { OverlayWidget } from '../overlay-widget'; -import { ImageSelectOverlayComponent, PropListGroup } from '../shared/image-select-overlay/image-select-overlay.component'; -import { OverlayService } from '../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; -import { HttpClientService } from '../../../services/http-client.service'; +import { Component, inject } from '@angular/core'; import { lastValueFrom } from 'rxjs'; -import { Prop, PropAttributes } from '../../../services/phaser/entities/registry/prop'; +import { HttpClientService } from '../../../services/http-client.service'; +import { PropAttributes } from '../../../services/phaser/entities/registry/prop'; import { Helper } from '../../../services/phaser/helper'; -import { Anims, prepareSheet, PropDef, PropSheet } from '../../../services/phaser/sheet-parser'; +import { + Anims, + prepareSheet, + PropDef, + PropSheet, +} from '../../../services/phaser/sheet-parser'; +import { OverlayWidget } from '../overlay-widget'; import { PropListCard } from '../shared/image-select-overlay/image-select-card/image-select-card.component'; +import { + ImageSelectOverlayComponent, + PropListGroup, +} from '../shared/image-select-overlay/image-select-overlay.component'; @Component({ selector: 'app-prop-type-widget', templateUrl: './prop-type-widget.component.html', styleUrls: ['./prop-type-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class PropTypeWidgetComponent extends OverlayWidget { - + private http = inject(HttpClientService); + private sheetKey = ['propType', 'sheet']; private nameKey = ['propType', 'name']; private animKey: keyof PropAttributes = 'propAnim'; - + private props: { name: string; propAnim: string; imgSrc?: string; }[] = []; - + private rightGroup: PropListGroup = { - props: [] + props: [], }; - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); - - constructor( - private http: HttpClientService, - overlayService: OverlayService, - overlay: Overlay - ) { - super(overlayService, overlay); - } - + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.splitBaseName = PropTypeWidgetComponent.name; this.comp.sheet = this.settings.propType?.sheet; this.comp.leftGroup.selected = this.settings.propType?.name; this.rightGroup.selected = this.settings.propAnim; - + this.comp.title = 'Prop Type'; - + this.comp.rightGroups = [this.rightGroup]; - + this.comp.exit.subscribe(() => { this.close(); }); - - this.comp.sheetChange.subscribe(sheet => this.setSheet(sheet)); - this.comp.leftGroup.click = prop => this.setPropType(prop); - this.rightGroup.click = prop => this.setPropAnim(prop); - + + this.comp.sheetChange.subscribe((sheet) => this.setSheet(sheet)); + this.comp.leftGroup.click = (prop) => this.setPropType(prop); + this.rightGroup.click = (prop) => this.setPropAnim(prop); + this.comp.sheets = await lastValueFrom(this.http.getProps()); - + this.comp.manualKey = this.key; this.comp.manualValue = this.settings.propType?.name; - this.comp.manualValueChange.subscribe(v => this.setPropType(v)); - + this.comp.manualValueChange.subscribe((v) => this.setPropType(v)); + await this.updateProps(); this.updatePropAnims(); - + return obj.ref; } - + private async setSheet(sheet: string) { this.comp.sheet = sheet; if (this.settings.propType?.sheet === sheet) { @@ -89,7 +89,7 @@ export class PropTypeWidgetComponent extends OverlayWidget { await this.updateProps(); this.setPropType(''); } - + private setPropType(prop: string) { this.comp.leftGroup.selected = prop; if (this.settings.propType?.name === prop) { @@ -99,88 +99,99 @@ export class PropTypeWidgetComponent extends OverlayWidget { this.updatePropAnims(); this.comp.manualValue = this.settings.propType?.name; } - + private setPropAnim(propAnim: string) { this.rightGroup.selected = propAnim; this.setSetting(this.animKey, propAnim); } - + private async updateProps() { this.props = []; const props: typeof this.props = []; - + this.comp.leftGroup.props = []; const leftProps: PropListCard[] = []; - + this.rightGroup.props = []; const sheetPath = this.settings.propType?.sheet ?? ''; if (!this.comp.sheets.includes(sheetPath)) { return; } this.comp.loading = true; - - let sheet = await Helper.getJsonPromise('data/props/' + sheetPath) as PropSheet; + + let sheet = (await Helper.getJsonPromise( + 'data/props/' + sheetPath, + )) as PropSheet; sheet = prepareSheet(sheet); - - const propDefs = sheet.props.filter(v => v.name) as (PropDef & { name: string })[]; + + const propDefs = sheet.props.filter((v) => v.name) as (PropDef & { + name: string; + })[]; this.comp.showRightProps = false; - - await Promise.all(propDefs.map(async def => { - const names = this.getSubNames(def.anims?.SUB); - let firstImg = ''; - - if (names.length === 0) { - names.push('default'); - } - if (names.length >= 2) { - this.comp.showRightProps = true; - } - let searchName = def.name; - for (const name of names) { - const imgSrc = await this.generateImage({ - propType: { - sheet: sheetPath, - name: def.name - }, - propAnim: name - }, 'Prop'); - props.push({ + + await Promise.all( + propDefs.map(async (def) => { + const names = this.getSubNames(def.anims?.SUB); + let firstImg = ''; + + if (names.length === 0) { + names.push('default'); + } + if (names.length >= 2) { + this.comp.showRightProps = true; + } + let searchName = def.name; + for (const name of names) { + const imgSrc = await this.generateImage( + { + propType: { + sheet: sheetPath, + name: def.name, + }, + propAnim: name, + }, + 'Prop', + ); + props.push({ + name: def.name, + propAnim: name, + imgSrc: imgSrc, + }); + searchName += name; + firstImg = firstImg || imgSrc; + } + leftProps.push({ name: def.name, - propAnim: name, - imgSrc: imgSrc + searchName: searchName, + imgSrc: firstImg, + count: names.length, }); - searchName += name; - firstImg = firstImg || imgSrc; - } - leftProps.push({ - name: def.name, - searchName: searchName, - imgSrc: firstImg, - count: names.length - }); - })); - + }), + ); + props.sort((a, b) => a.name.localeCompare(b.name)); leftProps.sort((a, b) => a.name.localeCompare(b.name)); - + this.props = props; this.comp.leftGroup.props = leftProps; this.comp.loading = false; } - + private updatePropAnims() { const name = this.settings.propType?.name; - this.rightGroup.props = this.props.filter(v => v.name === name).map(v => ({ - name: v.propAnim, - imgSrc: v.imgSrc - })); + this.rightGroup.props = this.props + .filter((v) => v.name === name) + .map((v) => ({ + name: v.propAnim, + imgSrc: v.imgSrc, + })); } - + private getSubNames(sub: Anims | Anims['SUB']): string[] { if (!sub) { return []; } - + if (Array.isArray(sub)) { const uniqueNames = new Set(); for (const v of sub) { @@ -193,9 +204,7 @@ export class PropTypeWidgetComponent extends OverlayWidget { } return Array.from(uniqueNames); } - + return []; } - - } diff --git a/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.html b/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.html index 479ae257..7ab46a16 100644 --- a/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.html +++ b/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.html @@ -1,6 +1,13 @@ -
    - - +
    + +
    diff --git a/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.ts b/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.ts index 95b1122a..34e81e75 100644 --- a/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.ts +++ b/webapp/src/app/components/widgets/scalable-prop-config-widget/scalable-prop-config-widget.component.ts @@ -1,69 +1,80 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ImageSelectOverlayComponent, PropListGroup } from '../shared/image-select-overlay/image-select-overlay.component'; -import { HttpClientService } from '../../../services/http-client.service'; -import { OverlayService } from '../../dialogs/overlay/overlay.service'; -import { Overlay } from '@angular/cdk/overlay'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { lastValueFrom } from 'rxjs'; -import { Helper } from '../../../services/phaser/helper'; -import { prepareSheet, ScalablePropSheet } from '../../../services/phaser/sheet-parser'; import { Globals } from '../../../services/globals'; +import { HttpClientService } from '../../../services/http-client.service'; +import { + EntryGfxEnds, + GfxEndsDir, + ScalableProp, + ScalablePropAttributes, + ScalablePropConfig, + ScalablePropDef, +} from '../../../services/phaser/entities/registry/scalable-prop'; +import { Helper } from '../../../services/phaser/helper'; +import { + prepareSheet, + ScalablePropSheet, +} from '../../../services/phaser/sheet-parser'; import { OverlayWidget } from '../overlay-widget'; -import { EntryGfxEnds, GfxEndsDir, ScalableProp, ScalablePropAttributes, ScalablePropConfig, ScalablePropDef } from '../../../services/phaser/entities/registry/scalable-prop'; import { PropListCard } from '../shared/image-select-overlay/image-select-card/image-select-card.component'; +import { + ImageSelectOverlayComponent, + PropListGroup, +} from '../shared/image-select-overlay/image-select-overlay.component'; import Point = Electron.Point; @Component({ selector: 'app-scalable-prop-config-widget', templateUrl: './scalable-prop-config-widget.component.html', - styleUrls: ['./scalable-prop-config-widget.component.scss', '../widget.scss'], - standalone: false + styleUrls: [ + './scalable-prop-config-widget.component.scss', + '../widget.scss', + ], + standalone: false, }) -export class ScalablePropConfigWidgetComponent extends OverlayWidget implements OnInit, OnDestroy { +export class ScalablePropConfigWidgetComponent + extends OverlayWidget + implements OnInit, OnDestroy +{ + private http = inject(HttpClientService); + private sheetKey = ['propConfig', 'sheet']; private nameKey = ['propConfig', 'name']; - - private comp: ImageSelectOverlayComponent = new ImageSelectOverlayComponent(); + + private comp: ImageSelectOverlayComponent = + new ImageSelectOverlayComponent(); private sheet?: ScalablePropSheet; - - constructor( - private http: HttpClientService, - overlayService: OverlayService, - overlay: Overlay - ) { - super(overlayService, overlay); - } - + override async openInternal() { const obj = this.overlayService.open(ImageSelectOverlayComponent, { positionStrategy: this.overlay.position().global(), hasBackdrop: false, - disablePhaserInput: true + disablePhaserInput: true, }); - + this.comp = obj.instance; - + this.comp.splitBaseName = ScalablePropConfigWidgetComponent.name; this.comp.sheet = this.settings.propConfig?.sheet; this.comp.title = 'Prop Config'; this.comp.leftGroup.selected = this.settings.propConfig?.name; this.comp.showPreview = true; - - + this.comp.exit.subscribe(() => { this.close(); }); - - this.comp.sheetChange.subscribe(sheet => this.setSheet(sheet)); - this.comp.leftGroup.click = prop => this.setPropName(prop); - + + this.comp.sheetChange.subscribe((sheet) => this.setSheet(sheet)); + this.comp.leftGroup.click = (prop) => this.setPropName(prop); + this.comp.sheets = await lastValueFrom(this.http.getScalableProps()); await this.updateProps(); await this.updateRightGroups(); await this.updatePreview(); - + return obj.ref; } - + private async setSheet(sheet: string) { this.comp.sheet = sheet; if (this.settings.propConfig?.sheet === sheet) { @@ -73,7 +84,7 @@ export class ScalablePropConfigWidgetComponent extends OverlayWidget { - return { - name: prop.name, - imgSrc: await this.generateImg(prop, prop.name) - }; - })); + + this.comp.leftGroup.props = await Promise.all( + entries.map(async (prop) => { + return { + name: prop.name, + imgSrc: await this.generateImg(prop, prop.name), + }; + }), + ); this.comp.leftGroup.props.sort((a, b) => a.name.localeCompare(b.name)); this.comp.loading = false; } - + private async updateRightGroups() { this.comp.rightGroups = []; const name = this.settings.propConfig?.name ?? ''; @@ -127,36 +141,38 @@ export class ScalablePropConfigWidgetComponent extends OverlayWidget { + click: async (val) => { this.setSetting(['propConfig', 'ends', key], val); group.selected = val; await this.updatePreview(); - } + }, }; this.comp.rightGroups.push(group); } } - + private async updatePreview() { this.comp.preview = undefined; const propConfig = this.settings.propConfig; @@ -167,9 +183,9 @@ export class ScalablePropConfigWidgetComponent extends OverlayWidget { - + + private async generateImg( + def: ScalablePropDef, + name?: string, + ends?: ScalablePropConfig['ends'], + renderAll = false, + size?: Point, + ): Promise { const entityClass = Globals.entityRegistry.getEntity('ScalableProp'); - const entity = new entityClass(Globals.scene, Globals.map, -999999, 0, 'ScalableProp') as unknown as ScalableProp; + const entity = new entityClass( + Globals.scene, + Globals.map, + -999999, + 0, + 'ScalableProp', + ) as unknown as ScalableProp; const settings: Partial = { propConfig: { sheet: this.settings.propConfig?.sheet, name: name, - ends: ends + ends: ends, }, size: { x: size?.x ?? def.baseSize?.x ?? 16, y: size?.y ?? def.baseSize?.y ?? 16, - } + }, }; entity.onlyEnds = !renderAll && !!ends; let offsetY = 0; diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.html b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.html index b8077478..ca84eaab 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.html +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.html @@ -8,7 +8,7 @@ }
    @if (card.imgSrc) { - + }
    diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.ts b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.ts index 06f5cdd2..fa5d51b1 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.ts +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-card/image-select-card.component.ts @@ -14,14 +14,14 @@ export interface PropListCard { selector: 'app-image-select-card', imports: [HighlightDirective], templateUrl: './image-select-card.component.html', - styleUrls: ['./image-select-card.component.scss'] + styleUrls: ['./image-select-card.component.scss'], }) export class ImageSelectCardComponent { @Input() card: PropListCard = { - name: '?' + name: '?', }; @Input() selected = false; @Input() filter = ''; - + @Output() onClick = new EventEmitter(); } diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.html b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.html index 169fbb13..23139bb5 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.html +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.html @@ -1,7 +1,6 @@
    @if (showFilter) { @@ -12,35 +11,36 @@ type="text" [(ngModel)]="filter" (ngModelChange)="filterChange.emit($event)" + /> + - } @if (title) { -

    {{ title }}

    -} + } + @if (title) { +

    {{ title }}

    + }
    - @if (items|listFilter:filter:filterItems; as filteredItems) { - + @if (items | listFilter: filter : filterItems; as filteredItems) { @for (prop of filteredItems; track prop) { - } @if (filteredItems.length === 0 && items.length > 0) { -
    - No items matching the filter -
    } - + @if (filteredItems.length === 0 && items.length > 0) { +
    No items matching the filter
    + } } -
    diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.ts b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.ts index 4a2fe86d..26cab794 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.ts +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/image-select-list.component.ts @@ -1,6 +1,9 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ImageSelectCardComponent, PropListCard } from '../image-select-card/image-select-card.component'; +import { + ImageSelectCardComponent, + PropListCard, +} from '../image-select-card/image-select-card.component'; import { MatInputModule } from '@angular/material/input'; import { MatIconModule } from '@angular/material/icon'; import { FormsModule } from '@angular/forms'; @@ -10,9 +13,17 @@ import { AutofocusDirective } from '../../../../../directives/autofocus.directiv @Component({ selector: 'app-image-select-list', - imports: [ImageSelectCardComponent, MatInputModule, MatIconModule, FormsModule, MatButtonModule, ListFilterPipe, AutofocusDirective], + imports: [ + ImageSelectCardComponent, + MatInputModule, + MatIconModule, + FormsModule, + MatButtonModule, + ListFilterPipe, + AutofocusDirective, + ], templateUrl: './image-select-list.component.html', - styleUrls: ['./image-select-list.component.scss'] + styleUrls: ['./image-select-list.component.scss'], }) export class ImageSelectListComponent { @Input() title?: string; @@ -20,9 +31,9 @@ export class ImageSelectListComponent { @Input() items: PropListCard[] = []; @Input() showFilter = false; @Input() filterItems = true; - + @Input() filter = ''; @Output() filterChange = new EventEmitter(); - + @Output() selectedChange = new EventEmitter(); } diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/list-filter.pipe.ts b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/list-filter.pipe.ts index aab7c41f..2e9ec6c1 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/list-filter.pipe.ts +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-list/list-filter.pipe.ts @@ -3,15 +3,21 @@ import { PropListCard } from '../image-select-card/image-select-card.component'; @Pipe({ name: 'listFilter', - standalone: true + standalone: true, }) export class ListFilterPipe implements PipeTransform { - - transform(items: PropListCard[], filter?: string, enable = true): PropListCard[] { + transform( + items: PropListCard[], + filter?: string, + enable = true, + ): PropListCard[] { if (!filter || !enable) { return items; } - return items.filter(v => (v.searchName ?? v.name).toLowerCase().includes(filter.toLowerCase().trim())); + return items.filter((v) => + (v.searchName ?? v.name) + .toLowerCase() + .includes(filter.toLowerCase().trim()), + ); } - } diff --git a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-overlay.component.html b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-overlay.component.html index ba911806..502a91fa 100644 --- a/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-overlay.component.html +++ b/webapp/src/app/components/widgets/shared/image-select-overlay/image-select-overlay.component.html @@ -1,6 +1,6 @@
    @if (sheets.length > 0) { -
    - } @if (manualKey) {
    @@ -47,20 +49,22 @@ + (ngModelChange)=" + manualValueChange.emit($event) + " + />
    - } @if (showGlobalCheckbox) { - - use GLOBAL - - } + } + @if (showGlobalCheckbox) { + + use GLOBAL + + }
    @if (loading) { @@ -73,19 +77,19 @@
    @if (showPreview) { -
    - }
    @for (group of rightGroups; track group) { (); - + @Input() sheets: string[] = []; @Input() sheet?: string; @Output() sheetChange = new EventEmitter(); - - @Input() leftGroup: PropListGroup = {props: []}; - + + @Input() leftGroup: PropListGroup = { props: [] }; + @Input() showRightProps = true; @Input() rightGroups: PropListGroup[] = []; - + @Input() showPreview = false; @Input() preview?: string; - + @Input() global = false; @Output() globalChange = new EventEmitter(); - + @Output() exit = new EventEmitter(); - + filter = ''; - + ngOnChanges(changes: SimpleChanges) { const key: keyof ImageSelectOverlayComponent = 'splitBaseName'; if (!changes[key].currentValue) { return; } try { - this.splitBase = parseFloat(localStorage.getItem(this.splitBaseName)!) ?? this.splitBase; + this.splitBase = + parseFloat(localStorage.getItem(this.splitBaseName)!) ?? + this.splitBase; } catch (e) { console.error(e); } } - + ngOnDestroy() { localStorage.setItem(this.splitBaseName, this.splitBase.toString()); } - + close() { this.exit.emit(); } - - } diff --git a/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.html b/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.html index e51e48ff..0ffb6dc7 100644 --- a/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.html +++ b/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.html @@ -3,23 +3,25 @@ [matAutocomplete]="auto" [ngModel]="text" (ngModelChange)="textChange.emit($event)" -> +/> @for (option of suggestedOptions; track option) { - + } @if (showWarning) { warning + >warning } diff --git a/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.ts b/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.ts index fb381a43..572c5b8e 100644 --- a/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.ts +++ b/webapp/src/app/components/widgets/string-widget/autocompleted-textbox/autocompleted-textbox.component.ts @@ -1,13 +1,23 @@ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnChanges, + Output, + SimpleChanges, + inject, +} from '@angular/core'; import { SearchFilterService } from '../../../../services/search-filter.service'; @Component({ selector: 'app-autocompleted-textbox', templateUrl: './autocompleted-textbox.component.html', styleUrls: ['./autocompleted-textbox.component.scss', '../../widget.scss'], - standalone: false + standalone: false, }) export class AutocompletedTextboxComponent implements OnChanges { + private searchFilterService = inject(SearchFilterService); + @Input() availableOptions!: string[]; @Input() text = ''; @Output() textChange = new EventEmitter(); @@ -15,20 +25,18 @@ export class AutocompletedTextboxComponent implements OnChanges { suggestedOptions = new Set(); disableTooltip = false; showWarning = false; - - constructor( - private searchFilterService: SearchFilterService, - ) { - } - + ngOnChanges(changes: SimpleChanges) { this.updateSuggestedOptions(); } - + updateSuggestedOptions() { const inputText = this.text; - const searchResults = this.searchFilterService.filterOptions(this.availableOptions, inputText); - + const searchResults = this.searchFilterService.filterOptions( + this.availableOptions, + inputText, + ); + this.suggestedOptions.clear(); if (searchResults.includes(inputText)) { this.showWarning = false; @@ -40,11 +48,17 @@ export class AutocompletedTextboxComponent implements OnChanges { this.suggestedOptions.add(searchResult); } } - searchResults.forEach(searchResult => this.suggestedOptions.add(searchResult)); - this.availableOptions.forEach(option => this.suggestedOptions.add(option)); + searchResults.forEach((searchResult) => + this.suggestedOptions.add(searchResult), + ); + this.availableOptions.forEach((option) => + this.suggestedOptions.add(option), + ); } else { this.showWarning = true; - searchResults.forEach(searchResult => this.suggestedOptions.add(searchResult)); + searchResults.forEach((searchResult) => + this.suggestedOptions.add(searchResult), + ); } } } diff --git a/webapp/src/app/components/widgets/string-widget/string-widget.component.html b/webapp/src/app/components/widgets/string-widget/string-widget.component.html index 37e8cb04..e23a805a 100644 --- a/webapp/src/app/components/widgets/string-widget/string-widget.component.html +++ b/webapp/src/app/components/widgets/string-widget/string-widget.component.html @@ -1,5 +1,9 @@ -
    +
    @if (attribute?.options) {
    @@ -11,22 +15,18 @@ >
    } @else { - - @if (!attribute?.large) { - - } @else { - - - + @if (!attribute?.large) { + + } @else { + + } } - -} - -
    diff --git a/webapp/src/app/components/widgets/string-widget/string-widget.component.ts b/webapp/src/app/components/widgets/string-widget/string-widget.component.ts index f83bdef2..7ae5e164 100644 --- a/webapp/src/app/components/widgets/string-widget/string-widget.component.ts +++ b/webapp/src/app/components/widgets/string-widget/string-widget.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { SearchFilterService } from '../../../services/search-filter.service'; import { AbstractWidget } from '../abstract-widget'; @@ -6,19 +6,14 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-string-widget', templateUrl: './string-widget.component.html', styleUrls: ['./string-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class StringWidgetComponent extends AbstractWidget implements OnInit { - + private searchFilterService = inject(SearchFilterService); + keys: string[] = []; disableTooltip = false; - - constructor( - private searchFilterService: SearchFilterService, - ) { - super(); - } - + override ngOnInit() { super.ngOnInit(); const attr = this.attribute; diff --git a/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.html b/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.html index 06f07dc3..ab5c8f92 100644 --- a/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.html +++ b/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.html @@ -1,23 +1,31 @@ -
    +
    - + - - + + - +
    diff --git a/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.ts b/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.ts index 9556304a..a95835d5 100644 --- a/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.ts +++ b/webapp/src/app/components/widgets/vec2-widget/vec2-widget.component.ts @@ -7,20 +7,19 @@ import { AbstractWidget } from '../abstract-widget'; selector: 'app-vec2-widget', templateUrl: './vec2-widget.component.html', styleUrls: ['./vec2-widget.component.scss', '../widget.scss'], - standalone: false + standalone: false, }) export class Vec2WidgetComponent extends AbstractWidget implements OnChanges { - @Input() def?: ScaleSettings; @Input() displayName = ''; - + scaleSettings: ScaleSettings; - + constructor() { super(); this.scaleSettings = this.updateScaleSettings(); } - + override ngOnChanges(): void { super.ngOnChanges(); this.scaleSettings = this.updateScaleSettings(); @@ -28,23 +27,23 @@ export class Vec2WidgetComponent extends AbstractWidget implements OnChanges { const minSize = this.scaleSettings.baseSize; const value = { x: minSize.x > 0 ? minSize.x : 0, - y: minSize.y > 0 ? minSize.y : 0 + y: minSize.y > 0 ? minSize.y : 0, }; this.settings[this.key] = value; this.updateType(value); } } - + toInt(value: string) { return parseInt(value, 10); } - + setVal(key: keyof Point, val: number) { const setting = this.settings[this.key]; setting[key] = val; this.updateType(val); } - + applySnap(key: keyof Point) { const setting = this.settings[this.key]; let val = setting[key] ?? 0; @@ -52,7 +51,7 @@ export class Vec2WidgetComponent extends AbstractWidget implements OnChanges { const value = Math.max(val, this.scaleSettings.baseSize[key]); this.setVal(key, value); } - + private updateScaleSettings(): ScaleSettings { if (this.def) { return this.def; @@ -60,9 +59,8 @@ export class Vec2WidgetComponent extends AbstractWidget implements OnChanges { return { scalableX: true, scalableY: true, - baseSize: {x: -9999, y: -9999}, - scalableStep: 1 + baseSize: { x: -9999, y: -9999 }, + scalableStep: 1, }; } - } diff --git a/webapp/src/app/components/widgets/widget-registry.service.ts b/webapp/src/app/components/widgets/widget-registry.service.ts index ea9060c4..ac13b8f4 100644 --- a/webapp/src/app/components/widgets/widget-registry.service.ts +++ b/webapp/src/app/components/widgets/widget-registry.service.ts @@ -16,12 +16,12 @@ import { CharacterWidgetComponent } from './character-widget/character-widget.co import { ArrayWidgetComponent } from './array-widget/array-widget.component'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class WidgetRegistryService { - private widgets: { [type: string]: any } = {}; + private widgets: Record = {}; private defaultWidget: any; - + constructor() { this.setDefaultWidget(JsonWidgetComponent); this.register('String', StringWidgetComponent); @@ -43,26 +43,26 @@ export class WidgetRegistryService { this.register('VarCondition', StringWidgetComponent); this.register('Array', ArrayWidgetComponent); } - + private setDefaultWidget(widget: any) { this.defaultWidget = widget; } - + private register(type: string, widget: any) { this.widgets[type] = widget; } - + public getDefaultWidget(): any { return this.defaultWidget; } - + public getWidget(type: string): any { if (this.hasWidget(type)) { return this.widgets[type]; } return this.defaultWidget; } - + private hasWidget(type: string): boolean { return Object.prototype.hasOwnProperty.call(this.widgets, type); } diff --git a/webapp/src/app/directives/autofocus.directive.ts b/webapp/src/app/directives/autofocus.directive.ts index 1cb3f268..0067d049 100644 --- a/webapp/src/app/directives/autofocus.directive.ts +++ b/webapp/src/app/directives/autofocus.directive.ts @@ -1,4 +1,12 @@ -import { AfterContentInit, ChangeDetectorRef, Directive, ElementRef, Input, OnChanges } from '@angular/core'; +import { + AfterContentInit, + ChangeDetectorRef, + Directive, + ElementRef, + Input, + OnChanges, + inject, +} from '@angular/core'; /** * requires variable "true" to be used, directive without property does nothing @@ -7,23 +15,18 @@ import { AfterContentInit, ChangeDetectorRef, Directive, ElementRef, Input, OnCh */ @Directive({ selector: '[appAutofocus]', - standalone: true + standalone: true, }) export class AutofocusDirective implements AfterContentInit { - + private el = inject(ElementRef); + private ref = inject(ChangeDetectorRef); + @Input() appAutofocus = true; - - constructor( - private el: ElementRef, - private ref: ChangeDetectorRef, - ) { - } - + ngAfterContentInit(): void { if (this.appAutofocus) { this.el.nativeElement.focus(); this.ref.markForCheck(); } } - } diff --git a/webapp/src/app/directives/colored-text.directive.ts b/webapp/src/app/directives/colored-text.directive.ts index 570a5db0..30ea1dcc 100644 --- a/webapp/src/app/directives/colored-text.directive.ts +++ b/webapp/src/app/directives/colored-text.directive.ts @@ -1,20 +1,19 @@ -import { Directive, ElementRef, Input, OnChanges } from '@angular/core'; +import { Directive, ElementRef, Input, OnChanges, inject } from '@angular/core'; import { ColorService } from '../services/color.service'; @Directive({ selector: '[appColoredText]', - standalone: true + standalone: true, }) export class ColoredTextDirective implements OnChanges { + private element = inject(ElementRef); + private colorService = inject(ColorService); @Input({ required: true }) appColoredText!: string; - constructor( - private element: ElementRef, - private colorService: ColorService, - ) { } - ngOnChanges(): void { - this.element.nativeElement.innerHTML = this.colorService.processText(this.appColoredText); + this.element.nativeElement.innerHTML = this.colorService.processText( + this.appColoredText, + ); } } diff --git a/webapp/src/app/directives/highlight.directive.ts b/webapp/src/app/directives/highlight.directive.ts index e89dd64c..c3fba5b6 100644 --- a/webapp/src/app/directives/highlight.directive.ts +++ b/webapp/src/app/directives/highlight.directive.ts @@ -1,26 +1,21 @@ -import { Directive, ElementRef, Input, OnChanges } from '@angular/core'; +import { Directive, ElementRef, Input, OnChanges, inject } from '@angular/core'; import { SearchFilterService } from '../services/search-filter.service'; @Directive({ selector: '[appHighlight]', - standalone: true + standalone: true, }) export class HighlightDirective implements OnChanges { - + private element = inject(ElementRef); + private searchFilterService = inject(SearchFilterService); + @Input() highlightText?: string; @Input() highlightMatch?: string; - - constructor( - private element: ElementRef, - private searchFilterService: SearchFilterService, - ) { - - } - + ngOnChanges(): void { this.element.nativeElement.innerHTML = this.highlight(); } - + highlight() { if (!this.highlightText) { return ''; @@ -28,14 +23,22 @@ export class HighlightDirective implements OnChanges { if (!this.highlightMatch || this.highlightMatch.length === 0) { return this.highlightText; } - - const highlightPattern = this.searchFilterService.createSearcherRegex(this.highlightMatch, true); + + const highlightPattern = this.searchFilterService.createSearcherRegex( + this.highlightMatch, + true, + ); const highlights = this.highlightText.match(highlightPattern); if (!highlights) { return this.highlightText; } const plains = this.highlightText.split(highlightPattern); - const result = highlights.map((highlight, i) => plains[i] + `${highlight}`).join(''); + const result = highlights + .map( + (highlight, i) => + plains[i] + `${highlight}`, + ) + .join(''); return result + plains[plains.length - 1]; } } diff --git a/webapp/src/app/directives/host.directive.ts b/webapp/src/app/directives/host.directive.ts index 0ffb2f2e..b952310d 100644 --- a/webapp/src/app/directives/host.directive.ts +++ b/webapp/src/app/directives/host.directive.ts @@ -1,12 +1,8 @@ -import { Directive, ViewContainerRef } from '@angular/core'; +import { Directive, ViewContainerRef, inject } from '@angular/core'; @Directive({ selector: '[appHost]', - standalone: false }) export class HostDirective { - - constructor(public viewContainerRef: ViewContainerRef) { - } - + viewContainerRef = inject(ViewContainerRef); } diff --git a/webapp/src/app/directives/modal.directive.ts b/webapp/src/app/directives/modal.directive.ts index e2fada96..6af16b03 100644 --- a/webapp/src/app/directives/modal.directive.ts +++ b/webapp/src/app/directives/modal.directive.ts @@ -1,10 +1,8 @@ -import { Directive, ViewContainerRef } from '@angular/core'; +import { Directive, ViewContainerRef, inject } from '@angular/core'; @Directive({ selector: '[appModal]', - standalone: false }) export class ModalDirective { - constructor(public viewContainerRef: ViewContainerRef) { - } + viewContainerRef = inject(ViewContainerRef); } diff --git a/webapp/src/app/directives/resized.directive.ts b/webapp/src/app/directives/resized.directive.ts index 84fb3e71..e22fb9b4 100644 --- a/webapp/src/app/directives/resized.directive.ts +++ b/webapp/src/app/directives/resized.directive.ts @@ -1,6 +1,15 @@ // This code was copied from . -import { Directive, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, Output, } from '@angular/core'; +import { + Directive, + ElementRef, + EventEmitter, + NgZone, + OnDestroy, + OnInit, + Output, + inject, +} from '@angular/core'; export interface ResizedEvent { newRect: DOMRectReadOnly; @@ -10,33 +19,32 @@ export interface ResizedEvent { @Directive({ selector: '[appResized]', - standalone: false }) export class ResizedDirective implements OnInit, OnDestroy { + private readonly element = inject(ElementRef); + private readonly zone = inject(NgZone); + private observer: ResizeObserver; private oldRect?: DOMRectReadOnly; - + @Output() public readonly appResized; - - public constructor( - private readonly element: ElementRef, - private readonly zone: NgZone - ) { + + public constructor() { this.appResized = new EventEmitter(); this.observer = new ResizeObserver((entries) => - this.zone.run(() => this.observe(entries)) + this.zone.run(() => this.observe(entries)), ); } - + public ngOnInit(): void { this.observer.observe(this.element.nativeElement); } - + public ngOnDestroy(): void { this.observer.disconnect(); } - + private observe(entries: ResizeObserverEntry[]): void { const domSize = entries[0]; const resizedEvent: ResizedEvent = { diff --git a/webapp/src/app/external-modules/material.module.ts b/webapp/src/app/external-modules/material.module.ts index 8a34b144..21257439 100644 --- a/webapp/src/app/external-modules/material.module.ts +++ b/webapp/src/app/external-modules/material.module.ts @@ -18,7 +18,11 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; -import { MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions, MatTooltipModule } from '@angular/material/tooltip'; +import { + MAT_TOOLTIP_DEFAULT_OPTIONS, + MatTooltipDefaultOptions, + MatTooltipModule, +} from '@angular/material/tooltip'; import { MatTreeModule } from '@angular/material/tree'; const tooltipOptions: Partial = { @@ -59,5 +63,4 @@ const MODULES = [ }, ], }) -export class MaterialModule { -} +export class MaterialModule {} diff --git a/webapp/src/app/models/cross-code-map.ts b/webapp/src/app/models/cross-code-map.ts index a7481817..d835c4d5 100644 --- a/webapp/src/app/models/cross-code-map.ts +++ b/webapp/src/app/models/cross-code-map.ts @@ -10,7 +10,7 @@ export interface CrossCodeMap { layer: MapLayer[]; attributes: Attributes; screen: Point; - + filename?: string; path?: string; } @@ -43,7 +43,7 @@ export interface MapEntity { type: string; x: number; y: number; - level: number | { level: number, offset: number }; + level: number | { level: number; offset: number }; settings: Partial; } diff --git a/webapp/src/app/models/enemy.ts b/webapp/src/app/models/enemy.ts index aa733062..f01af088 100644 --- a/webapp/src/app/models/enemy.ts +++ b/webapp/src/app/models/enemy.ts @@ -1,54 +1,54 @@ import { Point, Point3 } from './cross-code-map'; export interface EnemyData { - name: string; - boss: boolean; - bossLabel?: string; - bossOrder?: number; - detectType?: DETECT_TYPE; - credit?: number; - aiGroup?: string; - aiLearnType?: ENEMY_AI_LEARN; - enduranceScale?: number; - exp?: number; - level?: number; - boostedLevel?: number; - maxSp?: number; - headIdx: number; - healDropRate?: number; - itemDrops: any[]; - trackers: any; - params: any; - elementModes: any; - modifiers: any; - hpBreaks: any[]; - anims: string; - size: Point3; - cameraZFocus?: number; - dmgZFocus?: number; - padding?: Point; - walkConfigs: any; - material?: COMBATANT_MATERIAL; - proxies: any; - targetDetect: any; - actions: any; - defaultState: any; - states: any; - reactions: any; + name: string; + boss: boolean; + bossLabel?: string; + bossOrder?: number; + detectType?: DETECT_TYPE; + credit?: number; + aiGroup?: string; + aiLearnType?: ENEMY_AI_LEARN; + enduranceScale?: number; + exp?: number; + level?: number; + boostedLevel?: number; + maxSp?: number; + headIdx: number; + healDropRate?: number; + itemDrops: any[]; + trackers: any; + params: any; + elementModes: any; + modifiers: any; + hpBreaks: any[]; + anims: string; + size: Point3; + cameraZFocus?: number; + dmgZFocus?: number; + padding?: Point; + walkConfigs: any; + material?: COMBATANT_MATERIAL; + proxies: any; + targetDetect: any; + actions: any; + defaultState: any; + states: any; + reactions: any; } export enum DETECT_TYPE { - DISTANCE, - VIEW, - NONE + DISTANCE, + VIEW, + NONE, } export enum ENEMY_AI_LEARN { - REGULAR, - IMMEDIATELY + REGULAR, + IMMEDIATELY, } export enum COMBATANT_MATERIAL { - METAL, - ORGANIC + METAL, + ORGANIC, } diff --git a/webapp/src/app/models/events.ts b/webapp/src/app/models/events.ts index 88e89435..91146124 100644 --- a/webapp/src/app/models/events.ts +++ b/webapp/src/app/models/events.ts @@ -20,7 +20,7 @@ export enum EventArrayType { Quest = 'quest', Shop = 'shop', Arena = 'arena', - Trade = 'trade' + Trade = 'trade', } export interface TraderEvent { @@ -28,49 +28,65 @@ export interface TraderEvent { trader?: string; //Maps have it but it's a pain to handle type set and trader set at the same time in the UI } -export type EventArray = - EventType[] | - {quest: EventType[]} | - {shop: EventType[]} | - {arena: EventType[]} | - {trade: TraderEvent} -; +export type EventArray = + | EventType[] + | { quest: EventType[] } + | { shop: EventType[] } + | { arena: EventType[] } + | { trade: TraderEvent }; - - -export function destructureEventArray(events: EventArray): {events: EventType[], type: EventArrayType, trader?: string} { +export function destructureEventArray(events: EventArray): { + events: EventType[]; + type: EventArrayType; + trader?: string; +} { if (Array.isArray(events)) { - return {events: events, type: EventArrayType.Simple}; + return { events: events, type: EventArrayType.Simple }; } else if ('quest' in events) { - return {events: events.quest, type: EventArrayType.Quest}; + return { events: events.quest, type: EventArrayType.Quest }; } else if ('shop' in events) { - return {events: events.shop, type: EventArrayType.Shop}; + return { events: events.shop, type: EventArrayType.Shop }; } else if ('arena' in events) { - return {events: events.arena, type: EventArrayType.Arena}; + return { events: events.arena, type: EventArrayType.Arena }; } else if ('trade' in events) { const event = events.trade.event; return { events: event ?? [], type: EventArrayType.Trade, - trader: events.trade.trader + trader: events.trade.trader, }; } - throw new TypeError('Argument passed to ' + destructureEventArray.name + ' is not of type EventArray.'); + throw new TypeError( + 'Argument passed to ' + + destructureEventArray.name + + ' is not of type EventArray.', + ); } -export function createEventArray(events: EventType[], type: EventArrayType, trader?: string): EventArray { +export function createEventArray( + events: EventType[], + type: EventArrayType, + trader?: string, +): EventArray { switch (type) { - case 'simple': return events; - case 'arena': return {arena: events}; - case 'quest': return {quest: events}; - case 'shop': return {shop: events}; - case 'trade': - return { - trade: { - event: events.length > 0? events : undefined, - trader: trader - } - }; - default: throw new TypeError(`'type' argument passed to ${createEventArray.name} must be of type EventArrayType. Received ${type} instead.`); + case EventArrayType.Simple: + return events; + case EventArrayType.Arena: + return { arena: events }; + case EventArrayType.Quest: + return { quest: events }; + case EventArrayType.Shop: + return { shop: events }; + case EventArrayType.Trade: + return { + trade: { + event: events.length > 0 ? events : undefined, + trader: trader, + }, + }; + default: + throw new TypeError( + `'type' argument passed to ${createEventArray.name} must be of type EventArrayType. Received ${type as unknown as string} instead.`, + ); } } diff --git a/webapp/src/app/models/file-infos.ts b/webapp/src/app/models/file-infos.ts index e7eb9aa8..b5418d9a 100644 --- a/webapp/src/app/models/file-infos.ts +++ b/webapp/src/app/models/file-infos.ts @@ -1,4 +1,4 @@ export interface FileInfos { - images: string[]; - data: string[]; + images: string[]; + data: string[]; } diff --git a/webapp/src/app/models/map-styles.ts b/webapp/src/app/models/map-styles.ts index b48a1d30..90c33ada 100644 --- a/webapp/src/app/models/map-styles.ts +++ b/webapp/src/app/models/map-styles.ts @@ -1,12 +1,10 @@ export interface MapStyles { default: MapStyleType; - + [key: string]: MapStyleType | undefined; } -export interface MapStyleType { - [key: string]: MapStyle | undefined; -} +export type MapStyleType = Record; export interface MapStyle { sheet?: string; diff --git a/webapp/src/app/models/multi-dir-animation.ts b/webapp/src/app/models/multi-dir-animation.ts index a9772d2a..05435719 100644 --- a/webapp/src/app/models/multi-dir-animation.ts +++ b/webapp/src/app/models/multi-dir-animation.ts @@ -1,11 +1,11 @@ import { TileSheet } from './tile-sheet'; export interface MultiDirAnimation { - DOCTYPE: 'MULTI_DIR_ANIMATION'; - name: string; - namedSheets: Record; - frames: number[]; - sheet: TileSheet | string; - dirs: number; - tileOffsets?: number[]; + DOCTYPE: 'MULTI_DIR_ANIMATION'; + name: string; + namedSheets: Record; + frames: number[]; + sheet: TileSheet | string; + dirs: number; + tileOffsets?: number[]; } diff --git a/webapp/src/app/models/multi-entity-animation.ts b/webapp/src/app/models/multi-entity-animation.ts index 49cadb59..40ac891d 100644 --- a/webapp/src/app/models/multi-entity-animation.ts +++ b/webapp/src/app/models/multi-entity-animation.ts @@ -2,56 +2,56 @@ import { Point3 } from './cross-code-map'; import { TileSheet } from './tile-sheet'; export interface MultiEntityAnimation { - DOCTYPE: 'MULTI_ENTITY_ANIMATION'; + DOCTYPE: 'MULTI_ENTITY_ANIMATION'; - anims: Record; - baseSize: Point3; - namedSheets?: Record; - parts: Record; + anims: Record; + baseSize: Point3; + namedSheets?: Record; + parts: Record; } interface EntityAnim { - time: number; - frameCount: number; - repeat: boolean; - partAnims: Record; + time: number; + frameCount: number; + repeat: boolean; + partAnims: Record; } interface PartAnim { - anim: string; - posFrames: number[]; - reset: boolean; - collType?: COLLTYPE; + anim: string; + posFrames: number[]; + reset: boolean; + collType?: COLLTYPE; } interface Part { - anims: { SUB: unknown[] }; - collType: COLLTYPE; - group: string; - heightShape: COLL_HEIGHT_SHAPE; - pos: Point3; - size: Point3; + anims: { SUB: unknown[] }; + collType: COLLTYPE; + group: string; + heightShape: COLL_HEIGHT_SHAPE; + pos: Point3; + size: Point3; } enum COLL_HEIGHT_SHAPE { - NONE, - NORTH_UP, - EAST_UP, - WEST_UP, - SOUTH_UP, + NONE, + NORTH_UP, + EAST_UP, + WEST_UP, + SOUTH_UP, } enum COLLTYPE { - NONE, - IGNORE, - PROJECTILE, - VIRTUAL, - PBLOCK, - NPBLOCK, - BLOCK, - TRIGGER, - PASSIVE, - SEMI_IGNORE, - FENCE, - NPFENCE, + NONE, + IGNORE, + PROJECTILE, + VIRTUAL, + PBLOCK, + NPBLOCK, + BLOCK, + TRIGGER, + PASSIVE, + SEMI_IGNORE, + FENCE, + NPFENCE, } diff --git a/webapp/src/app/models/single-dir-animation.ts b/webapp/src/app/models/single-dir-animation.ts index 547d0b6a..05d1a660 100644 --- a/webapp/src/app/models/single-dir-animation.ts +++ b/webapp/src/app/models/single-dir-animation.ts @@ -1,15 +1,15 @@ import { TileSheet } from './tile-sheet'; export interface SingleDirAnimation { - flipX: boolean; - flipY: boolean; - /** Actually a string[] but used as index for an array */ - frames: number[]; - framesAngle: number[]; - framesSpriteOffset: number[]; - name: string; - repeat: boolean; - sheet: string | TileSheet; - time: number; - wallY: number; + flipX: boolean; + flipY: boolean; + /** Actually a string[] but used as index for an array */ + frames: number[]; + framesAngle: number[]; + framesSpriteOffset: number[]; + name: string; + repeat: boolean; + sheet: string | TileSheet; + time: number; + wallY: number; } diff --git a/webapp/src/app/models/tile-sheet.ts b/webapp/src/app/models/tile-sheet.ts index 7d88d7c1..62c0d1c1 100644 --- a/webapp/src/app/models/tile-sheet.ts +++ b/webapp/src/app/models/tile-sheet.ts @@ -1,8 +1,8 @@ export interface TileSheet { - src: string; - width: number; - height: number; - offX?: number; - offY?: number; - xCount?: number; + src: string; + width: number; + height: number; + offX?: number; + offY?: number; + xCount?: number; } diff --git a/webapp/src/app/pipes/combined-tooltip.pipe.ts b/webapp/src/app/pipes/combined-tooltip.pipe.ts index 5d40410d..92e7bb7a 100644 --- a/webapp/src/app/pipes/combined-tooltip.pipe.ts +++ b/webapp/src/app/pipes/combined-tooltip.pipe.ts @@ -2,12 +2,10 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'combinedTooltip', - standalone: true + standalone: true, }) export class CombinedTooltipPipe implements PipeTransform { - transform(v?: string, v2?: string): string { - return [v, v2].filter(v => !!v).join('\n\n'); + return [v, v2].filter((v) => !!v).join('\n\n'); } - } diff --git a/webapp/src/app/pipes/keep-html.pipe.ts b/webapp/src/app/pipes/keep-html.pipe.ts index 9a7b6c82..f30ed188 100644 --- a/webapp/src/app/pipes/keep-html.pipe.ts +++ b/webapp/src/app/pipes/keep-html.pipe.ts @@ -1,14 +1,13 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Pipe({ - name: 'keepHtml', pure: false, - standalone: false + name: 'keepHtml', + pure: false, }) export class KeepHtmlPipe implements PipeTransform { - constructor(private sanitizer: DomSanitizer) { - } - + private sanitizer = inject(DomSanitizer); + transform(content: string) { return this.sanitizer.bypassSecurityTrustHtml(content); } diff --git a/webapp/src/app/services/3d/babylon-viewer.service.ts b/webapp/src/app/services/3d/babylon-viewer.service.ts index ca5564da..45474976 100644 --- a/webapp/src/app/services/3d/babylon-viewer.service.ts +++ b/webapp/src/app/services/3d/babylon-viewer.service.ts @@ -1,5 +1,13 @@ -import { Injectable } from '@angular/core'; -import { Color3, Engine, FreeCamera, HemisphericLight, Mesh, Scene, Vector3 } from '@babylonjs/core'; +import { Injectable, inject } from '@angular/core'; +import { + Color3, + Engine, + FreeCamera, + HemisphericLight, + Mesh, + Scene, + Vector3, +} from '@babylonjs/core'; import { EditorView } from '../../models/editor-view'; import { GlobalEventsService } from '../global-events.service'; import { Globals } from '../globals'; @@ -20,9 +28,11 @@ interface CamStore { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class BabylonViewerService { + private globalEvents = inject(GlobalEventsService); + private engine?: Engine; private scene?: Scene; private cam?: FreeCamera; @@ -30,12 +40,7 @@ export class BabylonViewerService { private textureGenerator = new TextureGenerator(); private groundLayers: CCMapLayer[] = []; private entityManager?: EntityManager3d; - - public constructor( - private globalEvents: GlobalEventsService - ) { - } - + public async init(canvas: HTMLCanvasElement) { try { this.globalEvents.babylonLoading.next(true); @@ -45,7 +50,7 @@ export class BabylonViewerService { this.globalEvents.babylonLoading.next(false); } } - + private async initBabylonInternal(canvas: HTMLCanvasElement) { const map = Globals.map; this.groundLayers = []; @@ -53,60 +58,73 @@ export class BabylonViewerService { this.engine = engine; const scene = new Scene(engine); this.scene = scene; - - const cam = new CustomFreeCamera('camera', new Vector3(0, 0, -4), scene); + + const cam = new CustomFreeCamera( + 'camera', + new Vector3(0, 0, -4), + scene, + ); cam.position.y = 1; cam.speed = 0.3; cam.attachControl(canvas, true); cam.minZ = 0; this.cam = cam; - + Object.assign(cam.rotation, { x: 0.7, y: 0, - z: 0 + z: 0, }); - + Object.assign(cam.position, { x: map.mapWidth / 2, y: 31, - z: (-map.mapHeight / 2) - 36 + z: -map.mapHeight / 2 - 36, }); - + try { - const store = JSON.parse(sessionStorage.getItem(this.storageKey)!) as CamStore; + const store = JSON.parse( + sessionStorage.getItem(this.storageKey)!, + ) as CamStore; Object.assign(cam.position, store.position); Object.assign(cam.rotation, store.rotation); - } catch (e) { - - } - - const light1 = new HemisphericLight('light1', new Vector3(0, 1, 0), scene); - const light2 = new HemisphericLight('light2', new Vector3(0, -1, 0), scene); + } catch (e) {} + + const light1 = new HemisphericLight( + 'light1', + new Vector3(0, 1, 0), + scene, + ); + const light2 = new HemisphericLight( + 'light2', + new Vector3(0, -1, 0), + scene, + ); // light1.diffuse = new Color3(1, 1, 1).scale(0.7); // light2.diffuse = new Color3(1, 1, 1).scale(0.7); - + light1.specular = new Color3(1, 1, 1).scale(0.2); light2.specular = new Color3(1, 1, 1).scale(0.2); - - + // const light1 = new HemisphericLight('light1', new Vector3(1, 1, 1), scene); // light1.diffuse = new Color3(1, 1, 1).scale(1); // light1.specular = new Color3(1, 1, 1).scale(0); - + performance.mark('start'); - - let layers = map.layers.filter(layer => layer.details.type.toLowerCase() === 'collision'); + + let layers = map.layers.filter( + (layer) => layer.details.type.toLowerCase() === 'collision', + ); layers.sort((a, b) => a.details.level - b.details.level); - + // add another layer to the bottom to make the ground visible layers = [await this.generateGroundLayer(layers[0]), ...layers]; // layers = [layers[2]]; - + const meshGenerator = new LayerMeshGenerator(); - + const allMeshes: Mesh[] = []; - + for (let i = 0; i < layers.length; i++) { performance.mark('layerStart'); const coll = layers[i]; @@ -115,56 +133,67 @@ export class BabylonViewerService { if (i === layers.length - 2) { renderAll = 9999; } - const layerMaterial = this.textureGenerator.generate(coll.details.level + 1 + renderAll, scene); + const layerMaterial = this.textureGenerator.generate( + coll.details.level + 1 + renderAll, + scene, + ); performance.mark('texture'); const meshes = meshGenerator.generateLevel(coll, above, scene); - + for (const mesh of meshes) { mesh.material = layerMaterial; } - + allMeshes.push(...meshes); performance.mark('layerEnd'); - performance.measure('layer: ' + coll.details.level, 'layerStart', 'layerEnd'); - performance.measure('texture: ' + coll.details.level, 'layerStart', 'texture'); - performance.measure('mesh: ' + coll.details.level, 'texture', 'layerEnd'); - - + performance.measure( + 'layer: ' + coll.details.level, + 'layerStart', + 'layerEnd', + ); + performance.measure( + 'texture: ' + coll.details.level, + 'layerStart', + 'texture', + ); + performance.measure( + 'mesh: ' + coll.details.level, + 'texture', + 'layerEnd', + ); } performance.mark('layersEnd'); - - + const entityManager = new EntityManager3d(); this.entityManager = entityManager; - + const entityGenerator = new EntityGenerator(entityManager); - + const entities = map.entityManager.entities; const promises: Promise[] = []; for (const e of entities) { promises.push(entityGenerator.generateEntity(e, scene)); } - + await Promise.all(promises); performance.mark('end'); - + entityManager.init(entityGenerator, scene); - + const toggle = new ToggleMesh(scene); - + addWireframeButton(toggle, allMeshes); - + showAxis(2, scene); - + performance.measure('layers', 'start', 'layersEnd'); performance.measure('entities', 'layersEnd', 'end'); - + this.globalEvents.currentView.next(EditorView.Entities); - + engine.runRenderLoop(() => scene.render()); } - - + private async generateGroundLayer(other: CCMapLayer) { const data: number[][] = []; const height = other.details.height + 10; @@ -174,7 +203,7 @@ export class BabylonViewerService { data[y][x] = 2; } } - const layer = new CCMapLayer(other.getPhaserLayer()!.tilemap); + const layer = new CCMapLayer(other.getPhaserLayer().tilemap); await layer.init({ type: 'Collision', name: 'groundColl', @@ -186,29 +215,29 @@ export class BabylonViewerService { repeat: false, distance: 1, tilesize: Globals.TILE_SIZE, - moveSpeed: {x: 0, y: 0}, + moveSpeed: { x: 0, y: 0 }, data: data, }); - + customPutTilesAt(layer.details.data, layer.getPhaserLayer()); this.groundLayers.push(layer); return layer; } - + onDestroy() { for (const layer of this.groundLayers) { layer.destroy(); } - + if (this.entityManager) { this.entityManager.destroy(); } - + this.textureGenerator.destroy(); if (this.cam) { const store: CamStore = { rotation: this.cam.rotation, - position: this.cam.position + position: this.cam.position, }; sessionStorage.setItem(this.storageKey, JSON.stringify(store)); } @@ -217,10 +246,9 @@ export class BabylonViewerService { } if (this.engine) { const gl = this.engine._gl; - gl!.getExtension('WEBGL_lose_context')!.loseContext(); + gl.getExtension('WEBGL_lose_context')!.loseContext(); this.engine.dispose(); } this.globalEvents.babylonLoading.next(false); } - } diff --git a/webapp/src/app/services/3d/camera/custom-free-camera.ts b/webapp/src/app/services/3d/camera/custom-free-camera.ts index f9f9ad96..0442631a 100644 --- a/webapp/src/app/services/3d/camera/custom-free-camera.ts +++ b/webapp/src/app/services/3d/camera/custom-free-camera.ts @@ -5,7 +5,6 @@ import { WasdCamInput } from './wasd-cam-input'; * Same as Free Camera but uses WASD instead of arrow keys to control */ export class CustomFreeCamera extends UniversalCamera { - constructor(name: string, position: Vector3, scene: Scene) { super(name, position, scene); this.inertia = 0; @@ -13,6 +12,4 @@ export class CustomFreeCamera extends UniversalCamera { this.inputs.removeByType('FreeCameraKeyboardMoveInput'); this.inputs.add(new WasdCamInput()); } - - } diff --git a/webapp/src/app/services/3d/camera/wasd-cam-input.ts b/webapp/src/app/services/3d/camera/wasd-cam-input.ts index 3abe47e2..d1abb892 100644 --- a/webapp/src/app/services/3d/camera/wasd-cam-input.ts +++ b/webapp/src/app/services/3d/camera/wasd-cam-input.ts @@ -1,13 +1,12 @@ import { ICameraInput, Nullable, Vector3 } from '@babylonjs/core'; import { CustomFreeCamera } from './custom-free-camera'; - /** * Uses WASD to move camera in all directions and QE to move it up/down */ export class WasdCamInput implements ICameraInput { camera: Nullable = null; - + private onKeyDown?: (evt: KeyboardEvent) => void; private onKeyUp?: (evt: KeyboardEvent) => void; private keys = new Set(); @@ -25,16 +24,16 @@ export class WasdCamInput implements ICameraInput { ...this.keysBackward, ...this.keysUp, ...this.keysDown, - ...this.keysTurbo + ...this.keysTurbo, ]; private sensibility = 11.8; - + attachControl(noPreventDefault?: boolean): void { const engine = this.camera!.getEngine(); const element = engine.getInputElement()!; if (!this.onKeyDown) { element.tabIndex = 1; - this.onKeyDown = evt => { + this.onKeyDown = (evt) => { if (this.keysAll.includes(evt.code)) { this.keys.add(evt.code); if (!noPreventDefault) { @@ -42,7 +41,7 @@ export class WasdCamInput implements ICameraInput { } } }; - this.onKeyUp = evt => { + this.onKeyUp = (evt) => { if (this.keysAll.includes(evt.code)) { this.keys.delete(evt.code); if (!noPreventDefault) { @@ -50,12 +49,12 @@ export class WasdCamInput implements ICameraInput { } } }; - + element.addEventListener('keydown', this.onKeyDown, false); element.addEventListener('keyup', this.onKeyUp, false); } } - + detachControl(): void { const engine = this.camera!.getEngine(); const element = engine.getInputElement()!; @@ -67,15 +66,17 @@ export class WasdCamInput implements ICameraInput { this.onKeyUp = undefined; } } - + checkInputs() { if (this.onKeyDown) { const camera = this.camera!; - const scale = this.keysTurbo.some(t => this.keys.has(t)) ? 3.5 : 1; - + const scale = this.keysTurbo.some((t) => this.keys.has(t)) + ? 3.5 + : 1; + for (const key of this.keys) { const speed = camera._computeLocalCameraSpeed(); - + if (this.keysForward.includes(key)) { camera._localDirection.copyFromFloats(0, 0, speed); } else if (this.keysBackward.includes(key)) { @@ -91,21 +92,29 @@ export class WasdCamInput implements ICameraInput { } else if (this.keysTurbo.includes(key)) { continue; } - - camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix); - Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection); - camera._transformedDirection.scaleToRef(this.sensibility * scale, camera._transformedDirection); + + camera + .getViewMatrix() + .invertToRef(camera._cameraTransformMatrix); + Vector3.TransformNormalToRef( + camera._localDirection, + camera._cameraTransformMatrix, + camera._transformedDirection, + ); + camera._transformedDirection.scaleToRef( + this.sensibility * scale, + camera._transformedDirection, + ); camera.cameraDirection.addInPlace(camera._transformedDirection); } } } - + getClassName(): string { return 'WasdCamInput'; } - + getSimpleName(): string { return 'WasdCamInput'; } - } diff --git a/webapp/src/app/services/3d/debug/show-axis.ts b/webapp/src/app/services/3d/debug/show-axis.ts index 53fd5e55..82bd7f41 100644 --- a/webapp/src/app/services/3d/debug/show-axis.ts +++ b/webapp/src/app/services/3d/debug/show-axis.ts @@ -1,10 +1,31 @@ -import { Color3, DynamicTexture, Mesh, MeshBuilder, Scene, StandardMaterial, Vector3 } from '@babylonjs/core'; +import { + Color3, + DynamicTexture, + Mesh, + MeshBuilder, + Scene, + StandardMaterial, + Vector3, +} from '@babylonjs/core'; export function showAxis(size: number, scene: Scene) { function makeTextPlane(text: string, color: string, size: number) { - const dynamicTexture = new DynamicTexture('DynamicTexture', 50, scene, true); + const dynamicTexture = new DynamicTexture( + 'DynamicTexture', + 50, + scene, + true, + ); dynamicTexture.hasAlpha = true; - dynamicTexture.drawText(text, 5, 40, 'bold 36px Arial', color, 'transparent', true); + dynamicTexture.drawText( + text, + 5, + 40, + 'bold 36px Arial', + color, + 'transparent', + true, + ); const plane = Mesh.CreatePlane('TextPlane', size, scene, true); const mat = new StandardMaterial('TextPlaneMaterial', scene); plane.material = mat; @@ -13,33 +34,54 @@ export function showAxis(size: number, scene: Scene) { mat.diffuseTexture = dynamicTexture; return plane; } - - const axisX = MeshBuilder.CreateLines('axisX', { - points: [ - new Vector3(), new Vector3(size, 0, 0), new Vector3(size * 0.95, 0.05 * size, 0), - new Vector3(size, 0, 0), new Vector3(size * 0.95, -0.05 * size, 0) - ] - }, scene); + + const axisX = MeshBuilder.CreateLines( + 'axisX', + { + points: [ + new Vector3(), + new Vector3(size, 0, 0), + new Vector3(size * 0.95, 0.05 * size, 0), + new Vector3(size, 0, 0), + new Vector3(size * 0.95, -0.05 * size, 0), + ], + }, + scene, + ); axisX.color = new Color3(1, 0, 0); const xChar = makeTextPlane('X', 'red', size / 10); xChar.position = new Vector3(0.9 * size, -0.05 * size, 0); - - const axisY = MeshBuilder.CreateLines('axisY', { - points: [ - new Vector3(), new Vector3(0, size, 0), new Vector3(-0.05 * size, size * 0.95, 0), - new Vector3(0, size, 0), new Vector3(0.05 * size, size * 0.95, 0) - ] - }, scene); + + const axisY = MeshBuilder.CreateLines( + 'axisY', + { + points: [ + new Vector3(), + new Vector3(0, size, 0), + new Vector3(-0.05 * size, size * 0.95, 0), + new Vector3(0, size, 0), + new Vector3(0.05 * size, size * 0.95, 0), + ], + }, + scene, + ); axisY.color = new Color3(0, 1, 0); const yChar = makeTextPlane('Y', 'green', size / 10); yChar.position = new Vector3(0, 0.9 * size, -0.05 * size); - - const axisZ = MeshBuilder.CreateLines('axisZ', { - points: [ - new Vector3(), new Vector3(0, 0, size), new Vector3(0, -0.05 * size, size * 0.95), - new Vector3(0, 0, size), new Vector3(0, 0.05 * size, size * 0.95) - ] - }, scene); + + const axisZ = MeshBuilder.CreateLines( + 'axisZ', + { + points: [ + new Vector3(), + new Vector3(0, 0, size), + new Vector3(0, -0.05 * size, size * 0.95), + new Vector3(0, 0, size), + new Vector3(0, 0.05 * size, size * 0.95), + ], + }, + scene, + ); axisZ.color = new Color3(0, 0, 1); const zChar = makeTextPlane('Z', 'blue', size / 10); zChar.position = new Vector3(0, 0.05 * size, 0.9 * size); diff --git a/webapp/src/app/services/3d/debug/toggle-mesh.ts b/webapp/src/app/services/3d/debug/toggle-mesh.ts index 03db4fbe..0e17c5e7 100644 --- a/webapp/src/app/services/3d/debug/toggle-mesh.ts +++ b/webapp/src/app/services/3d/debug/toggle-mesh.ts @@ -1,22 +1,30 @@ import { Scene } from '@babylonjs/core'; -import { AdvancedDynamicTexture, Button, Control, StackPanel } from '@babylonjs/gui'; +import { + AdvancedDynamicTexture, + Button, + Control, + StackPanel, +} from '@babylonjs/gui'; export class ToggleMesh { - private texture: AdvancedDynamicTexture; private buttonWidth = 150; private padding = 5; private panel: StackPanel; - + constructor(scene: Scene) { - this.texture = AdvancedDynamicTexture.CreateFullscreenUI('myui', true, scene); + this.texture = AdvancedDynamicTexture.CreateFullscreenUI( + 'myui', + true, + scene, + ); this.panel = new StackPanel(); this.panel.isVertical = false; this.panel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT; this.panel.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM; this.texture.addControl(this.panel); } - + addButton(title: string, action: () => void) { const button1 = Button.CreateSimpleButton('but1', title); button1.widthInPixels = this.buttonWidth; diff --git a/webapp/src/app/services/3d/entities/entity-generator.ts b/webapp/src/app/services/3d/entities/entity-generator.ts index 6aec5a22..4ed85911 100644 --- a/webapp/src/app/services/3d/entities/entity-generator.ts +++ b/webapp/src/app/services/3d/entities/entity-generator.ts @@ -12,7 +12,7 @@ import { StandardMaterial, Texture, Vector3, - Vector4 + Vector4, } from '@babylonjs/core'; import { Globals } from '../../globals'; import { CCEntity, Fix } from '../../phaser/entities/cc-entity'; @@ -26,19 +26,18 @@ interface Dimensions { } export class EntityGenerator { - private entityManager: EntityManager3d; - + constructor(entityManager: EntityManager3d) { this.entityManager = entityManager; } - + async generateEntity(entity: CCEntity, scene: Scene) { const fix = entity.entitySettings.sheets.fix; if (!fix || fix.length === 0) { throw new Error('entity has no graphics (fix)'); } - + for (const img of fix) { let material: StandardMaterial; if (img.gfx === 'pixel') { @@ -46,158 +45,200 @@ export class EntityGenerator { } else { material = await this.makeMaterial(entity, img, scene); } - + const m = this.generateMesh(entity, img, material, scene); m.edgesWidth = 0; m.edgesColor = new Color4(1, 1, 0, 1); m.enableEdgesRendering(); m.actionManager = new ActionManager(scene); - m.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnPickTrigger, () => { - this.entityManager.onClick(m); - })); - m.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnDoublePickTrigger, () => { - this.entityManager.onDoubleClick(m); - })); - + m.actionManager.registerAction( + new ExecuteCodeAction(ActionManager.OnPickTrigger, () => { + this.entityManager.onClick(m); + }), + ); + m.actionManager.registerAction( + new ExecuteCodeAction(ActionManager.OnDoublePickTrigger, () => { + this.entityManager.onDoubleClick(m); + }), + ); + this.entityManager.registerEntity(entity, m); } } - - private generateMesh(entity: CCEntity, fix: Fix, material: StandardMaterial, scene: Scene) { + + private generateMesh( + entity: CCEntity, + fix: Fix, + material: StandardMaterial, + scene: Scene, + ) { const actualSize = entity.getActualSize(); if ( - actualSize.x === fix.w && actualSize.y + actualSize.z === fix.h || + (actualSize.x === fix.w && actualSize.y + actualSize.z === fix.h) || entity.entitySettings.scalableY || - actualSize.x === fix.w && actualSize.x !== actualSize.y + (actualSize.x === fix.w && actualSize.x !== actualSize.y) ) { return this.generateBox(entity, fix, material, scene); } else { return this.generatePlane(entity, fix, material, scene); } } - - private generatePlane(entity: CCEntity, fix: Fix, material: StandardMaterial, scene: Scene) { + + private generatePlane( + entity: CCEntity, + fix: Fix, + material: StandardMaterial, + scene: Scene, + ) { const width = fix.w / Globals.TILE_SIZE; const height = fix.h / Globals.TILE_SIZE; - + let fullWidth = width; if (entity.entitySettings.scalableX) { fullWidth = entity.details.settings['size'].x / Globals.TILE_SIZE; } - + const meshes: Mesh[] = []; - + for (let start = 0; start < fullWidth; start += width) { const currWidth = Math.min(fullWidth - start, width); const customFix = { x: fix.x, y: fix.y, w: currWidth * Globals.TILE_SIZE, - h: fix.h + h: fix.h, }; - - const uvs = this.calculateUvs(material.diffuseTexture, customFix, entity.entitySettings.scalableX); - - const mesh = MeshBuilder.CreatePlane('', { - width: currWidth, - height: height, - sideOrientation: Mesh.DOUBLESIDE, - frontUVs: uvs, - backUVs: uvs - }, scene); - + + const uvs = this.calculateUvs( + material.diffuseTexture, + customFix, + entity.entitySettings.scalableX, + ); + + const mesh = MeshBuilder.CreatePlane( + '', + { + width: currWidth, + height: height, + sideOrientation: Mesh.DOUBLESIDE, + frontUVs: uvs, + backUVs: uvs, + }, + scene, + ); + if (fullWidth === width) { mesh.billboardMode = Mesh.BILLBOARDMODE_Y; } const pos = this.posFromEntity(entity); mesh.position.copyFromFloats(pos.x, pos.z, -pos.y); mesh.position.addInPlaceFromFloats(0, height / 2, 0); - mesh.position.addInPlaceFromFloats(start + (currWidth - width) / 2, 0, 0); + mesh.position.addInPlaceFromFloats( + start + (currWidth - width) / 2, + 0, + 0, + ); mesh.material = material; meshes.push(mesh); } - + if (meshes.length === 1) { return meshes[0]; } else { return Mesh.MergeMeshes(meshes)!; } } - - + // TODO: babylon introduced MeshBuilder.CreateTiledBox with a new version, // but the new version requires a typescript update. // To update typescript we also need to update angular. // So after angular is on version >= 9 we can try to add this, // for now the textures are broken with scalable entities - private generateBox(entity: CCEntity, fix: Fix, material: StandardMaterial, scene: Scene) { + private generateBox( + entity: CCEntity, + fix: Fix, + material: StandardMaterial, + scene: Scene, + ) { // const width = fix.w / Globals.TILE_SIZE; // const height = fix.h / Globals.TILE_SIZE; - + const size = entity.getActualSize(); - + size.z = Math.max(0.01, size.z); - + const scaledSize = { x: size.x / Globals.TILE_SIZE, y: size.y / Globals.TILE_SIZE, z: size.z / Globals.TILE_SIZE, }; - - const uvs = this.calculateBoxUvs(material.diffuseTexture, fix, size.y, fix.flipX); - - const mesh = MeshBuilder.CreateBox('', { - width: scaledSize.x, - height: scaledSize.z, - depth: scaledSize.y, - faceUV: uvs, - // sideOrientation: Mesh.DOUBLESIDE, - // @ts-ignore - wrap: true - }, scene); - + + const uvs = this.calculateBoxUvs( + material.diffuseTexture, + fix, + size.y, + fix.flipX, + ); + + const mesh = MeshBuilder.CreateBox( + '', + { + width: scaledSize.x, + height: scaledSize.z, + depth: scaledSize.y, + faceUV: uvs, + // sideOrientation: Mesh.DOUBLESIDE, + wrap: true, + }, + scene, + ); + const pos = this.posFromEntity(entity); - mesh.position.copyFromFloats(entity.container.x / Globals.TILE_SIZE, pos.z, -entity.container.y / Globals.TILE_SIZE); - mesh.position.addInPlaceFromFloats(scaledSize.x / 2, scaledSize.z / 2, -scaledSize.y / 2); + mesh.position.copyFromFloats( + entity.container.x / Globals.TILE_SIZE, + pos.z, + -entity.container.y / Globals.TILE_SIZE, + ); + mesh.position.addInPlaceFromFloats( + scaledSize.x / 2, + scaledSize.z / 2, + -scaledSize.y / 2, + ); mesh.position.addInPlaceFromFloats(0, 0, 1 / Globals.TILE_SIZE); mesh.material = material; - + return mesh; } - + // z = height private posFromEntity(entity: CCEntity) { const level = entity.details.level; - + let levelObj = Globals.map.levels[level.level]; if (!levelObj) { // TODO: check why it's actually not defined - levelObj = {height: 0}; + levelObj = { height: 0 }; } - + const out = new Vector3( entity.container.x, entity.container.y, - levelObj.height + level.offset + levelObj.height + level.offset, ); - + // bounding box offset - const boundBoxOffset = {x: 0, y: 0}; + const boundBoxOffset = { x: 0, y: 0 }; if (entity.entitySettings.baseSize) { boundBoxOffset.x = entity.entitySettings.baseSize.x / 2; boundBoxOffset.y = entity.entitySettings.baseSize.y; } - - out.addInPlaceFromFloats( - boundBoxOffset.x, - boundBoxOffset.y, - 0 - ); + + out.addInPlaceFromFloats(boundBoxOffset.x, boundBoxOffset.y, 0); out.scaleInPlace(1 / Globals.TILE_SIZE); - + return out; } - + private async makeMaterial(entity: CCEntity, fix: Fix, scene: Scene) { const material = new StandardMaterial(fix.gfx, scene); const texture = await this.loadTexture(Globals.URL + fix.gfx, scene); @@ -205,120 +246,172 @@ export class EntityGenerator { // texture.wrapV = Texture.CLAMP_ADDRESSMODE; texture.hasAlpha = true; material.diffuseTexture = texture; - if (fix.renderMode === 'lighter' || entity.entitySettings.sheets.renderMode === 'lighter') { + if ( + fix.renderMode === 'lighter' || + entity.entitySettings.sheets.renderMode === 'lighter' + ) { material.alphaMode = Engine.ALPHA_ADD; material.opacityTexture = texture; } - + return material; } - + private makePixelMaterial(entity: CCEntity, fix: Fix, scene: Scene) { const material = new StandardMaterial('pixel' + fix.tint, scene); - + const color = fix.tint || 0; - + const b = color % 256; const g = ((color - b) / 256) % 256; - const r = ((color - b) / 256 ** 2) - g / 256; - + const r = (color - b) / 256 ** 2 - g / 256; + material.diffuseColor = new Color3(r / 255, g / 255, b / 255); material.alpha = fix.alpha || 0; - if (fix.renderMode === 'lighter' || entity.entitySettings.sheets.renderMode === 'lighter') { + if ( + fix.renderMode === 'lighter' || + entity.entitySettings.sheets.renderMode === 'lighter' + ) { material.alphaMode = Engine.ALPHA_ADD; } - + return material; } - + private async loadTexture(url: string, scene: Scene) { return new Promise((res, rej) => { - const texture = new Texture(url, scene, undefined, undefined, Texture.NEAREST_SAMPLINGMODE, () => { - res(texture); - }, rej); + const texture = new Texture( + url, + scene, + undefined, + undefined, + Texture.NEAREST_SAMPLINGMODE, + () => { + res(texture); + }, + rej, + ); }); } - - private calculateUvs(texture: BaseTexture | null, fix: Dimensions, flipX = false) { + + private calculateUvs( + texture: BaseTexture | null, + fix: Dimensions, + flipX = false, + ) { let size: ISize; if (texture) { size = texture.getSize(); } else { size = { height: 1, - width: 1 + width: 1, }; } - + let out: Vector4; const u = fix.w + fix.x; const v = size.height - (fix.h + fix.y); const u2 = fix.x; const v2 = size.height - fix.y; - + out = new Vector4(u, v, u2, v2); if (flipX) { out = new Vector4(u2, v, u, v2); } - - return out.multiplyByFloats(1 / size.width, 1 / size.height, 1 / size.width, 1 / size.height); + + return out.multiplyByFloats( + 1 / size.width, + 1 / size.height, + 1 / size.width, + 1 / size.height, + ); } - - private calculateBoxUvs(texture: BaseTexture | null, fix: Dimensions, depth: number, flipX = false) { + + private calculateBoxUvs( + texture: BaseTexture | null, + fix: Dimensions, + depth: number, + flipX = false, + ) { let box = fix.w === depth; - + // always use box, looks better most of the time box = true; - - const front = this.calculateUvs(texture, { - x: fix.x, - y: fix.y + depth, - w: fix.w, - h: fix.h - depth, - }, !flipX); - - const back = this.calculateUvs(texture, { - x: fix.x, - y: fix.y + depth, - w: fix.w, - h: fix.h - depth, - }, flipX); - - let right = this.calculateUvs(texture, { - x: fix.x + fix.w - 1, - y: fix.y + depth, - w: 1, - h: fix.h - depth, - }, !flipX); - - let left = this.calculateUvs(texture, { - x: fix.x, - y: fix.y + depth, - w: 1, - h: fix.h - depth, - }, !flipX); - + + const front = this.calculateUvs( + texture, + { + x: fix.x, + y: fix.y + depth, + w: fix.w, + h: fix.h - depth, + }, + !flipX, + ); + + const back = this.calculateUvs( + texture, + { + x: fix.x, + y: fix.y + depth, + w: fix.w, + h: fix.h - depth, + }, + flipX, + ); + + let right = this.calculateUvs( + texture, + { + x: fix.x + fix.w - 1, + y: fix.y + depth, + w: 1, + h: fix.h - depth, + }, + !flipX, + ); + + let left = this.calculateUvs( + texture, + { + x: fix.x, + y: fix.y + depth, + w: 1, + h: fix.h - depth, + }, + !flipX, + ); + // needs to be rotated 90° - const top = this.calculateUvs(texture, { - x: fix.x, - y: fix.y, - w: fix.w, - h: fix.h - (fix.h - depth), - }, !flipX); - - const bottom = this.calculateUvs(texture, { - x: fix.x, - y: fix.y, - w: fix.w, - h: fix.h, - }, true); - + const top = this.calculateUvs( + texture, + { + x: fix.x, + y: fix.y, + w: fix.w, + h: fix.h - (fix.h - depth), + }, + !flipX, + ); + + const bottom = this.calculateUvs( + texture, + { + x: fix.x, + y: fix.y, + w: fix.w, + h: fix.h, + }, + true, + ); + if (flipX) { const tmp = left; left = right; right = tmp; } - + if (box) { return [ back, // back, diff --git a/webapp/src/app/services/3d/entities/entity-manager-3d.ts b/webapp/src/app/services/3d/entities/entity-manager-3d.ts index f5e1de5c..88b8a2e4 100644 --- a/webapp/src/app/services/3d/entities/entity-manager-3d.ts +++ b/webapp/src/app/services/3d/entities/entity-manager-3d.ts @@ -7,12 +7,11 @@ import { EntityGenerator } from './entity-generator'; const EDGE_WIDTH = 6; export class EntityManager3d { - private meshMap = new Map(); private entityMap = new Map(); private prevSelected?: Mesh; private sub?: Subscription; - + /** Called after all entities are added */ init(entityGenerator: EntityGenerator, scene: Scene) { for (const [mesh, entity] of this.meshMap) { @@ -21,21 +20,23 @@ export class EntityManager3d { break; } } - - this.sub = Globals.globalEventsService.updateEntitySettings.subscribe(async entity => { - await entityGenerator.generateEntity(entity, scene); - const m = this.entityMap.get(entity)!; - m.edgesWidth = EDGE_WIDTH; - this.prevSelected = m; - }); + + this.sub = Globals.globalEventsService.updateEntitySettings.subscribe( + async (entity) => { + await entityGenerator.generateEntity(entity, scene); + const m = this.entityMap.get(entity)!; + m.edgesWidth = EDGE_WIDTH; + this.prevSelected = m; + }, + ); } - + destroy() { if (this.sub) { this.sub.unsubscribe(); } } - + onClick(m: Mesh) { if (this.prevSelected) { this.select(this.prevSelected, false); @@ -51,7 +52,7 @@ export class EntityManager3d { } entity.doubleClick(); } - + select(m: Mesh, activate: boolean) { if (activate) { m.edgesWidth = EDGE_WIDTH; @@ -60,12 +61,11 @@ export class EntityManager3d { throw new Error('entity should always exist'); } Globals.globalEventsService.selectedEntity.next(entity); - } else { m.edgesWidth = 0; } } - + registerEntity(entity: CCEntity, m: Mesh) { this.meshMap.set(m, entity); const prev = this.entityMap.get(entity); diff --git a/webapp/src/app/services/3d/layer-generation/boundary-tracing/boundary-tracer.spec.ts b/webapp/src/app/services/3d/layer-generation/boundary-tracing/boundary-tracer.spec.ts index 8bd0b764..f013fce2 100644 --- a/webapp/src/app/services/3d/layer-generation/boundary-tracing/boundary-tracer.spec.ts +++ b/webapp/src/app/services/3d/layer-generation/boundary-tracing/boundary-tracer.spec.ts @@ -4,17 +4,16 @@ import { BoundaryTracer } from './boundary-tracer'; import { NodeTracer } from './node-grid'; describe('boundary tracing', () => { - let tracer: BoundaryTracer; - + beforeEach(() => { // jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - + beforeEach(() => { tracer = new NodeTracer(); }); - + // to generate output in tracer: // let pathOut = '\n'; // for (const tile of path) { @@ -29,218 +28,243 @@ describe('boundary tracing', () => { // } // console.log('hole', pathOut); // } - + // https://user-images.githubusercontent.com/9483499/76783283-e52c9d00-67b1-11ea-8761-b49a62526d73.png it('simple rectangle without holes', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 2, y: 2}, - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 3, y: 4}, - {x: 4, y: 4}, - {x: 5, y: 4}, - {x: 5, y: 3}, - {x: 5, y: 2}, - {x: 4, y: 2}, - {x: 3, y: 2}, - ], - holes: [] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 2, y: 2 }, + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 3, y: 4 }, + { x: 4, y: 4 }, + { x: 5, y: 4 }, + { x: 5, y: 3 }, + { x: 5, y: 2 }, + { x: 4, y: 2 }, + { x: 3, y: 2 }, + ], + holes: [], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76783353-055c5c00-67b2-11ea-9e3f-e6184d4f659b.png it('simple with corners', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 11, 2, 8, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 10, 2, 9, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 3, y: 1}, - {x: 2, y: 2}, - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 3, y: 5}, - {x: 4, y: 5}, - {x: 5, y: 4}, - {x: 5, y: 3}, - {x: 5, y: 2}, - {x: 4, y: 1}, - ], - holes: [] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 11, 2, 8, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 10, 2, 9, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 3, y: 1 }, + { x: 2, y: 2 }, + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 3, y: 5 }, + { x: 4, y: 5 }, + { x: 5, y: 4 }, + { x: 5, y: 3 }, + { x: 5, y: 2 }, + { x: 4, y: 1 }, + ], + holes: [], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76783703-a77c4400-67b2-11ea-8c61-1be4a3ee85ff.png it('1 tile hole', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 2, 0, 2, 0, 0, 0], - [0, 0, 2, 2, 2, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 2, y: 5}, - {x: 2, y: 6}, - {x: 3, y: 6}, - {x: 4, y: 6}, - {x: 5, y: 6}, - {x: 5, y: 5}, - {x: 5, y: 4}, - {x: 5, y: 3}, - {x: 4, y: 3}, - {x: 3, y: 3}, - ], - holes: [[ - {x: 3, y: 4}, - {x: 3, y: 5}, - {x: 4, y: 5}, - {x: 4, y: 4}, - ]] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 2, 0, 2, 0, 0, 0], + [0, 0, 2, 2, 2, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 2, y: 5 }, + { x: 2, y: 6 }, + { x: 3, y: 6 }, + { x: 4, y: 6 }, + { x: 5, y: 6 }, + { x: 5, y: 5 }, + { x: 5, y: 4 }, + { x: 5, y: 3 }, + { x: 4, y: 3 }, + { x: 3, y: 3 }, + ], + holes: [ + [ + { x: 3, y: 4 }, + { x: 3, y: 5 }, + { x: 4, y: 5 }, + { x: 4, y: 4 }, + ], + ], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76783543-59ffd700-67b2-11ea-9541-e86f7861c466.png it('4 tile hole', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 2, 2, 2, 2, 2, 2, 0], - [0, 2, 2, 2, 2, 2, 2, 0], - [0, 2, 2, 0, 0, 2, 2, 0], - [0, 2, 2, 0, 0, 2, 2, 0], - [0, 2, 2, 2, 2, 2, 2, 0], - [0, 2, 2, 2, 2, 2, 2, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 1, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 1, y: 5}, - {x: 1, y: 6}, - {x: 1, y: 7}, - {x: 2, y: 7}, - {x: 3, y: 7}, - {x: 4, y: 7}, - {x: 5, y: 7}, - {x: 6, y: 7}, - {x: 7, y: 7}, - {x: 7, y: 6}, - {x: 7, y: 5}, - {x: 7, y: 4}, - {x: 7, y: 3}, - {x: 7, y: 2}, - {x: 7, y: 1}, - {x: 6, y: 1}, - {x: 5, y: 1}, - {x: 4, y: 1}, - {x: 3, y: 1}, - {x: 2, y: 1}, - ], - holes: [[ - {x: 3, y: 3}, - {x: 3, y: 4}, - {x: 3, y: 5}, - {x: 4, y: 5}, - {x: 5, y: 5}, - {x: 5, y: 4}, - {x: 5, y: 3}, - {x: 4, y: 3}, - ]] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 2, 2, 2, 2, 2, 2, 0], + [0, 2, 2, 2, 2, 2, 2, 0], + [0, 2, 2, 0, 0, 2, 2, 0], + [0, 2, 2, 0, 0, 2, 2, 0], + [0, 2, 2, 2, 2, 2, 2, 0], + [0, 2, 2, 2, 2, 2, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 1, y: 5 }, + { x: 1, y: 6 }, + { x: 1, y: 7 }, + { x: 2, y: 7 }, + { x: 3, y: 7 }, + { x: 4, y: 7 }, + { x: 5, y: 7 }, + { x: 6, y: 7 }, + { x: 7, y: 7 }, + { x: 7, y: 6 }, + { x: 7, y: 5 }, + { x: 7, y: 4 }, + { x: 7, y: 3 }, + { x: 7, y: 2 }, + { x: 7, y: 1 }, + { x: 6, y: 1 }, + { x: 5, y: 1 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + { x: 2, y: 1 }, + ], + holes: [ + [ + { x: 3, y: 3 }, + { x: 3, y: 4 }, + { x: 3, y: 5 }, + { x: 4, y: 5 }, + { x: 5, y: 5 }, + { x: 5, y: 4 }, + { x: 5, y: 3 }, + { x: 4, y: 3 }, + ], + ], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76784090-5456c100-67b3-11ea-961b-1aedb4faedd2.png it('hole with diagonals', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 2, 2, 2, 2, 2, 0, 0], - [0, 2, 9, 0, 10, 2, 0, 0], - [0, 2, 0, 0, 0, 2, 0, 0], - [0, 2, 2, 8, 11, 2, 0, 0], - [0, 2, 2, 2, 2, 2, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 1, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 1, y: 5}, - {x: 1, y: 6}, - {x: 2, y: 6}, - {x: 3, y: 6}, - {x: 4, y: 6}, - {x: 5, y: 6}, - {x: 6, y: 6}, - {x: 6, y: 5}, - {x: 6, y: 4}, - {x: 6, y: 3}, - {x: 6, y: 2}, - {x: 6, y: 1}, - {x: 5, y: 1}, - {x: 4, y: 1}, - {x: 3, y: 1}, - {x: 2, y: 1}, - ], - holes: [[ - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 3, y: 4}, - {x: 4, y: 5}, - {x: 5, y: 4}, - {x: 5, y: 3}, - {x: 4, y: 2}, - {x: 3, y: 2}, - ]] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 2, 2, 2, 2, 2, 0, 0], + [0, 2, 9, 0, 10, 2, 0, 0], + [0, 2, 0, 0, 0, 2, 0, 0], + [0, 2, 2, 8, 11, 2, 0, 0], + [0, 2, 2, 2, 2, 2, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 1, y: 5 }, + { x: 1, y: 6 }, + { x: 2, y: 6 }, + { x: 3, y: 6 }, + { x: 4, y: 6 }, + { x: 5, y: 6 }, + { x: 6, y: 6 }, + { x: 6, y: 5 }, + { x: 6, y: 4 }, + { x: 6, y: 3 }, + { x: 6, y: 2 }, + { x: 6, y: 1 }, + { x: 5, y: 1 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + { x: 2, y: 1 }, + ], + holes: [ + [ + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 3, y: 4 }, + { x: 4, y: 5 }, + { x: 5, y: 4 }, + { x: 5, y: 3 }, + { x: 4, y: 2 }, + { x: 3, y: 2 }, + ], + ], + }, + ); }); - + // don't care about result, just don't crash describe('crash tests', () => { - // https://user-images.githubusercontent.com/9483499/76784400-d941da80-67b3-11ea-8193-1e0753aa76f6.png it('weird shape', () => { const layer = new SimpleTileLayer(); - + layer.initSimple([ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], @@ -250,16 +274,16 @@ describe('boundary tracing', () => { [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0, 0, 0], ]); const traceObj = tracer.getContour(layer); console.log(traceObj); }); - + // https://user-images.githubusercontent.com/9483499/76887863-2fca1a00-6883-11ea-98f8-547ca0f7fbb5.png it('?!', () => { const layer = new SimpleTileLayer(); - + layer.initSimple([ [0, 10, 2, 10, 10, 8, 8, 8], [0, 9, 10, 10, 10, 8, 10, 8], @@ -268,28 +292,58 @@ describe('boundary tracing', () => { [0, 2, 8, 8, 9, 8, 8, 0], [10, 9, 8, 8, 11, 9, 8, 0], [2, 8, 11, 8, 10, 11, 0, 0], - [10, 8, 8, 8, 2, 0, 0, 0] + [10, 8, 8, 8, 2, 0, 0, 0], ]); const traceObj = tracer.getContour(layer); console.log(traceObj); }); - + // https://user-images.githubusercontent.com/9483499/76967305-89842000-6927-11ea-9df7-f7b36a7b1100.png it('?!²', () => { const layer = new SimpleTileLayer(); layer.initSimple([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 0, 11, 8, 8, 0, 0], - [0, 0, 0, 10, 0, 0, 0, 0, 2, 0, 2, 2, 11, 8, 10, 8, 11, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 2, 8, 9, 9, 8, 10, 11, 10, 8, 0], - [0, 0, 0, 0, 0, 0, 2, 9, 0, 10, 10, 9, 10, 10, 10, 10, 11, 0, 10, 0], - [0, 0, 0, 0, 0, 0, 0, 2, 8, 10, 9, 11, 11, 11, 8, 11, 2, 2, 10, 0], - [0, 0, 0, 0, 0, 9, 8, 2, 2, 2, 9, 11, 10, 8, 9, 10, 11, 2, 8, 0], - [0, 0, 0, 0, 0, 9, 8, 2, 10, 11, 9, 9, 10, 9, 2, 10, 11, 8, 2, 0], - [0, 0, 0, 0, 0, 9, 8, 9, 0, 2, 10, 9, 11, 11, 11, 0, 8, 10, 2, 0], - [0, 0, 0, 0, 0, 0, 8, 8, 0, 9, 11, 9, 0, 11, 8, 11, 0, 0, 10, 2], - [0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 8, 9, 8, 11, 11, 11, 10, 0, 10], + [ + 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 0, 11, 8, 8, 0, + 0, + ], + [ + 0, 0, 0, 10, 0, 0, 0, 0, 2, 0, 2, 2, 11, 8, 10, 8, 11, 0, 0, + 0, + ], + [ + 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 2, 8, 9, 9, 8, 10, 11, 10, + 8, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 2, 9, 0, 10, 10, 9, 10, 10, 10, 10, 11, 0, + 10, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 2, 8, 10, 9, 11, 11, 11, 8, 11, 2, 2, + 10, 0, + ], + [ + 0, 0, 0, 0, 0, 9, 8, 2, 2, 2, 9, 11, 10, 8, 9, 10, 11, 2, 8, + 0, + ], + [ + 0, 0, 0, 0, 0, 9, 8, 2, 10, 11, 9, 9, 10, 9, 2, 10, 11, 8, + 2, 0, + ], + [ + 0, 0, 0, 0, 0, 9, 8, 9, 0, 2, 10, 9, 11, 11, 11, 0, 8, 10, + 2, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 8, 8, 0, 9, 11, 9, 0, 11, 8, 11, 0, 0, 10, + 2, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 8, 9, 8, 11, 11, 11, 10, 0, + 10, + ], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 11, 9, 9, 9, 9, 9, 9, 10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 11, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0], @@ -297,12 +351,12 @@ describe('boundary tracing', () => { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ]); const result = tracer.getContour(layer); console.log(result); }); - + // https://user-images.githubusercontent.com/9483499/76968814-cc46f780-6929-11ea-87fc-c32401af7e2d.png it('evil pattern', () => { const layer = new SimpleTileLayer(); @@ -312,12 +366,12 @@ describe('boundary tracing', () => { [0, 0, 11, 0, 0, 0], [0, 0, 2, 0, 0, 0], [0, 2, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0], ]); const result = tracer.getContour(layer); console.log(result); }); - + // https://user-images.githubusercontent.com/9483499/80650239-e967fd80-8a73-11ea-9c58-00dc8e0f2a52.png it('forest/caves/cave-013-pandza-01', () => { const layer = new SimpleTileLayer(); @@ -329,362 +383,401 @@ describe('boundary tracing', () => { [2, 2, 9, 0, 0, 0, 0, 0, 10, 2, 2], [2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], ]); const result = tracer.getContour(layer); console.log(result); }); - }); - - + // https://user-images.githubusercontent.com/9483499/76784726-6e44d380-67b4-11ea-9236-af4817cba85d.png it('complex', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 11, 8, 0, 0], - [0, 11, 2, 2, 9, 10, 2, 0], - [0, 2, 9, 0, 0, 0, 2, 0], - [0, 2, 0, 11, 2, 2, 2, 0], - [0, 2, 2, 2, 0, 0, 2, 0], - [0, 10, 2, 2, 8, 0, 2, 0], - [0, 0, 0, 10, 2, 2, 2, 0], - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 2, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 1, y: 5}, - {x: 2, y: 6}, - {x: 3, y: 6}, - {x: 4, y: 7}, - {x: 5, y: 7}, - {x: 6, y: 7}, - {x: 7, y: 7}, - {x: 7, y: 6}, - {x: 7, y: 5}, - {x: 7, y: 4}, - {x: 7, y: 3}, - {x: 7, y: 2}, - {x: 7, y: 1}, - {x: 6, y: 1}, - {x: 5, y: 0}, - {x: 4, y: 1}, - {x: 3, y: 1}, - ], - holes: [[ - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 3, y: 4}, - {x: 4, y: 3}, - {x: 5, y: 3}, - {x: 6, y: 3}, - {x: 6, y: 2}, - {x: 5, y: 1}, - {x: 4, y: 2}, - {x: 3, y: 2}, - ], [ - {x: 4, y: 4}, - {x: 4, y: 5}, - {x: 5, y: 6}, - {x: 6, y: 6}, - {x: 6, y: 5}, - {x: 6, y: 4}, - {x: 5, y: 4}, - ]] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 11, 8, 0, 0], + [0, 11, 2, 2, 9, 10, 2, 0], + [0, 2, 9, 0, 0, 0, 2, 0], + [0, 2, 0, 11, 2, 2, 2, 0], + [0, 2, 2, 2, 0, 0, 2, 0], + [0, 10, 2, 2, 8, 0, 2, 0], + [0, 0, 0, 10, 2, 2, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 2, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 1, y: 5 }, + { x: 2, y: 6 }, + { x: 3, y: 6 }, + { x: 4, y: 7 }, + { x: 5, y: 7 }, + { x: 6, y: 7 }, + { x: 7, y: 7 }, + { x: 7, y: 6 }, + { x: 7, y: 5 }, + { x: 7, y: 4 }, + { x: 7, y: 3 }, + { x: 7, y: 2 }, + { x: 7, y: 1 }, + { x: 6, y: 1 }, + { x: 5, y: 0 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + ], + holes: [ + [ + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 3, y: 4 }, + { x: 4, y: 3 }, + { x: 5, y: 3 }, + { x: 6, y: 3 }, + { x: 6, y: 2 }, + { x: 5, y: 1 }, + { x: 4, y: 2 }, + { x: 3, y: 2 }, + ], + [ + { x: 4, y: 4 }, + { x: 4, y: 5 }, + { x: 5, y: 6 }, + { x: 6, y: 6 }, + { x: 6, y: 5 }, + { x: 6, y: 4 }, + { x: 5, y: 4 }, + ], + ], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76882082-6bacb180-687a-11ea-9977-8c7380648600.png it('full collision', () => { - compare(0, { - layer: [ - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2] - ] - }, { - path: [ - {x: 0, y: 0}, - {x: 0, y: 1}, - {x: 0, y: 2}, - {x: 0, y: 3}, - {x: 0, y: 4}, - {x: 0, y: 5}, - {x: 0, y: 6}, - {x: 0, y: 7}, - {x: 0, y: 8}, - {x: 0, y: 9}, - {x: 1, y: 9}, - {x: 2, y: 9}, - {x: 3, y: 9}, - {x: 4, y: 9}, - {x: 5, y: 9}, - {x: 6, y: 9}, - {x: 7, y: 9}, - {x: 8, y: 9}, - {x: 8, y: 8}, - {x: 8, y: 7}, - {x: 8, y: 6}, - {x: 8, y: 5}, - {x: 8, y: 4}, - {x: 8, y: 3}, - {x: 8, y: 2}, - {x: 8, y: 1}, - {x: 8, y: 0}, - {x: 7, y: 0}, - {x: 6, y: 0}, - {x: 5, y: 0}, - {x: 4, y: 0}, - {x: 3, y: 0}, - {x: 2, y: 0}, - {x: 1, y: 0}, - ], - holes: [] - }); + compare( + 0, + { + layer: [ + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2], + ], + }, + { + path: [ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 0, y: 2 }, + { x: 0, y: 3 }, + { x: 0, y: 4 }, + { x: 0, y: 5 }, + { x: 0, y: 6 }, + { x: 0, y: 7 }, + { x: 0, y: 8 }, + { x: 0, y: 9 }, + { x: 1, y: 9 }, + { x: 2, y: 9 }, + { x: 3, y: 9 }, + { x: 4, y: 9 }, + { x: 5, y: 9 }, + { x: 6, y: 9 }, + { x: 7, y: 9 }, + { x: 8, y: 9 }, + { x: 8, y: 8 }, + { x: 8, y: 7 }, + { x: 8, y: 6 }, + { x: 8, y: 5 }, + { x: 8, y: 4 }, + { x: 8, y: 3 }, + { x: 8, y: 2 }, + { x: 8, y: 1 }, + { x: 8, y: 0 }, + { x: 7, y: 0 }, + { x: 6, y: 0 }, + { x: 5, y: 0 }, + { x: 4, y: 0 }, + { x: 3, y: 0 }, + { x: 2, y: 0 }, + { x: 1, y: 0 }, + ], + holes: [], + }, + ); }); - + // https://user-images.githubusercontent.com/9483499/76883774-f2fb2480-687c-11ea-9414-e38689674677.png describe('inception', () => { it('outer', () => { - compare(0, { - layer: [ - [11, 2, 2, 2, 2, 2, 2, 8], - [2, 9, 0, 0, 0, 0, 10, 2], - [2, 0, 11, 2, 2, 8, 0, 2], - [2, 0, 2, 9, 10, 2, 0, 2], - [2, 0, 2, 8, 11, 2, 0, 2], - [2, 0, 10, 2, 2, 9, 0, 2], - [2, 8, 0, 0, 0, 0, 11, 2], - [10, 2, 2, 2, 2, 2, 2, 9], - ] - }, { - path: [ - {x: 1, y: 0}, - {x: 0, y: 1}, - {x: 0, y: 2}, - {x: 0, y: 3}, - {x: 0, y: 4}, - {x: 0, y: 5}, - {x: 0, y: 6}, - {x: 0, y: 7}, - {x: 1, y: 8}, - {x: 2, y: 8}, - {x: 3, y: 8}, - {x: 4, y: 8}, - {x: 5, y: 8}, - {x: 6, y: 8}, - {x: 7, y: 8}, - {x: 8, y: 7}, - {x: 8, y: 6}, - {x: 8, y: 5}, - {x: 8, y: 4}, - {x: 8, y: 3}, - {x: 8, y: 2}, - {x: 8, y: 1}, - {x: 7, y: 0}, - {x: 6, y: 0}, - {x: 5, y: 0}, - {x: 4, y: 0}, - {x: 3, y: 0}, - {x: 2, y: 0}, - ], - holes: [[ - {x: 2, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 1, y: 5}, - {x: 1, y: 6}, - {x: 2, y: 7}, - {x: 3, y: 7}, - {x: 4, y: 7}, - {x: 5, y: 7}, - {x: 6, y: 7}, - {x: 7, y: 6}, - {x: 7, y: 5}, - {x: 7, y: 4}, - {x: 7, y: 3}, - {x: 7, y: 2}, - {x: 6, y: 1}, - {x: 5, y: 1}, - {x: 4, y: 1}, - {x: 3, y: 1}, - ]] - }); + compare( + 0, + { + layer: [ + [11, 2, 2, 2, 2, 2, 2, 8], + [2, 9, 0, 0, 0, 0, 10, 2], + [2, 0, 11, 2, 2, 8, 0, 2], + [2, 0, 2, 9, 10, 2, 0, 2], + [2, 0, 2, 8, 11, 2, 0, 2], + [2, 0, 10, 2, 2, 9, 0, 2], + [2, 8, 0, 0, 0, 0, 11, 2], + [10, 2, 2, 2, 2, 2, 2, 9], + ], + }, + { + path: [ + { x: 1, y: 0 }, + { x: 0, y: 1 }, + { x: 0, y: 2 }, + { x: 0, y: 3 }, + { x: 0, y: 4 }, + { x: 0, y: 5 }, + { x: 0, y: 6 }, + { x: 0, y: 7 }, + { x: 1, y: 8 }, + { x: 2, y: 8 }, + { x: 3, y: 8 }, + { x: 4, y: 8 }, + { x: 5, y: 8 }, + { x: 6, y: 8 }, + { x: 7, y: 8 }, + { x: 8, y: 7 }, + { x: 8, y: 6 }, + { x: 8, y: 5 }, + { x: 8, y: 4 }, + { x: 8, y: 3 }, + { x: 8, y: 2 }, + { x: 8, y: 1 }, + { x: 7, y: 0 }, + { x: 6, y: 0 }, + { x: 5, y: 0 }, + { x: 4, y: 0 }, + { x: 3, y: 0 }, + { x: 2, y: 0 }, + ], + holes: [ + [ + { x: 2, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 1, y: 5 }, + { x: 1, y: 6 }, + { x: 2, y: 7 }, + { x: 3, y: 7 }, + { x: 4, y: 7 }, + { x: 5, y: 7 }, + { x: 6, y: 7 }, + { x: 7, y: 6 }, + { x: 7, y: 5 }, + { x: 7, y: 4 }, + { x: 7, y: 3 }, + { x: 7, y: 2 }, + { x: 6, y: 1 }, + { x: 5, y: 1 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + ], + ], + }, + ); }); - + it('inner', () => { - compare(1, { - layer: [ - [11, 2, 2, 2, 2, 2, 2, 8], - [2, 9, 0, 0, 0, 0, 10, 2], - [2, 0, 11, 2, 2, 8, 0, 2], - [2, 0, 2, 9, 10, 2, 0, 2], - [2, 0, 2, 8, 11, 2, 0, 2], - [2, 0, 10, 2, 2, 9, 0, 2], - [2, 8, 0, 0, 0, 0, 11, 2], - [10, 2, 2, 2, 2, 2, 2, 9], - ] - }, { - path: [ - {x: 3, y: 2}, - {x: 2, y: 3}, - {x: 2, y: 4}, - {x: 2, y: 5}, - {x: 3, y: 6}, - {x: 4, y: 6}, - {x: 5, y: 6}, - {x: 6, y: 5}, - {x: 6, y: 4}, - {x: 6, y: 3}, - {x: 5, y: 2}, - {x: 4, y: 2}, - ], - holes: [[ - {x: 3, y: 4}, - {x: 4, y: 5}, - {x: 5, y: 4}, - {x: 4, y: 3}, - ]] - }); + compare( + 1, + { + layer: [ + [11, 2, 2, 2, 2, 2, 2, 8], + [2, 9, 0, 0, 0, 0, 10, 2], + [2, 0, 11, 2, 2, 8, 0, 2], + [2, 0, 2, 9, 10, 2, 0, 2], + [2, 0, 2, 8, 11, 2, 0, 2], + [2, 0, 10, 2, 2, 9, 0, 2], + [2, 8, 0, 0, 0, 0, 11, 2], + [10, 2, 2, 2, 2, 2, 2, 9], + ], + }, + { + path: [ + { x: 3, y: 2 }, + { x: 2, y: 3 }, + { x: 2, y: 4 }, + { x: 2, y: 5 }, + { x: 3, y: 6 }, + { x: 4, y: 6 }, + { x: 5, y: 6 }, + { x: 6, y: 5 }, + { x: 6, y: 4 }, + { x: 6, y: 3 }, + { x: 5, y: 2 }, + { x: 4, y: 2 }, + ], + holes: [ + [ + { x: 3, y: 4 }, + { x: 4, y: 5 }, + { x: 5, y: 4 }, + { x: 4, y: 3 }, + ], + ], + }, + ); }); }); - + // https://user-images.githubusercontent.com/9483499/76885959-5d619400-6880-11ea-9851-76321d165598.png describe('separate blocks', () => { it('top left', () => { - compare(0, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 11, 2, 2, 0, 2, 8, 0], - [0, 2, 2, 2, 0, 2, 2, 0], - [0, 2, 2, 2, 0, 2, 2, 0], - [0, 0, 0, 0, 0, 2, 2, 0], - [0, 0, 2, 2, 2, 2, 9, 0], - [0, 0, 10, 2, 2, 9, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 2, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 2, y: 4}, - {x: 3, y: 4}, - {x: 4, y: 4}, - {x: 4, y: 3}, - {x: 4, y: 2}, - {x: 4, y: 1}, - {x: 3, y: 1}, - ], - holes: [] - }); + compare( + 0, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 11, 2, 2, 0, 2, 8, 0], + [0, 2, 2, 2, 0, 2, 2, 0], + [0, 2, 2, 2, 0, 2, 2, 0], + [0, 0, 0, 0, 0, 2, 2, 0], + [0, 0, 2, 2, 2, 2, 9, 0], + [0, 0, 10, 2, 2, 9, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 2, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 2, y: 4 }, + { x: 3, y: 4 }, + { x: 4, y: 4 }, + { x: 4, y: 3 }, + { x: 4, y: 2 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + ], + holes: [], + }, + ); }); - + it('bottom right', () => { - compare(1, { - layer: [ - [0, 0, 0, 0, 0, 0, 0, 0], - [0, 11, 2, 2, 0, 2, 8, 0], - [0, 2, 2, 2, 0, 2, 2, 0], - [0, 2, 2, 2, 0, 2, 2, 0], - [0, 0, 0, 0, 0, 2, 2, 0], - [0, 0, 2, 2, 2, 2, 9, 0], - [0, 0, 10, 2, 2, 9, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0] - ] - }, { - path: [ - {x: 5, y: 1}, - {x: 5, y: 2}, - {x: 5, y: 3}, - {x: 5, y: 4}, - {x: 5, y: 5}, - {x: 4, y: 5}, - {x: 3, y: 5}, - {x: 2, y: 5}, - {x: 2, y: 6}, - {x: 3, y: 7}, - {x: 4, y: 7}, - {x: 5, y: 7}, - {x: 6, y: 6}, - {x: 7, y: 5}, - {x: 7, y: 4}, - {x: 7, y: 3}, - {x: 7, y: 2}, - {x: 6, y: 1}, - ], - holes: [] - }); + compare( + 1, + { + layer: [ + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 11, 2, 2, 0, 2, 8, 0], + [0, 2, 2, 2, 0, 2, 2, 0], + [0, 2, 2, 2, 0, 2, 2, 0], + [0, 0, 0, 0, 0, 2, 2, 0], + [0, 0, 2, 2, 2, 2, 9, 0], + [0, 0, 10, 2, 2, 9, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + ], + }, + { + path: [ + { x: 5, y: 1 }, + { x: 5, y: 2 }, + { x: 5, y: 3 }, + { x: 5, y: 4 }, + { x: 5, y: 5 }, + { x: 4, y: 5 }, + { x: 3, y: 5 }, + { x: 2, y: 5 }, + { x: 2, y: 6 }, + { x: 3, y: 7 }, + { x: 4, y: 7 }, + { x: 5, y: 7 }, + { x: 6, y: 6 }, + { x: 7, y: 5 }, + { x: 7, y: 4 }, + { x: 7, y: 3 }, + { x: 7, y: 2 }, + { x: 6, y: 1 }, + ], + holes: [], + }, + ); }); }); - + // https://user-images.githubusercontent.com/9483499/76888261-eb8b4980-6883-11ea-87f1-31d7f0171fc8.png it('all types of tiles', () => { - compare(0, { - layer: [ - [0, 1, 1, 1, 1, 1, 4, 0], - [0, 27, 2, 2, 2, 24, 5, 0], - [0, 2, 25, 1, 26, 2, 6, 0], - [0, 2, 24, 1, 27, 2, 7, 0], - [12, 26, 2, 2, 2, 25, 0, 0], - [13, 3, 23, 2, 20, 3, 3, 3], - [14, 0, 22, 2, 21, 16, 17, 3], - [15, 0, 3, 3, 3, 18, 19, 3] - ] - }, { - path: [ - {x: 2, y: 1}, - {x: 1, y: 2}, - {x: 1, y: 3}, - {x: 1, y: 4}, - {x: 2, y: 5}, - {x: 3, y: 5}, - {x: 2, y: 6}, - {x: 3, y: 7}, - {x: 4, y: 7}, - {x: 5, y: 6}, - {x: 4, y: 5}, - {x: 5, y: 5}, - {x: 6, y: 4}, - {x: 6, y: 3}, - {x: 6, y: 2}, - {x: 5, y: 1}, - {x: 4, y: 1}, - {x: 3, y: 1}, - ], - holes: [[ - {x: 3, y: 2}, - {x: 2, y: 3}, - {x: 3, y: 4}, - {x: 4, y: 4}, - {x: 5, y: 3}, - {x: 4, y: 2}, - ]] - }); + compare( + 0, + { + layer: [ + [0, 1, 1, 1, 1, 1, 4, 0], + [0, 27, 2, 2, 2, 24, 5, 0], + [0, 2, 25, 1, 26, 2, 6, 0], + [0, 2, 24, 1, 27, 2, 7, 0], + [12, 26, 2, 2, 2, 25, 0, 0], + [13, 3, 23, 2, 20, 3, 3, 3], + [14, 0, 22, 2, 21, 16, 17, 3], + [15, 0, 3, 3, 3, 18, 19, 3], + ], + }, + { + path: [ + { x: 2, y: 1 }, + { x: 1, y: 2 }, + { x: 1, y: 3 }, + { x: 1, y: 4 }, + { x: 2, y: 5 }, + { x: 3, y: 5 }, + { x: 2, y: 6 }, + { x: 3, y: 7 }, + { x: 4, y: 7 }, + { x: 5, y: 6 }, + { x: 4, y: 5 }, + { x: 5, y: 5 }, + { x: 6, y: 4 }, + { x: 6, y: 3 }, + { x: 6, y: 2 }, + { x: 5, y: 1 }, + { x: 4, y: 1 }, + { x: 3, y: 1 }, + ], + holes: [ + [ + { x: 3, y: 2 }, + { x: 2, y: 3 }, + { x: 3, y: 4 }, + { x: 4, y: 4 }, + { x: 5, y: 3 }, + { x: 4, y: 2 }, + ], + ], + }, + ); }); - - function compare(index: number, input: { layer: number[][] }, expected: { path: Point[], holes: Point[][] }) { + + function compare( + index: number, + input: { layer: number[][] }, + expected: { path: Point[]; holes: Point[][] }, + ) { const layer = new SimpleTileLayer(); - + layer.initSimple(input.layer); - + const result = tracer.getContour(layer); - + const traceObj = result[index]; - + expected.path = reorder(expected.path, traceObj.points[0]); const holes = []; for (let i = 0; i < expected.holes.length; i++) { @@ -692,19 +785,28 @@ describe('boundary tracing', () => { holes.push(reorder(hole, traceObj.holes[i][0])); } expected.holes = holes; - - expect(traceObj.points.map(p => ({x: p.x, y: p.y}))).toEqual(expected.path); - expect(traceObj.holes.map(h => h.map(p => ({x: p.x, y: p.y})))).toEqual(expected.holes); + + expect(traceObj.points.map((p) => ({ x: p.x, y: p.y }))).toEqual( + expected.path, + ); + expect( + traceObj.holes.map((h) => h.map((p) => ({ x: p.x, y: p.y }))), + ).toEqual(expected.holes); } - + // doesn't matter where the path starts, only the order is important function reorder(path: Point[], startPoint: Point) { - const index = path.findIndex(p => p.x === startPoint.x && p.y === startPoint.y); - expect(index).toBeGreaterThanOrEqual(0, `path should not contain point: {${startPoint.x}/${startPoint.y}}`); + const index = path.findIndex( + (p) => p.x === startPoint.x && p.y === startPoint.y, + ); + expect(index).toBeGreaterThanOrEqual( + 0, + `path should not contain point: {${startPoint.x}/${startPoint.y}}`, + ); return rotate(path, index); } - - function rotate(arr: Array, n: number) { + + function rotate(arr: any[], n: number) { return arr.slice(n, arr.length).concat(arr.slice(0, n)); } }); diff --git a/webapp/src/app/services/3d/layer-generation/boundary-tracing/node-grid.ts b/webapp/src/app/services/3d/layer-generation/boundary-tracing/node-grid.ts index 61b34fc7..64398988 100644 --- a/webapp/src/app/services/3d/layer-generation/boundary-tracing/node-grid.ts +++ b/webapp/src/app/services/3d/layer-generation/boundary-tracing/node-grid.ts @@ -55,7 +55,10 @@ export class NodeGrid { for (let j = i + 1; j < result.length; j++) { const sub = result[j]; const node = sub.points[0]; - if (poly.contains(node.x, node.y) && subPolys.every(p => !p.contains(node.x, node.y))) { + if ( + poly.contains(node.x, node.y) && + subPolys.every((p) => !p.contains(node.x, node.y)) + ) { desc.holes.push(sub.points); desc.holePolys.push(sub.poly); result.splice(j, 1); @@ -67,7 +70,9 @@ export class NodeGrid { return result; } - private findPolygon(found: PolygonDescription[]): PolygonDescription | null { + private findPolygon( + found: PolygonDescription[], + ): PolygonDescription | null { let start: Node | undefined; for (const node of this.nodes) { if (node.hasConnections) { @@ -95,50 +100,58 @@ export class NodeGrid { nodes = nodes.reverse(); } - const p = new Phaser.Geom.Polygon(nodes as unknown as Phaser.Geom.Point[]); + const p = new Phaser.Geom.Polygon( + nodes as unknown as Phaser.Geom.Point[], + ); return { points: nodes, holes: [], poly: p, holePolys: [] }; } - private connect(index: number, n11: Node, n12: Node, n21: Node, n22: Node): void { + private connect( + index: number, + n11: Node, + n12: Node, + n21: Node, + n22: Node, + ): void { switch (index) { - case 0: - return; - case 2: // ■ - n11.connectTo(n21); //Left - n12.connectTo(n22); //Right - n11.connectTo(n12); //Top - n21.connectTo(n22); //Bottom - return; - case 8: // ◣ - case 20: - case 24: - n11.connectTo(n21); //Left - n21.connectTo(n22); //Bottom - n11.connectTo(n22); //Top -> bottom - return; - case 9: // ◤ - case 21: - case 25: - n11.connectTo(n21); //Left - n11.connectTo(n12); //Top - n21.connectTo(n12); //Bottom -> top - return; - case 10: // ◥ - case 22: - case 26: - n12.connectTo(n22); //Right - n11.connectTo(n12); //Top - n11.connectTo(n22); //Top -> bottom - return; - case 11: // ◢ - case 23: - case 27: - n12.connectTo(n22); //Right - n21.connectTo(n22); //Bottom - n21.connectTo(n12); //Bottom -> top - return; - default: - return; + case 0: + return; + case 2: // ■ + n11.connectTo(n21); //Left + n12.connectTo(n22); //Right + n11.connectTo(n12); //Top + n21.connectTo(n22); //Bottom + return; + case 8: // ◣ + case 20: + case 24: + n11.connectTo(n21); //Left + n21.connectTo(n22); //Bottom + n11.connectTo(n22); //Top -> bottom + return; + case 9: // ◤ + case 21: + case 25: + n11.connectTo(n21); //Left + n11.connectTo(n12); //Top + n21.connectTo(n12); //Bottom -> top + return; + case 10: // ◥ + case 22: + case 26: + n12.connectTo(n22); //Right + n11.connectTo(n12); //Top + n11.connectTo(n22); //Top -> bottom + return; + case 11: // ◢ + case 23: + case 27: + n12.connectTo(n22); //Right + n21.connectTo(n22); //Bottom + n21.connectTo(n12); //Bottom -> top + return; + default: + return; //throw new Error('Unknown collision tile: ' + index); } } @@ -151,8 +164,10 @@ export class NodeGrid { const conn1 = { x: next.x - node.x, y: next.y - node.y }; const conn2 = { x: prev.x - node.x, y: prev.y - node.y }; - return (conn1.x === conn2.x && conn1.y > conn2.y) // ◥ - || (conn1.x > conn2.x); // ■, ◣, ◤, ◢ + return ( + (conn1.x === conn2.x && conn1.y > conn2.y) || // ◥ + conn1.x > conn2.x + ); // ■, ◣, ◤, ◢ } } @@ -177,7 +192,8 @@ export class Node { const connIndex = this.connections.indexOf(node); if (connIndex >= 0) { this.connections[connIndex] = undefined as unknown as Node; - node.connections[node.connections.indexOf(this)] = undefined as unknown as Node; + node.connections[node.connections.indexOf(this)] = + undefined as unknown as Node; } else { this.connections[this.nextConnection] = node; node.connections[node.nextConnection] = this; @@ -188,7 +204,7 @@ export class Node { } public get hasConnections(): boolean { - return this.connections.filter(c => !!c).length > 0; + return this.connections.filter((c) => !!c).length > 0; } public get firstConnection(): Node { @@ -201,7 +217,12 @@ export class Node { return result; } - private findRouteToGo(node: Node, from: Node, hole: boolean, result: Node[]): void { + private findRouteToGo( + node: Node, + from: Node, + hole: boolean, + result: Node[], + ): void { this.connectTo(from); //Removes the connection. if (node === this && node !== from) { @@ -211,7 +232,9 @@ export class Node { result.push(this); const fromDir = this.direction(from); - const nextNode = hole ? this.rightNode(fromDir) : this.leftNode(fromDir); + const nextNode = hole + ? this.rightNode(fromDir) + : this.leftNode(fromDir); if (nextNode) { return nextNode.findRouteToGo(node, this, hole, result); } @@ -269,34 +292,34 @@ export class Node { const dirY = connection.y - this.y; switch (dirY) { - case -1: - switch (dirX) { - case -1: - return 7; - case 0: - return 0; - case 1: - return 1; - } - break; - case 0: - switch (dirX) { - case -1: - return 6; - case 1: - return 2; - } - break; - case 1: - switch (dirX) { case -1: - return 5; + switch (dirX) { + case -1: + return 7; + case 0: + return 0; + case 1: + return 1; + } + break; case 0: - return 4; + switch (dirX) { + case -1: + return 6; + case 1: + return 2; + } + break; case 1: - return 3; - } - break; + switch (dirX) { + case -1: + return 5; + case 0: + return 4; + case 1: + return 3; + } + break; } throw new Error('Invalid connection'); } diff --git a/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.spec.ts b/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.spec.ts index b8a5a967..19ee7948 100644 --- a/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.spec.ts +++ b/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.spec.ts @@ -2,71 +2,72 @@ import { LayerMeshGenerator } from './layer-mesh-generator'; import { SimpleTileLayer } from './simple-tile-layer'; describe('layer mesh generator', () => { - beforeEach(() => { // jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - - + // outputs 2d array similar to the following test cases // `[\n${data.map(inner => '\t[' + inner.join(', ') + ']').join(',\n')} // ]` describe('layer transformations below master level', () => { - it('empty layer => full with red blocks', () => { - compare([ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - [ - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - ]); + compare( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + [ + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + ], + ); }); - + it('blue blocks => holes', () => { - compare([ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - [ - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - ]); + compare( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + [ + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + ], + ); }); - + it('complex blue shape', () => { compare( [ @@ -79,7 +80,7 @@ describe('layer mesh generator', () => { [0, 0, 6, 1, 1, 1, 4, 0, 0, 0, 0, 0], [0, 0, 0, 6, 1, 1, 5, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], @@ -91,11 +92,11 @@ describe('layer mesh generator', () => { [2, 2, 8, 0, 0, 0, 10, 2, 2, 2, 2, 2], [2, 2, 2, 8, 0, 0, 11, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - ] + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + ], ); }); - + it('complex shape on edge', () => { compare( [ @@ -108,7 +109,7 @@ describe('layer mesh generator', () => { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ [0, 0, 0, 0, 0, 0, 0, 11, 2, 2, 2, 2], @@ -120,15 +121,15 @@ describe('layer mesh generator', () => { [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - ] + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + ], ); }); - + function compare(input: number[][], expected: number[][]) { const generator = new LayerMeshGenerator(); const layer = new SimpleTileLayer(); - + layer.initSimple(input); generator.transformToBelowMaster(layer); expect(layer.exportLayer()).toEqual(expected); diff --git a/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.ts b/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.ts index 843c87d2..4d6a1ec1 100644 --- a/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.ts +++ b/webapp/src/app/services/3d/layer-generation/layer-mesh-generator.ts @@ -9,51 +9,72 @@ import { SimpleTileLayer } from './simple-tile-layer'; import Tile = Phaser.Tilemaps.Tile; export class LayerMeshGenerator { - - public generateLevel(collLayer: CCMapLayer, aboveLayer: CCMapLayer | undefined, scene: Scene) { + public generateLevel( + collLayer: CCMapLayer, + aboveLayer: CCMapLayer | undefined, + scene: Scene, + ) { const map = Globals.map; if (!map) { throw new Error('map doesnt exist'); } - + const phaserLayer = collLayer.getPhaserLayer(); if (!phaserLayer) { throw new Error('phaser layer of collision layer does not exist'); } - + const simpleTileLayer = new SimpleTileLayer(); simpleTileLayer.initLayer(phaserLayer); - + if (collLayer.details.level < map.masterLevel) { this.transformToBelowMaster(simpleTileLayer, aboveLayer); } - + // extend bottom based on level; // let offset = getLevelOffsetTile(collLayer.details.level + 1) - getLevelOffsetTile(collLayer.details.level); - const levelOffset = getLevelOffsetTile(adjustLevel(collLayer.details.level)); - const levelOffset2 = getLevelOffsetTile(adjustLevel(collLayer.details.level + 1)); + const levelOffset = getLevelOffsetTile( + adjustLevel(collLayer.details.level), + ); + const levelOffset2 = getLevelOffsetTile( + adjustLevel(collLayer.details.level + 1), + ); let offset = levelOffset2 - levelOffset; if (collLayer.details.level < Globals.map.masterLevel) { offset *= -1; } // console.log(`levelOffset: ${levelOffset} - levelOffset2: ${levelOffset2} - offset: ${offset}`); simpleTileLayer.extendBottom(offset); - + const tiles: Tile[] = simpleTileLayer.tiles.flat(); - + const meshes: Mesh[] = []; - + let meshCounter = 0; - - const grid = new NodeGrid(simpleTileLayer.width, simpleTileLayer.height); + + const grid = new NodeGrid( + simpleTileLayer.width, + simpleTileLayer.height, + ); grid.findEdges(tiles); const polygons = grid.findPolygons(); for (const polygon of polygons) { - meshes.push(this.generateMesh('coll layer ' + collLayer.details.level + ' - ' + (++meshCounter), polygon, collLayer, simpleTileLayer, scene)); + meshes.push( + this.generateMesh( + 'coll layer ' + + collLayer.details.level + + ' - ' + + ++meshCounter, + polygon, + collLayer, + simpleTileLayer, + scene, + ), + ); } return meshes; } - + // empty -> block, blue -> empty public transformToBelowMaster(layer: SimpleTileLayer, above?: CCMapLayer) { let tileOffset = 0; @@ -67,7 +88,9 @@ export class LayerMeshGenerator { for (const tile of row) { let aboveIndex = -1; if (above) { - const aboveTile = above.getPhaserLayer()!.getTileAt(tile.x, tile.y + tileOffset); + const aboveTile = above + .getPhaserLayer() + .getTileAt(tile.x, tile.y + tileOffset); if (aboveTile) { aboveIndex = aboveTile.index; } @@ -76,120 +99,146 @@ export class LayerMeshGenerator { if (tile.index === 2 && [1, 4, 5, 6, 7].includes(aboveIndex)) { tile.index = this.reverseTileIndex(aboveIndex); } - } } } - + private reverseTileIndex(index: number) { switch (index) { // empty case 0: return 2; - + // ■ case 1: return 0; - + // ◣ case 4: return 10; - + // ◤ case 5: return 11; - + // ◥ case 6: return 8; - + // ◢ case 7: return 9; - + default: return index; } } - - private generateMesh(name: string, polygon: PolygonDescription, ccLayer: CCMapLayer, simpleTileLayer: SimpleTileLayer, scene: Scene) { - const layer = ccLayer.getPhaserLayer()!; + + private generateMesh( + name: string, + polygon: PolygonDescription, + ccLayer: CCMapLayer, + simpleTileLayer: SimpleTileLayer, + scene: Scene, + ) { + const layer = ccLayer.getPhaserLayer(); const path = polygon.points; - + let maxX = -9999; let minX = 9999; - + let maxY = -9999; let minY = 9999; for (const v of path) { minX = Math.min(minX, v.x); maxX = Math.max(maxX, v.x); - + minY = Math.min(minY, v.y); maxY = Math.max(maxY, v.y); } - + // minY = 0; const topWidth = (maxX - minX) / layer.tilemap.width; const topHeight = (maxY - minY) / layer.tilemap.height; - + const offsetX = minX / layer.tilemap.width; - + const level = ccLayer.details.level; let heightOffset = 0; // TODO: try to handle this case with adjustLevel function if (level + 1 >= Globals.map.masterLevel) { - heightOffset = getLevelOffsetTile(adjustLevel(level + 1)) - getLevelOffsetTile(adjustLevel(level)); + heightOffset = + getLevelOffsetTile(adjustLevel(level + 1)) - + getLevelOffsetTile(adjustLevel(level)); } else { - heightOffset = getLevelOffsetTile(adjustLevel(level + 2)) - getLevelOffsetTile(adjustLevel(level + 1)); + heightOffset = + getLevelOffsetTile(adjustLevel(level + 2)) - + getLevelOffsetTile(adjustLevel(level + 1)); } - - let offsetY = (layer.tilemap.height - maxY + heightOffset) / layer.tilemap.height; - + + let offsetY = + (layer.tilemap.height - maxY + heightOffset) / layer.tilemap.height; + // 1 px offset, no idea where that comes from offsetY -= 1 / layer.tilemap.heightInPixels; - - const pathArr = path.map(t => { + + const pathArr = path.map((t) => { return new Vector3(t.x, 0, -t.y); }); - - const holes = polygon.holes.map(hole => { - return hole.map(t => { + + const holes = polygon.holes.map((hole) => { + return hole.map((t) => { return new Vector3(t.x, 0, -t.y); }); }); - - const top = MeshBuilder.CreatePolygon(name, { - shape: pathArr, - holes: holes, - updatable: false, - faceUV: [ - new Vector4(offsetX, offsetY, offsetX + topWidth, offsetY + topHeight), - new Vector4(0, 0, 0, 0), - new Vector4(0, 0, 0, 0) - ] - }, scene, earcut); - - + + const top = MeshBuilder.CreatePolygon( + name, + { + shape: pathArr, + holes: holes, + updatable: false, + faceUV: [ + new Vector4( + offsetX, + offsetY, + offsetX + topWidth, + offsetY + topHeight, + ), + new Vector4(0, 0, 0, 0), + new Vector4(0, 0, 0, 0), + ], + }, + scene, + earcut, + ); + const sideMeshGenerator = new SideMeshGenerator(); - const mesh = sideMeshGenerator.generate(pathArr, holes, ccLayer, simpleTileLayer); - + const mesh = sideMeshGenerator.generate( + pathArr, + holes, + ccLayer, + simpleTileLayer, + ); + const merge = Mesh.MergeMeshes([top, mesh])!; // const merge = Mesh.MergeMeshes([top])!; - + // merge.position.x = -layer.tilemap.width * 0.5; // merge.position.z = layer.tilemap.height * 0.5; - - const verticalOffset = getLevelOffsetTile((ccLayer.details.level) + 1); + + const verticalOffset = getLevelOffsetTile(ccLayer.details.level + 1); merge.position.y = verticalOffset; - - const horizontalOffset = getLevelOffsetTile(adjustLevel(ccLayer.details.level)); - + + const horizontalOffset = getLevelOffsetTile( + adjustLevel(ccLayer.details.level), + ); + merge.position.z -= horizontalOffset; - + return merge; } - + private getNeighbours(tile: Tile, layer: SimpleTileLayer) { const out: (Tile | null)[] = [ layer.getTileAt(tile.x + 1, tile.y), @@ -197,7 +246,7 @@ export class LayerMeshGenerator { layer.getTileAt(tile.x, tile.y + 1), layer.getTileAt(tile.x, tile.y - 1), ]; - - return out.filter(tile => tile); + + return out.filter((tile) => tile); } } diff --git a/webapp/src/app/services/3d/layer-generation/offset-helper.ts b/webapp/src/app/services/3d/layer-generation/offset-helper.ts index 41e59ae5..59dd341b 100644 --- a/webapp/src/app/services/3d/layer-generation/offset-helper.ts +++ b/webapp/src/app/services/3d/layer-generation/offset-helper.ts @@ -5,9 +5,14 @@ export function getLevelOffset(level: number) { if (level >= map.levels.length) { let maxDiff = 80; if (map.levels.length > 1) { - maxDiff = map.levels[map.levels.length - 1].height - map.levels[map.levels.length - 2].height; + maxDiff = + map.levels[map.levels.length - 1].height - + map.levels[map.levels.length - 2].height; } - return map.levels[map.levels.length - 1].height + maxDiff * (level - map.levels.length + 1); + return ( + map.levels[map.levels.length - 1].height + + maxDiff * (level - map.levels.length + 1) + ); } else if (level < 0) { let minDiff = 80; if (map.levels.length > 1) { @@ -25,7 +30,7 @@ export function getLevelOffsetTile(level: number) { // adjust level dependent on masterLevel. level < masterLevel => level+1 export function adjustLevel(level: number) { if (level < Globals.map.masterLevel) { - return level += 1; + return (level += 1); } return level; } diff --git a/webapp/src/app/services/3d/layer-generation/side-mesh-generator.ts b/webapp/src/app/services/3d/layer-generation/side-mesh-generator.ts index 7c9e1b48..78443455 100644 --- a/webapp/src/app/services/3d/layer-generation/side-mesh-generator.ts +++ b/webapp/src/app/services/3d/layer-generation/side-mesh-generator.ts @@ -18,75 +18,95 @@ interface Quad { } export class SideMeshGenerator { - private width = 0; private height = 0; - + private level = -1; - + private heightOffset = 0; private heightOffset2 = 0; - - generate(path: Vector3[], holes: Vector3[][], ccLayer: CCMapLayer, simpleTileLayer: SimpleTileLayer): Mesh { + + generate( + path: Vector3[], + holes: Vector3[][], + ccLayer: CCMapLayer, + simpleTileLayer: SimpleTileLayer, + ): Mesh { this.heightOffset = getLevelOffsetTile(ccLayer.details.level); - this.heightOffset2 = getLevelOffsetTile(ccLayer.details.level + 1) - getLevelOffsetTile(ccLayer.details.level); - + this.heightOffset2 = + getLevelOffsetTile(ccLayer.details.level + 1) - + getLevelOffsetTile(ccLayer.details.level); + this.width = simpleTileLayer.width; this.height = simpleTileLayer.height - simpleTileLayer.extendedBottom; - + this.level = ccLayer.details.level; - + const quads = this.makeQuads(path, holes, -this.heightOffset2); - + const positions: Point3[] = []; const indices: number[] = []; const uvs = new Map(); const colors: number[] = []; const mathNormals: number[] = []; - + for (const quad of quads) { - this.processQuad(quad, positions, indices, uvs, colors, mathNormals); + this.processQuad( + quad, + positions, + indices, + uvs, + colors, + mathNormals, + ); } - + const simplePositions: number[] = []; for (const p of positions) { simplePositions.push(p.x); simplePositions.push(p.y); simplePositions.push(p.z); } - + const simpleUVs: number[] = []; for (const uv of uvs.values()) { simpleUVs.push(uv.u); simpleUVs.push(uv.v); } - + const customMesh = new Mesh('custom'); - + const normals: number[] = []; VertexData.ComputeNormals(simplePositions, indices, normals); - + const vertexData = new VertexData(); - + vertexData.positions = simplePositions; vertexData.indices = indices; vertexData.normals = mathNormals; // vertexData.colors = colors; vertexData.uvs = simpleUVs; - + vertexData.applyToMesh(customMesh); - + return customMesh; } - - private processQuad(quad: Quad, positions: Point3[], indices: number[], uvs: Map, colors: number[], normals: number[]) { + + private processQuad( + quad: Quad, + positions: Point3[], + indices: number[], + uvs: Map, + colors: number[], + normals: number[], + ) { const vertex: number[] = [ this.findPosIndex(quad.v1, positions), this.findPosIndex(quad.v2, positions), this.findPosIndex(quad.v3, positions), this.findPosIndex(quad.v4, positions), ]; - + indices.push( vertex[0], vertex[1], @@ -94,7 +114,7 @@ export class SideMeshGenerator { vertex[2], vertex[3], vertex[0], - + // backside vertex[2], vertex[1], @@ -103,21 +123,16 @@ export class SideMeshGenerator { vertex[3], vertex[2], ); - + // side/back is broken, remove them for (const v of [quad.v1, quad.v2, quad.v3, quad.v4]) { const hash = this.p3Hash(v); uvs.set(hash, this.getUvFromVertex(v)); } - + // normals are wrong. Doesn't seem to matter much - normals.push( - 0, 1, 0, - 0, 1, 0, - 0, 1, 0, - 0, 1, 0, - ); - + normals.push(0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0); + // vertex colors, currently unused // colors.push(...[ // 1, 0, 0, 1, @@ -128,59 +143,62 @@ export class SideMeshGenerator { // 0, 1, 0, 1 // ]); } - + private getUvFromVertex(vertex: Point3): UV { let u = vertex.x; let v = vertex.y === 0 ? this.heightOffset2 : 0; v += vertex.z; v += this.height; if (this.level < Globals.map.masterLevel) { - v += getLevelOffsetTile(this.level) - getLevelOffsetTile(Globals.map.masterLevel); + v += + getLevelOffsetTile(this.level) - + getLevelOffsetTile(Globals.map.masterLevel); } // v -= this.heightOffset; u /= this.width; v /= this.height; - + // 1 pixel offset v -= 1 / (this.height * Globals.TILE_SIZE); - return {u: u, v: v}; + return { u: u, v: v }; } - + private findPosIndex(pos: Point3, positions: Point3[]) { - let index = positions.findIndex((p) => p.x === pos.x && p.y === pos.y && p.z === pos.z); + let index = positions.findIndex( + (p) => p.x === pos.x && p.y === pos.y && p.z === pos.z, + ); if (index === -1) { positions.push(pos); index = positions.length - 1; } return index; } - + private p3Hash(p: Point3) { return p.x * 100000000 + p.y * 10000 + p.z; } - + private makeQuads(path: Vector3[], holes: Vector3[][], height: number) { - const groups = [path, ...holes]; - + const quads: Quad[] = []; - + for (const vertices of groups) { for (let i = 0; i < vertices.length; i++) { const curr = vertices[i]; const next = vertices[i + 1] || vertices[0]; - + quads.push({ - v1: {x: curr.x, y: curr.y, z: curr.z}, - v2: {x: curr.x, y: curr.y + height, z: curr.z}, - v3: {x: next.x, y: next.y + height, z: next.z}, - v4: {x: next.x, y: next.y, z: next.z}, + v1: { x: curr.x, y: curr.y, z: curr.z }, + v2: { x: curr.x, y: curr.y + height, z: curr.z }, + v3: { x: next.x, y: next.y + height, z: next.z }, + v4: { x: next.x, y: next.y, z: next.z }, }); } } return quads; } - + // backup() { // //Create a custom mesh // const customMesh = new Mesh('custom'); diff --git a/webapp/src/app/services/3d/layer-generation/simple-tile-layer.ts b/webapp/src/app/services/3d/layer-generation/simple-tile-layer.ts index b822bec0..439667ca 100644 --- a/webapp/src/app/services/3d/layer-generation/simple-tile-layer.ts +++ b/webapp/src/app/services/3d/layer-generation/simple-tile-layer.ts @@ -2,28 +2,27 @@ import Tile = Phaser.Tilemaps.Tile; import DynamicTilemapLayer = Phaser.Tilemaps.TilemapLayer; export class SimpleTileLayer { - private _data: Tile[][] = []; - + public get tiles(): Phaser.Tilemaps.Tile[][] { return this._data; } - + private _width = 0; public get width(): number { return this._width; } - + private _height = 0; public get height(): number { return this._height; } - + private _extendedBottom = 0; get extendedBottom(): number { return this._extendedBottom; } - + public init(width: number, height: number) { this._width = width; this._height = height; @@ -37,7 +36,7 @@ export class SimpleTileLayer { } } } - + public initSimple(tiles: number[][]) { const width = tiles[0].length; const height = tiles.length; @@ -53,38 +52,43 @@ export class SimpleTileLayer { } } } - + public initLayer(layer: DynamicTilemapLayer) { this.init(layer.layer.width, layer.layer.height); - + // TODO: set directly, skip isInLayerBounds check for (const tile of layer.getTilesWithin()) { this.setTileAt(tile.index, tile.x, tile.y); } } - + extendBottom(offset: number) { if (offset < 0) { // this.tiles.length = this.tiles.length + offset; } else { for (let i = 0; i < offset; i++) { const lastRow = this.tiles[this.tiles.length - 1]; - this.tiles.push(lastRow.map(tile => new Tile( - tile.layer, - tile.index, - tile.x, - tile.y + 1, - tile.width, - tile.height, - tile.baseWidth, - tile.baseHeight) - )); + this.tiles.push( + lastRow.map( + (tile) => + new Tile( + tile.layer, + tile.index, + tile.x, + tile.y + 1, + tile.width, + tile.height, + tile.baseWidth, + tile.baseHeight, + ), + ), + ); } } this._height = this.tiles.length; this._extendedBottom += offset; } - + public getTileAt(tileX: number, tileY: number) { if (!this.isInLayerBounds(tileX, tileY)) { return null; @@ -92,17 +96,22 @@ export class SimpleTileLayer { const tile = this._data[tileY][tileX]; return tile || null; } - + public setTileAt(index: number, x: number, y: number) { if (this.isInLayerBounds(x, y)) { this._data[y][x].index = index; } } - + private isInLayerBounds(tileX: number, tileY: number) { - return (tileX >= 0 && tileX < this._width && tileY >= 0 && tileY < this._height); + return ( + tileX >= 0 && + tileX < this._width && + tileY >= 0 && + tileY < this._height + ); } - + public debug() { let all = ''; for (let j = 0; j < this._height; j++) { @@ -114,13 +123,13 @@ export class SimpleTileLayer { } console.log(all); } - + public exportTestCases() { - return `[\n${this._data.map(inner => '\t[' + inner.map(t => t.index).join(', ') + ']').join(',\n')} + return `[\n${this._data.map((inner) => '\t[' + inner.map((t) => t.index).join(', ') + ']').join(',\n')} ]`; } - + public exportLayer() { - return this.tiles.map(row => row.map(tile => tile.index)); + return this.tiles.map((row) => row.map((tile) => tile.index)); } } diff --git a/webapp/src/app/services/3d/layer-generation/texture-generator.ts b/webapp/src/app/services/3d/layer-generation/texture-generator.ts index 97bbf411..ce5a66f8 100644 --- a/webapp/src/app/services/3d/layer-generation/texture-generator.ts +++ b/webapp/src/app/services/3d/layer-generation/texture-generator.ts @@ -7,30 +7,28 @@ export class TextureGenerator { private layers: CCMapLayer[] = []; private ctx!: CanvasRenderingContext2D; private startLayer = 0; - + init() { const map = Globals.map; - + const canvas = document.createElement('canvas'); canvas.width = map.mapWidth * Globals.TILE_SIZE; canvas.height = map.mapHeight * Globals.TILE_SIZE; - + const ctx = canvas.getContext('2d'); if (!ctx) { throw new Error('Could not get context of buffer canvas'); } ctx.clearRect(0, 0, canvas.width, canvas.height); this.ctx = ctx; - + this.layers = map.layers - .filter(l => l.details.type.toLowerCase() === 'background') + .filter((l) => l.details.type.toLowerCase() === 'background') .sort((a, b) => a.details.level - b.details.level); - } - - destroy() { - } - + + destroy() {} + /** * Assumes that on every call layer is only increased. * @param level @@ -40,26 +38,42 @@ export class TextureGenerator { for (const layer of this.layers) { layer.visible = layer.details.level <= level; } - - const layerMaterial = new StandardMaterial('layerMaterial' + level, scene); + + const layerMaterial = new StandardMaterial( + 'layerMaterial' + level, + scene, + ); this.snapshot(level); - - const texture = new Texture('data:level' + level, scene, undefined, undefined, Texture.NEAREST_SAMPLINGMODE, undefined, undefined, this.ctx.canvas.toDataURL()); + + const texture = new Texture( + 'data:level' + level, + scene, + undefined, + undefined, + Texture.NEAREST_SAMPLINGMODE, + undefined, + undefined, + this.ctx.canvas.toDataURL(), + ); texture.wrapU = Texture.CLAMP_ADDRESSMODE; texture.wrapV = Texture.CLAMP_ADDRESSMODE; - + layerMaterial.diffuseTexture = texture; - + return layerMaterial; } - + private snapshot(level: number) { const ctx = this.ctx; let i = this.startLayer; - for (; i < this.layers.length && this.layers[i].details.level <= level; i++) { + for ( + ; + i < this.layers.length && this.layers[i].details.level <= level; + i++ + ) { const layer = this.layers[i]; - const phaserLayer = layer.getPhaserLayer()!; - + const phaserLayer = layer.getPhaserLayer(); + const tiles = phaserLayer.getTilesWithin(); for (const tile of tiles) { const index = tile.index; @@ -73,11 +87,21 @@ export class TextureGenerator { if (!tilesetImage) { throw new Error('tilesetImage is not defined'); } - const uv = tile.tileset.getTileTextureCoordinates(index) as Point; - - ctx.drawImage(tilesetImage, - uv.x, uv.y, Globals.TILE_SIZE, Globals.TILE_SIZE, - x * Globals.TILE_SIZE, y * Globals.TILE_SIZE, Globals.TILE_SIZE, Globals.TILE_SIZE); + const uv = tile.tileset.getTileTextureCoordinates( + index, + ) as Point; + + ctx.drawImage( + tilesetImage, + uv.x, + uv.y, + Globals.TILE_SIZE, + Globals.TILE_SIZE, + x * Globals.TILE_SIZE, + y * Globals.TILE_SIZE, + Globals.TILE_SIZE, + Globals.TILE_SIZE, + ); } } this.startLayer = i; diff --git a/webapp/src/app/services/3d/ui/wireframe.ts b/webapp/src/app/services/3d/ui/wireframe.ts index c21d3169..a7682af0 100644 --- a/webapp/src/app/services/3d/ui/wireframe.ts +++ b/webapp/src/app/services/3d/ui/wireframe.ts @@ -6,7 +6,7 @@ export function addWireframeButton(toggle: ToggleMesh, meshes: Mesh[]) { for (const mesh of meshes) { materials.add(mesh.material!); } - + toggle.addButton('wireframe', () => { for (const mat of materials) { mat.wireframe = !mat.wireframe; diff --git a/webapp/src/app/services/add-entity-menu.service.ts b/webapp/src/app/services/add-entity-menu.service.ts index 4da63465..4311b02d 100644 --- a/webapp/src/app/services/add-entity-menu.service.ts +++ b/webapp/src/app/services/add-entity-menu.service.ts @@ -1,5 +1,5 @@ import { Overlay } from '@angular/cdk/overlay'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ListSearchOverlayComponent } from '../components/dialogs/list-search-overlay/list-search-overlay.component'; import { OverlayRefControl } from '../components/dialogs/overlay/overlay-ref-control'; @@ -12,76 +12,80 @@ import { JsonLoaderService } from './json-loader.service'; import { EntitiesJson } from './phaser/entities/registry/default-entity'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AddEntityMenuService { - + private events = inject(GlobalEventsService); + private overlayService = inject(OverlayService); + private overlay = inject(Overlay); + private entityRegistry = inject(EntityRegistryService); + private jsonLoader = inject(JsonLoaderService); + private ref?: OverlayRefControl; - private worldPos: Point = {x: 0, y: 0}; - private mousePos: Point = {x: 0, y: 0}; - - pos: Point = {x: 0, y: 0}; + private worldPos: Point = { x: 0, y: 0 }; + private mousePos: Point = { x: 0, y: 0 }; + + pos: Point = { x: 0, y: 0 }; keys: string[] = []; - - constructor( - private events: GlobalEventsService, - private overlayService: OverlayService, - private overlay: Overlay, - private entityRegistry: EntityRegistryService, - private jsonLoader: JsonLoaderService, - ) { - } - + public async init() { - const entities = await this.jsonLoader.loadJsonMerged('entities.json'); + const entities = + await this.jsonLoader.loadJsonMerged('entities.json'); const registry = Object.keys(this.entityRegistry.getAll()); const entityNames = Object.keys(entities); - + const eventSet = new Set([...registry, ...entityNames]); - + this.keys = Array.from(eventSet); this.keys.sort(); - - document.onmousemove = e => { + + document.onmousemove = (e) => { this.mousePos.x = e.pageX; this.mousePos.y = e.pageY; }; - - this.events.showAddEntityMenu.subscribe(pos => this.showAddEntityMenu(pos)); + + this.events.showAddEntityMenu.subscribe((pos) => + this.showAddEntityMenu(pos), + ); } - + showAddEntityMenu(pos: Point) { Vec2.assign(this.pos, this.mousePos); this.worldPos = pos; - + if (this.ref && this.ref.isOpen()) { return; } const obj = this.overlayService.open(ListSearchOverlayComponent, { - positionStrategy: this.overlay.position().global() + positionStrategy: this.overlay + .position() + .global() .left(this.mousePos.x + 'px') .top(this.mousePos.y + 'px'), height: '50vh', backdropClickClose: true, hasBackdrop: true, - disablePhaserInput: true + disablePhaserInput: true, }); this.ref = obj.ref; - + obj.instance.list = this.keys; obj.instance.animation = 'scale'; - obj.instance.selected.subscribe((v: string) => { - this.generateEntity(v); - this.close(); - }, () => this.close()); + obj.instance.selected.subscribe( + (v: string) => { + this.generateEntity(v); + this.close(); + }, + () => this.close(), + ); } - + private close() { if (this.ref) { this.ref.close(); } } - + generateEntity(key: string) { const entity: MapEntity = { x: this.worldPos.x, @@ -89,8 +93,8 @@ export class AddEntityMenuService { type: key, level: 0, settings: { - size: {x: 16, y: 16} - } + size: { x: 16, y: 16 }, + }, }; this.events.generateNewEntity.next(entity); } diff --git a/webapp/src/app/services/autotile/autotile.constants.ts b/webapp/src/app/services/autotile/autotile.constants.ts index 30248d57..927d362b 100644 --- a/webapp/src/app/services/autotile/autotile.constants.ts +++ b/webapp/src/app/services/autotile/autotile.constants.ts @@ -60,158 +60,153 @@ const empty: FillType = { XXOO: [], XOOX: [], OXOO: [], - XOOO: [] + XOOO: [], }; - const fillType8x2: FillType = { - XXXX: [{x: 0, y: 0}], - OXXX: [{x: 1, y: 0}], - XOXX: [{x: 2, y: 0}], - OXOX: [{x: 3, y: 0}], - OOXX: [{x: 4, y: 0}], - OXXO: [{x: 5, y: 0}], - OOXO: [{x: 6, y: 0}], - OOOX: [{x: 7, y: 0}], - OOOO: [{x: 0, y: 1}], - XXXO: [{x: 1, y: 1}], - XXOX: [{x: 2, y: 1}], - XOXO: [{x: 3, y: 1}], - XXOO: [{x: 4, y: 1}], - XOOX: [{x: 5, y: 1}], - OXOO: [{x: 6, y: 1}], - XOOO: [{x: 7, y: 1}], + XXXX: [{ x: 0, y: 0 }], + OXXX: [{ x: 1, y: 0 }], + XOXX: [{ x: 2, y: 0 }], + OXOX: [{ x: 3, y: 0 }], + OOXX: [{ x: 4, y: 0 }], + OXXO: [{ x: 5, y: 0 }], + OOXO: [{ x: 6, y: 0 }], + OOOX: [{ x: 7, y: 0 }], + OOOO: [{ x: 0, y: 1 }], + XXXO: [{ x: 1, y: 1 }], + XXOX: [{ x: 2, y: 1 }], + XOXO: [{ x: 3, y: 1 }], + XXOO: [{ x: 4, y: 1 }], + XOOX: [{ x: 5, y: 1 }], + OXOO: [{ x: 6, y: 1 }], + XOOO: [{ x: 7, y: 1 }], }; const fillType10x2 = Helper.copy(fillType8x2); -fillType10x2.OOXX.push({x: 8, y: 0}); -fillType10x2.OXXO.push({x: 9, y: 0}); -fillType10x2.XXOO.push({x: 8, y: 1}); -fillType10x2.XOOX.push({x: 9, y: 1}); +fillType10x2.OOXX.push({ x: 8, y: 0 }); +fillType10x2.OXXO.push({ x: 9, y: 0 }); +fillType10x2.XXOO.push({ x: 8, y: 1 }); +fillType10x2.XOOX.push({ x: 9, y: 1 }); const fillType12x2 = Helper.copy(fillType10x2); -fillType12x2.OOXO.push({x: 10, y: 0}); -fillType12x2.OOOX.push({x: 11, y: 0}); -fillType12x2.OXOO.push({x: 10, y: 1}); -fillType12x2.XOOO.push({x: 11, y: 1}); +fillType12x2.OOXO.push({ x: 10, y: 0 }); +fillType12x2.OOOX.push({ x: 11, y: 0 }); +fillType12x2.OXOO.push({ x: 10, y: 1 }); +fillType12x2.XOOO.push({ x: 11, y: 1 }); const fillType14x2 = Helper.copy(fillType12x2); -fillType14x2.OOXX.push({x: 12, y: 0}); -fillType14x2.OXXO.push({x: 13, y: 0}); -fillType14x2.XXOO.push({x: 12, y: 1}); -fillType14x2.XOOX.push({x: 13, y: 1}); +fillType14x2.OOXX.push({ x: 12, y: 0 }); +fillType14x2.OXXO.push({ x: 13, y: 0 }); +fillType14x2.XXOO.push({ x: 12, y: 1 }); +fillType14x2.XOOX.push({ x: 13, y: 1 }); const fillType4x4: FillType = { // order is important for same offset. Last one is used for reverse mapping - OOOO: [{x: 2, y: 2}], - XXXX: [{x: 2, y: 2}], - - OXXX: [{x: 2, y: 1}], - XOXX: [{x: 3, y: 2}], - XXOX: [{x: 2, y: 3}], - XXXO: [{x: 1, y: 2}], - - XXOO: [{x: 1, y: 3}], - OXXO: [{x: 1, y: 1}], - OOXX: [{x: 3, y: 1}], - XOOX: [{x: 3, y: 3}], - - OXOX: [{x: 1, y: 0}], - XOXO: [{x: 0, y: 2}], - - XOOO: [{x: 0, y: 3}], - OXOO: [{x: 0, y: 0}], - OOXO: [{x: 0, y: 1}], - OOOX: [{x: 2, y: 0}], + OOOO: [{ x: 2, y: 2 }], + XXXX: [{ x: 2, y: 2 }], + + OXXX: [{ x: 2, y: 1 }], + XOXX: [{ x: 3, y: 2 }], + XXOX: [{ x: 2, y: 3 }], + XXXO: [{ x: 1, y: 2 }], + + XXOO: [{ x: 1, y: 3 }], + OXXO: [{ x: 1, y: 1 }], + OOXX: [{ x: 3, y: 1 }], + XOOX: [{ x: 3, y: 3 }], + + OXOX: [{ x: 1, y: 0 }], + XOXO: [{ x: 0, y: 2 }], + + XOOO: [{ x: 0, y: 3 }], + OXOO: [{ x: 0, y: 0 }], + OOXO: [{ x: 0, y: 1 }], + OOOX: [{ x: 2, y: 0 }], }; - -export const FILL_TYPE: { - [key in AutotileType]: FillType -} = { +export const FILL_TYPE: Record = { '4x4': fillType4x4, '8x2': fillType8x2, '10x2': fillType10x2, '12x2': fillType12x2, - '14x2': fillType14x2 + '14x2': fillType14x2, }; - export const FILL_TYPE_CLIFF_BORDER = Helper.copy(empty); FILL_TYPE_CLIFF_BORDER.XXXX = [ - {x: 1, y: 0}, - {x: 2, y: 0}, - {x: 3, y: 0}, - {x: 4, y: 0}, - - {x: 0, y: 1}, - {x: 1, y: 1}, - {x: 2, y: 1}, - {x: 3, y: 1}, - {x: 4, y: 1}, - {x: 5, y: 1}, - - {x: 0, y: 2}, - {x: 1, y: 2}, - {x: 2, y: 2}, - {x: 3, y: 2}, - {x: 4, y: 2}, - {x: 5, y: 2}, - - {x: 0, y: 3}, - {x: 1, y: 3}, - {x: 2, y: 3}, - {x: 3, y: 3}, - {x: 4, y: 3}, - {x: 5, y: 3}, - - {x: 1, y: 4}, - {x: 2, y: 4}, - {x: 3, y: 4}, - {x: 4, y: 4} + { x: 1, y: 0 }, + { x: 2, y: 0 }, + { x: 3, y: 0 }, + { x: 4, y: 0 }, + + { x: 0, y: 1 }, + { x: 1, y: 1 }, + { x: 2, y: 1 }, + { x: 3, y: 1 }, + { x: 4, y: 1 }, + { x: 5, y: 1 }, + + { x: 0, y: 2 }, + { x: 1, y: 2 }, + { x: 2, y: 2 }, + { x: 3, y: 2 }, + { x: 4, y: 2 }, + { x: 5, y: 2 }, + + { x: 0, y: 3 }, + { x: 1, y: 3 }, + { x: 2, y: 3 }, + { x: 3, y: 3 }, + { x: 4, y: 3 }, + { x: 5, y: 3 }, + + { x: 1, y: 4 }, + { x: 2, y: 4 }, + { x: 3, y: 4 }, + { x: 4, y: 4 }, ]; FILL_TYPE_CLIFF_BORDER.OOXX = [ - {x: 1, y: 5}, - {x: 4, y: 5} + { x: 1, y: 5 }, + { x: 4, y: 5 }, ]; FILL_TYPE_CLIFF_BORDER.OXXO = [ - {x: 2, y: 5}, - {x: 2, y: 6} + { x: 2, y: 5 }, + { x: 2, y: 6 }, ]; FILL_TYPE_CLIFF_BORDER.OOOO = [ - {x: 0, y: 0}, - {x: 5, y: 0}, - {x: 0, y: 4}, - {x: 5, y: 4}, - {x: 0, y: 5}, - {x: 5, y: 5}, - {x: 0, y: 6}, - {x: 5, y: 6} + { x: 0, y: 0 }, + { x: 5, y: 0 }, + { x: 0, y: 4 }, + { x: 5, y: 4 }, + { x: 0, y: 5 }, + { x: 5, y: 5 }, + { x: 0, y: 6 }, + { x: 5, y: 6 }, ]; FILL_TYPE_CLIFF_BORDER.XXOO = [ - {x: 1, y: 6}, - {x: 4, y: 6} + { x: 1, y: 6 }, + { x: 4, y: 6 }, ]; FILL_TYPE_CLIFF_BORDER.XOOX = [ - {x: 3, y: 5}, - {x: 3, y: 6} + { x: 3, y: 5 }, + { x: 3, y: 6 }, ]; export const FILL_TYPE_CLIFF: FillType = Helper.copy(empty); FILL_TYPE_CLIFF.XXXX = [ - {x: 0, y: 3}, - {x: 1, y: 4}, - {x: 4, y: 4}, - {x: 5, y: 3}, + { x: 0, y: 3 }, + { x: 1, y: 4 }, + { x: 4, y: 4 }, + { x: 5, y: 3 }, ]; export const FILL_TYPE_CLIFF_ALT: FillType = Helper.copy(empty); FILL_TYPE_CLIFF_ALT.XXXX = [ - {x: 0, y: 0}, - {x: 1, y: 0}, - {x: 4, y: 0}, - {x: 5, y: 0}, + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 4, y: 0 }, + { x: 5, y: 0 }, ]; diff --git a/webapp/src/app/services/autotile/autotile.service.ts b/webapp/src/app/services/autotile/autotile.service.ts index 7b410c74..08b9eecf 100644 --- a/webapp/src/app/services/autotile/autotile.service.ts +++ b/webapp/src/app/services/autotile/autotile.service.ts @@ -1,6 +1,10 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Point } from '../../models/cross-code-map'; -import { CHECK_DIR, CHECK_ITERATE, CheckDir } from '../height-map/heightmap.constants'; +import { + CHECK_DIR, + CHECK_ITERATE, + CheckDir, +} from '../height-map/heightmap.constants'; import { CCMapLayer } from '../phaser/tilemap/cc-map-layer'; import { AutotileConfig, FillType } from './autotile.constants'; import { GfxMapper } from './gfx-mapper'; @@ -18,22 +22,21 @@ interface TileData { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AutotileService { - private gfxMapper: GfxMapper; - - constructor( - phaserEvents: PhaserEventsService, - mapLoader: MapLoaderService, - events: GlobalEventsService, - jsonLoader: JsonLoaderService, - ) { + + constructor() { + const phaserEvents = inject(PhaserEventsService); + const mapLoader = inject(MapLoaderService); + const events = inject(GlobalEventsService); + const jsonLoader = inject(JsonLoaderService); + this.gfxMapper = new GfxMapper(jsonLoader); combineLatest([ phaserEvents.changeSelectedTiles.asObservable(), - mapLoader.selectedLayer.asObservable() + mapLoader.selectedLayer.asObservable(), ]).subscribe(([tiles, layer]) => { if (!layer) { events.isAutotile.next(false); @@ -41,7 +44,11 @@ export class AutotileService { } let autotile = false; for (const tile of tiles) { - const config = this.gfxMapper.getAutotileConfig(layer.details.tilesetName, tile.id, false); + const config = this.gfxMapper.getAutotileConfig( + layer.details.tilesetName, + tile.id, + false, + ); if (config) { autotile = true; break; @@ -50,52 +57,65 @@ export class AutotileService { events.isAutotile.next(autotile); }); } - - public drawTile(layer: CCMapLayer, x: number, y: number, tile: number, checkCliff = true) { - const config = this.gfxMapper.getAutotileConfig(layer.details.tilesetName, tile, checkCliff); + + public drawTile( + layer: CCMapLayer, + x: number, + y: number, + tile: number, + checkCliff = true, + ) { + const config = this.gfxMapper.getAutotileConfig( + layer.details.tilesetName, + tile, + checkCliff, + ); if (!config) { return; } const tileData: TileData = { - pos: {x: x, y: y}, + pos: { x: x, y: y }, fill: 'XXXX', - update: true + update: true, }; if (!config.isCliff) { this.drawSingleTile(layer, config.config, tileData); } - const tilesToUpdate = CHECK_ITERATE - .map(v => this.getOther(config.config, layer, tileData, CHECK_DIR[v])) - .filter(tile => tile.update); - + const tilesToUpdate = CHECK_ITERATE.map((v) => + this.getOther(config.config, layer, tileData, CHECK_DIR[v]), + ).filter((tile) => tile.update); + for (const tileData of tilesToUpdate) { tileData.fill = 'XXXX'; this.drawSingleTile(layer, config.config, tileData); } - + if (!config.isCliff) { tilesToUpdate.push(tileData); } - + for (const tileData of tilesToUpdate) { this.updateTile(config.config, layer, tileData); } - } - - private updateTile(config: AutotileConfig, layer: CCMapLayer, tile: TileData) { + + private updateTile( + config: AutotileConfig, + layer: CCMapLayer, + tile: TileData, + ) { const n = this.getOther(config, layer, tile, CHECK_DIR.NORTH); const e = this.getOther(config, layer, tile, CHECK_DIR.EAST); const s = this.getOther(config, layer, tile, CHECK_DIR.SOUTH); const w = this.getOther(config, layer, tile, CHECK_DIR.WEST); - + const nw = this.getOther(config, layer, tile, CHECK_DIR.NW); const ne = this.getOther(config, layer, tile, CHECK_DIR.NE); const se = this.getOther(config, layer, tile, CHECK_DIR.SE); const sw = this.getOther(config, layer, tile, CHECK_DIR.SW); - + let fillType = ''; - + // 4x4 needs special handling, corners are ignored if (config.type === '4x4') { if (this.checkAt(n, 2)) { @@ -119,64 +139,101 @@ export class AutotileService { fillType += 'O'; } } else { - if (this.checkAt(w, 1) && this.checkAt(nw, 2) && this.checkAt(n, 3)) { + if ( + this.checkAt(w, 1) && + this.checkAt(nw, 2) && + this.checkAt(n, 3) + ) { fillType += 'X'; } else { fillType += 'O'; } - - if (this.checkAt(n, 2) && this.checkAt(ne, 3) && this.checkAt(e, 0)) { + + if ( + this.checkAt(n, 2) && + this.checkAt(ne, 3) && + this.checkAt(e, 0) + ) { fillType += 'X'; } else { fillType += 'O'; } - - if (this.checkAt(e, 3) && this.checkAt(se, 0) && this.checkAt(s, 1)) { + + if ( + this.checkAt(e, 3) && + this.checkAt(se, 0) && + this.checkAt(s, 1) + ) { fillType += 'X'; } else { fillType += 'O'; } - - if (this.checkAt(s, 0) && this.checkAt(sw, 1) && this.checkAt(w, 2)) { + + if ( + this.checkAt(s, 0) && + this.checkAt(sw, 1) && + this.checkAt(w, 2) + ) { fillType += 'X'; } else { fillType += 'O'; } } - + tile.fill = fillType as keyof FillType; this.drawSingleTile(layer, config, tile); } - + private checkAt(tile: TileData, index: number) { return tile.fill && tile.fill.charAt(index) === 'X'; } - - private drawSingleTile(layer: CCMapLayer, config: AutotileConfig, tile: TileData) { + + private drawSingleTile( + layer: CCMapLayer, + config: AutotileConfig, + tile: TileData, + ) { if (!tile.fill) { return; } const index = this.gfxMapper.getGfx(tile.fill, config); - customPutTileAt(index, tile.pos.x, tile.pos.y, layer.getPhaserLayer().layer); + customPutTileAt( + index, + tile.pos.x, + tile.pos.y, + layer.getPhaserLayer().layer, + ); } - - private getOther(config: AutotileConfig, layer: CCMapLayer, tile: TileData, dir: CheckDir): TileData { + + private getOther( + config: AutotileConfig, + layer: CCMapLayer, + tile: TileData, + dir: CheckDir, + ): TileData { const newPos = { x: tile.pos.x + dir.dx, - y: tile.pos.y + dir.dy + y: tile.pos.y + dir.dy, }; - - const out: TileData = { + + const out: TileData = { pos: newPos, - fill: 'OOOO' - }; - - if (newPos.x < 0 || newPos.y < 0 || newPos.x >= layer.details.width || newPos.y >= layer.details.height) { + fill: 'OOOO', + } as any; + + if ( + newPos.x < 0 || + newPos.y < 0 || + newPos.x >= layer.details.width || + newPos.y >= layer.details.height + ) { out.fill = 'XXXX'; out.update = false; return out; } - const index = layer.getPhaserLayer()!.getTileAt(newPos.x, newPos.y, true).index; + const index = layer + .getPhaserLayer() + .getTileAt(newPos.x, newPos.y, true).index; if (index === 0) { if (config.mergeWithEmpty) { out.fill = 'XXXX'; @@ -186,16 +243,17 @@ export class AutotileService { out.update = false; return out; } - + out.update = true; const fill = this.gfxMapper.getFillType(config, index); if (fill) { out.fill = fill; } else { out.update = false; - out.fill = this.gfxMapper.getFillType(config, index, true) || out.fill; + out.fill = + this.gfxMapper.getFillType(config, index, true) || out.fill; } - + return out; } } diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index ca30a971..81e1c88d 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -1,9 +1,17 @@ import { Point } from '../../models/cross-code-map'; import { ChipsetConfig } from '../height-map/gfx-mapper/gfx-mapper.constants'; +import { JsonLoaderService } from '../json-loader.service'; import { Helper } from '../phaser/helper'; import { Vec2 } from '../phaser/vec2'; -import { AutotileConfig, AutotileType, FILL_TYPE, FILL_TYPE_CLIFF, FILL_TYPE_CLIFF_ALT, FILL_TYPE_CLIFF_BORDER, FillType } from './autotile.constants'; -import { JsonLoaderService } from '../json-loader.service'; +import { + AutotileConfig, + AutotileType, + FILL_TYPE, + FILL_TYPE_CLIFF, + FILL_TYPE_CLIFF_ALT, + FILL_TYPE_CLIFF_BORDER, + FillType, +} from './autotile.constants'; interface JsonType { map: string; @@ -17,42 +25,39 @@ interface JsonType { } export class GfxMapper { - - private AUTOTILE_CONFIG: { - [key: string]: AutotileConfig[] | undefined; - } = {}; - - + private AUTOTILE_CONFIG: Record = {}; + private TILESET_CONFIG: Record = {}; - - private mapping: { [key in AutotileType]: Map } = {}; + + private mapping: Record> = + {} as any; private cliffBorderMapping = new Map(); private cliffMapping = new Map(); private cliffAltMapping = new Map(); - - constructor( - private jsonLoader: JsonLoaderService - ) { - this.init(); + + constructor(private jsonLoader: JsonLoaderService) { + void this.init(); } - + private async init() { - this.TILESET_CONFIG = await this.jsonLoader.loadJsonMerged('tilesets.json'); + this.TILESET_CONFIG = + await this.jsonLoader.loadJsonMerged('tilesets.json'); await this.generateAutotileConfig(); - + for (const type of Helper.typedKeys(FILL_TYPE)) { const map = new Map(); this.mapping[type] = map; this.generateMapping(map, FILL_TYPE[type]); } - + this.generateMapping(this.cliffBorderMapping, FILL_TYPE_CLIFF_BORDER); this.generateMapping(this.cliffMapping, FILL_TYPE_CLIFF); this.generateMapping(this.cliffAltMapping, FILL_TYPE_CLIFF_ALT); } - + private async generateAutotileConfig() { - const jsons = await this.jsonLoader.loadJson('autotiles.json'); + const jsons = + await this.jsonLoader.loadJson('autotiles.json'); const autotilesJson = jsons.flat(); for (const config of autotilesJson) { let arr: AutotileConfig[] = []; @@ -61,10 +66,11 @@ export class GfxMapper { arr = prevArr; } this.AUTOTILE_CONFIG[config.map] = arr; - + for (const autotile of config.autotiles) { - const generatedType: AutotileType = `${autotile.size.x}x${autotile.size.y}` as AutotileType; - + const generatedType: AutotileType = + `${autotile.size.x}x${autotile.size.y}` as AutotileType; + const tileset = this.TILESET_CONFIG[config.map]; const terrains = tileset?.terrains ?? []; if (tileset) { @@ -73,35 +79,47 @@ export class GfxMapper { let cliff = autotile.cliff; if (cliff === undefined) { for (const terrain of terrains) { - if (terrain.ground.x === autotile.base.x && terrain.ground.y === autotile.base.y) { + if ( + terrain.ground.x === autotile.base.x && + terrain.ground.y === autotile.base.y + ) { cliff = terrain.cliff; break; } } } - + const newConfig: AutotileConfig = { key: config.map, tileCountX: config.tileCountX, type: generatedType, mergeWithEmpty: !!autotile.mergeWithEmpty, base: autotile.base, - cliff: cliff ? cliff : undefined + cliff: cliff ? cliff : undefined, }; - + arr.push(newConfig); } } } - - private generateMapping(map: Map, fillType: FillType) { + + private generateMapping( + map: Map, + fillType: FillType, + ) { for (const key of Object.keys(fillType) as (keyof FillType)[]) { const offsets = fillType[key]; - offsets.forEach(offset => map.set(this.getMappingKey(offset), key)); + offsets.forEach((offset) => + map.set(this.getMappingKey(offset), key), + ); } } - - getAutotileConfig(tilesetName: string, tile: number, checkCliffs = false): { config: AutotileConfig, isCliff: boolean } | null { + + getAutotileConfig( + tilesetName: string, + tile: number, + checkCliffs = false, + ): { config: AutotileConfig; isCliff: boolean } | null { const configs = this.AUTOTILE_CONFIG[tilesetName]; if (!configs) { return null; @@ -109,19 +127,26 @@ export class GfxMapper { for (const config of configs) { const pos = Helper.indexToPoint(tile, config.tileCountX); if (this.getFill(pos, config.base, this.mapping[config.type])) { - return {config: config, isCliff: false}; + return { config: config, isCliff: false }; } - if (checkCliffs && this.getFill(pos, config.cliff, this.cliffBorderMapping)) { - return {config: config, isCliff: true}; + if ( + checkCliffs && + this.getFill(pos, config.cliff, this.cliffBorderMapping) + ) { + return { config: config, isCliff: true }; } } - + return null; } - - getFillType(config: AutotileConfig, tile: number, cliff = false): keyof FillType | undefined { + + getFillType( + config: AutotileConfig, + tile: number, + cliff = false, + ): keyof FillType | undefined { const pos = Helper.indexToPoint(tile, config.tileCountX); - + if (!cliff) { return this.getFill(pos, config.base, this.mapping[config.type]); } @@ -130,40 +155,49 @@ export class GfxMapper { if (tilesetConfig && tilesetConfig.base) { tilesetBase = tilesetConfig.base; } - + const fill = this.getFill(pos, config.cliff, this.cliffBorderMapping); - + if (fill) { return fill; } else if (tilesetBase) { - return this.getFill(pos, tilesetBase.cliff, this.cliffMapping) || this.getFill(pos, tilesetBase.cliffAlt, this.cliffAltMapping); + return ( + this.getFill(pos, tilesetBase.cliff, this.cliffMapping) || + this.getFill(pos, tilesetBase.cliffAlt, this.cliffAltMapping) + ); } - - + return undefined; } - - private getFill(pos: Point, offset: Point | undefined, mapping: Map) { + + private getFill( + pos: Point, + offset: Point | undefined, + mapping: Map, + ) { if (!offset) { return undefined; } const p = Vec2.sub(pos, offset, true); return mapping.get(this.getMappingKey(p)); } - + getGfx(fillType: keyof FillType, config: AutotileConfig) { const offsets = FILL_TYPE[config.type][fillType]; const offset = offsets[Math.floor(Math.random() * offsets.length)]; - - return this.getTile(config.base.x + offset.x, config.base.y + offset.y, config.tileCountX); + + return this.getTile( + config.base.x + offset.x, + config.base.y + offset.y, + config.tileCountX, + ); } - + private getTile(x: number, y: number, tileCountX: number) { return y * tileCountX + x + 1; } - + private getMappingKey(p: Point) { return p.y * 1000 + p.x; } - } diff --git a/webapp/src/app/services/browser.service.ts b/webapp/src/app/services/browser.service.ts index fc838810..7b4a45af 100644 --- a/webapp/src/app/services/browser.service.ts +++ b/webapp/src/app/services/browser.service.ts @@ -3,11 +3,11 @@ import { Globals } from './globals'; import { SharedService } from './shared-service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class BrowserService implements SharedService { private static readonly modName = 'selectedMod'; - + constructor() { if (Globals.isElectron) { return; @@ -18,7 +18,7 @@ export class BrowserService implements SharedService { if (Globals.isElectron) { return; } - + const selectedMod = localStorage.getItem(this.modName) || ''; try { @@ -27,18 +27,18 @@ export class BrowserService implements SharedService { console.error('Could not select mod: ', ex); //Don't crash when backend is not available } } - + private static async updateMod(mod: string): Promise { //We cant use HttpClient because this is called really early await fetch(Globals.URL + 'api/select', { method: 'POST', headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' + Accept: 'application/json', + 'Content-Type': 'application/json', }, - body: JSON.stringify({mod}) + body: JSON.stringify({ mod }), }); - } + } public async saveModSelect(mod: string): Promise { localStorage.setItem(BrowserService.modName, mod ?? ''); @@ -48,7 +48,7 @@ export class BrowserService implements SharedService { public getSelectedMod(): string { return localStorage.getItem(BrowserService.modName) || ''; } - + public relaunch(): void { location.reload(); } diff --git a/webapp/src/app/services/color.service.ts b/webapp/src/app/services/color.service.ts index 95a5a01c..fd969e8f 100644 --- a/webapp/src/app/services/color.service.ts +++ b/webapp/src/app/services/color.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ColorService { public processText(text: string): string { const textColors = [ - null, // \c[0] White + null, // \c[0] White '#ff6969', // \c[1] Red '#65ff89', // \c[2] Green '#ffe430', // \c[3] Yellow diff --git a/webapp/src/app/services/electron.service.ts b/webapp/src/app/services/electron.service.ts index 6455e7a1..99da3334 100644 --- a/webapp/src/app/services/electron.service.ts +++ b/webapp/src/app/services/electron.service.ts @@ -4,25 +4,24 @@ import { Globals } from './globals'; import { SharedService } from './shared-service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ElectronService implements SharedService { private static readonly storageName = 'assetsPath'; private static readonly modName = 'selectedMod'; private static assetsPath = ''; private static selectedMod = ''; - + private readonly fs?: typeof import('fs'); private readonly remote?: typeof import('@electron/remote'); - + constructor() { if (!Globals.isElectron) { return; } - + this.remote = window.require('@electron/remote'); this.fs = this.remote!.require('fs'); - } public static async init() { @@ -30,12 +29,13 @@ export class ElectronService implements SharedService { return; } - ElectronService.assetsPath = localStorage.getItem(ElectronService.storageName) || ''; + ElectronService.assetsPath = + localStorage.getItem(ElectronService.storageName) || ''; ElectronService.updateURL(); ElectronService.selectedMod = localStorage.getItem(this.modName) || ''; await ElectronService.updateMod(); } - + private static normalizePath(p: string) { if (p.endsWith('\\')) { p = p.split('\\').join('/'); @@ -45,15 +45,18 @@ export class ElectronService implements SharedService { } return p; } - + private static updateURL() { Globals.URL = 'file:///' + ElectronService.assetsPath; } private static async updateMod() { - await api.selectedMod(ElectronService.assetsPath, ElectronService.selectedMod); + await api.selectedMod( + ElectronService.assetsPath, + ElectronService.selectedMod, + ); } - + public relaunch() { if (!this.remote) { throw new Error('remote is not defined'); @@ -61,7 +64,7 @@ export class ElectronService implements SharedService { this.remote.app.relaunch(); this.remote.app.quit(); } - + public checkAssetsPath(path: string): boolean { if (!this.fs) { return false; @@ -74,20 +77,21 @@ export class ElectronService implements SharedService { return false; } } - + public selectCcFolder(): string | undefined { if (!this.remote) { throw new Error('remote is not defined'); } const newPath = this.remote.dialog.showOpenDialogSync({ title: 'Select CrossCode assets folder', - defaultPath: 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\CrossCode\\assets', - properties: ['openDirectory'] + defaultPath: + 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\CrossCode\\assets', + properties: ['openDirectory'], }); - + return newPath ? newPath[0] : undefined; } - + public saveAssetsPath(path: string) { const normalized = ElectronService.normalizePath(path); ElectronService.assetsPath = normalized; @@ -100,7 +104,7 @@ export class ElectronService implements SharedService { ElectronService.selectedMod = mod; await ElectronService.updateMod(); } - + public getAssetsPath() { return ElectronService.assetsPath; } diff --git a/webapp/src/app/services/global-events.service.ts b/webapp/src/app/services/global-events.service.ts index 704cbcdf..7742dabe 100644 --- a/webapp/src/app/services/global-events.service.ts +++ b/webapp/src/app/services/global-events.service.ts @@ -7,7 +7,7 @@ import { GridSettings } from '../components/toolbar/grid-menu/grid-menu.componen import { Globals } from './globals'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class GlobalEventsService { currentView = new BehaviorSubject(undefined); @@ -23,16 +23,16 @@ export class GlobalEventsService { toggleVisibility = new Subject(); showAddEntityMenu = new Subject(); updateEntities = new Subject(); - + updateCoords = new Subject(); updateTileSelectionSize = new Subject(); isAutotile = new BehaviorSubject(false); showIngamePreview = new BehaviorSubject(false); hasUnsavedChanges = new BehaviorSubject(false); gridSettings = new BehaviorSubject(Globals.gridSettings()); - + babylonLoading = new BehaviorSubject(false); is3D = new BehaviorSubject(false); - + constructor() {} } diff --git a/webapp/src/app/services/globals.ts b/webapp/src/app/services/globals.ts index bbaa9dd0..bebf6d4d 100644 --- a/webapp/src/app/services/globals.ts +++ b/webapp/src/app/services/globals.ts @@ -21,13 +21,13 @@ export class Globals { static TILE_SIZE = 16; static URL = 'http://localhost:8080/'; static gridSettings = signal({ - size: {x: 8, y: 8}, - offset: {x: 0, y: 0}, + size: { x: 8, y: 8 }, + offset: { x: 0, y: 0 }, color: '#222222', - enableGrid: false + enableGrid: false, }); static disablePhaserInput = new Set(); - + static stateHistoryService: StateHistoryService; static mapLoaderService: MapLoaderService; static globalEventsService: GlobalEventsService; diff --git a/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.constants.ts b/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.constants.ts index cf0eb2c5..f5973e32 100644 --- a/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.constants.ts +++ b/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.constants.ts @@ -30,14 +30,12 @@ export interface GfxMaps { DARK_WALL: GfxMap; CHASM_FLOOR: GfxMapChasm; BACK_WALL: GfxMap; - + SUB: { BASE: GfxMap; SHADOW: GfxMap; BACK_WALL: GfxMap; - BORDER: { - [key in GFX_TYPE]: number[][][]; - }; + BORDER: Record; CHASM_FLOOR?: GfxMap; ignoreTerrain?: GFX_TYPE[]; ignoreTerrainKeepWallBase?: GFX_TYPE[]; @@ -46,114 +44,227 @@ export interface GfxMaps { chasmTileAdd: number; } -type GfxBaseMap = { [key in GFX_TYPE]: number[][]; } & { +type GfxBaseMap = Record & { offset?: Point; }; export type GfxMap = GfxBaseMap & { - wallYVariance?: { - [key in GFX_TYPE]: { + wallYVariance?: Record< + GFX_TYPE, + { loop: number[]; end: number[]; start?: number[]; } - }; + >; }; export type GfxMapChasm = GfxBaseMap & { - wallYVariance?: { - [key in GFX_TYPE]: { + wallYVariance?: Record< + GFX_TYPE, + { start: number[]; } - }; + >; }; -export const GFX_MAPS: { [key: string]: GfxMaps } = {}; +export const GFX_MAPS: Record = {}; let map: GfxMaps = { - BASE: {}, - ALT: {}, - SHADOW: {}, - CHASM: {}, - DARK_WALL: {}, - CHASM_FLOOR: {}, - BACK_WALL: {}, - + BASE: {} as any, + ALT: {} as any, + SHADOW: {} as any, + CHASM: {} as any, + DARK_WALL: {} as any, + CHASM_FLOOR: {} as any, + BACK_WALL: {} as any, + SUB: { - BASE: {}, - SHADOW: {}, - BACK_WALL: {}, - BORDER: {} + BASE: {} as any, + SHADOW: {} as any, + BACK_WALL: {} as any, + BORDER: {} as any, }, - + hasShadowSide: true, - chasmTileAdd: 1 + chasmTileAdd: 1, }; GFX_MAPS['TYPE1'] = map; -map['BASE'][GFX_TYPE.NORTH] = [[2, 0], [3, 0]]; -map['BASE'][GFX_TYPE.EAST] = [[3, 1], [3, 2]]; -map['BASE'][GFX_TYPE.SOUTH] = [[2, 3], [3, 3]]; -map['BASE'][GFX_TYPE.WEST] = [[2, 1], [2, 2]]; -map['BASE'][GFX_TYPE.DIAGONAL_NE] = [[4, 0], [5, 1]]; -map['BASE'][GFX_TYPE.DIAGONAL_SE] = [[4, 3], [5, 2]]; -map['BASE'][GFX_TYPE.DIAGONAL_SW] = [[0, 2], [1, 3]]; -map['BASE'][GFX_TYPE.DIAGONAL_NW] = [[0, 1], [1, 0]]; +map['BASE'][GFX_TYPE.NORTH] = [ + [2, 0], + [3, 0], +]; +map['BASE'][GFX_TYPE.EAST] = [ + [3, 1], + [3, 2], +]; +map['BASE'][GFX_TYPE.SOUTH] = [ + [2, 3], + [3, 3], +]; +map['BASE'][GFX_TYPE.WEST] = [ + [2, 1], + [2, 2], +]; +map['BASE'][GFX_TYPE.DIAGONAL_NE] = [ + [4, 0], + [5, 1], +]; +map['BASE'][GFX_TYPE.DIAGONAL_SE] = [ + [4, 3], + [5, 2], +]; +map['BASE'][GFX_TYPE.DIAGONAL_SW] = [ + [0, 2], + [1, 3], +]; +map['BASE'][GFX_TYPE.DIAGONAL_NW] = [ + [0, 1], + [1, 0], +]; map['BASE'][GFX_TYPE.CORNER_NE] = [[4, 1]]; map['BASE'][GFX_TYPE.CORNER_SE] = [[4, 2]]; map['BASE'][GFX_TYPE.CORNER_SW] = [[1, 2]]; map['BASE'][GFX_TYPE.CORNER_NW] = [[1, 1]]; -map['BASE'][GFX_TYPE.WALL_SOUTH] = [[3, 4], [2, 4]]; -map['BASE'][GFX_TYPE.WALL_SE] = [[5, 3], [4, 4]]; -map['BASE'][GFX_TYPE.WALL_SW] = [[1, 4], [0, 3]]; -map['BASE'][GFX_TYPE.WALL_SOUTH_BASE] = [[2, 5], [3, 5]]; -map['BASE'][GFX_TYPE.WALL_SE_BASE] = [[4, 5], [5, 4]]; -map['BASE'][GFX_TYPE.WALL_SW_BASE] = [[0, 4], [1, 5]]; -map['BASE'][GFX_TYPE.WALL_END_WEST] = [[0, 6], [0, 7]]; -map['BASE'][GFX_TYPE.WALL_END_WEST_BASE] = [[0, 6], [0, 7]]; -map['BASE'][GFX_TYPE.WALL_END_EAST] = [[5, 6], [5, 7]]; -map['BASE'][GFX_TYPE.WALL_END_EAST_BASE] = [[5, 6], [5, 7]]; - -map['ALT'].offset = {x: 0, y: 6}; -map['ALT'][GFX_TYPE.WALL_SOUTH] = [[3, 0], [2, 0]]; -map['ALT'][GFX_TYPE.WALL_SE] = [[5, 0], [4, 0]]; -map['ALT'][GFX_TYPE.WALL_SW] = [[1, 0], [0, 0]]; - -map['SHADOW'].offset = {x: 0, y: 0}; +map['BASE'][GFX_TYPE.WALL_SOUTH] = [ + [3, 4], + [2, 4], +]; +map['BASE'][GFX_TYPE.WALL_SE] = [ + [5, 3], + [4, 4], +]; +map['BASE'][GFX_TYPE.WALL_SW] = [ + [1, 4], + [0, 3], +]; +map['BASE'][GFX_TYPE.WALL_SOUTH_BASE] = [ + [2, 5], + [3, 5], +]; +map['BASE'][GFX_TYPE.WALL_SE_BASE] = [ + [4, 5], + [5, 4], +]; +map['BASE'][GFX_TYPE.WALL_SW_BASE] = [ + [0, 4], + [1, 5], +]; +map['BASE'][GFX_TYPE.WALL_END_WEST] = [ + [0, 6], + [0, 7], +]; +map['BASE'][GFX_TYPE.WALL_END_WEST_BASE] = [ + [0, 6], + [0, 7], +]; +map['BASE'][GFX_TYPE.WALL_END_EAST] = [ + [5, 6], + [5, 7], +]; +map['BASE'][GFX_TYPE.WALL_END_EAST_BASE] = [ + [5, 6], + [5, 7], +]; + +map['ALT'].offset = { x: 0, y: 6 }; +map['ALT'][GFX_TYPE.WALL_SOUTH] = [ + [3, 0], + [2, 0], +]; +map['ALT'][GFX_TYPE.WALL_SE] = [ + [5, 0], + [4, 0], +]; +map['ALT'][GFX_TYPE.WALL_SW] = [ + [1, 0], + [0, 0], +]; + +map['SHADOW'].offset = { x: 0, y: 0 }; map['SHADOW'][GFX_TYPE.FILL] = [[0, 0]]; map['SHADOW'][GFX_TYPE.INVISIBLE_WALL] = [[5, 0]]; -map['CHASM'].offset = {x: 0, y: 1}; -map['CHASM'].wallYVariance = {}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SOUTH] = {start: [1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SE] = {start: [1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SW] = {start: [1, 0]}; - +map['CHASM'].offset = { x: 0, y: 1 }; +map['CHASM'].wallYVariance = {} as any; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SOUTH] = { start: [1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SE] = { start: [1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SW] = { start: [1, 0] }; -map['DARK_WALL'].offset = {x: 0, y: 7}; +map['DARK_WALL'].offset = { x: 0, y: 7 }; -map['BACK_WALL'].offset = {x: 0, y: 7}; -map['BACK_WALL'][GFX_TYPE.EAST] = [[3, 3], [4, 2]]; -map['BACK_WALL'][GFX_TYPE.WEST] = [[1, 2], [2, 3]]; +map['BACK_WALL'].offset = { x: 0, y: 7 }; +map['BACK_WALL'][GFX_TYPE.EAST] = [ + [3, 3], + [4, 2], +]; +map['BACK_WALL'][GFX_TYPE.WEST] = [ + [1, 2], + [2, 3], +]; -map['SUB'].ignoreTerrain = [GFX_TYPE.WALL_SOUTH, GFX_TYPE.WALL_SE, GFX_TYPE.WALL_SW]; -map['SUB']['BASE'][GFX_TYPE.WALL_SOUTH_BASE] = [[2, 4], [3, 4]]; -map['SUB']['BASE'][GFX_TYPE.WALL_SE_BASE] = [[4, 4], [5, 3]]; -map['SUB']['BASE'][GFX_TYPE.WALL_SW_BASE] = [[0, 3], [1, 4]]; +map['SUB'].ignoreTerrain = [ + GFX_TYPE.WALL_SOUTH, + GFX_TYPE.WALL_SE, + GFX_TYPE.WALL_SW, +]; +map['SUB']['BASE'][GFX_TYPE.WALL_SOUTH_BASE] = [ + [2, 4], + [3, 4], +]; +map['SUB']['BASE'][GFX_TYPE.WALL_SE_BASE] = [ + [4, 4], + [5, 3], +]; +map['SUB']['BASE'][GFX_TYPE.WALL_SW_BASE] = [ + [0, 3], + [1, 4], +]; -map['SUB']['SHADOW'].offset = {x: 0, y: 5}; -map['SUB']['SHADOW'][GFX_TYPE.NORTH] = [[2, 0], [3, 0]]; -map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NW] = [[0, 0], [1, 0]]; -map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NE] = [[4, 0], [5, 0]]; -map['SUB']['SHADOW'][GFX_TYPE.WEST] = [[0, 1], [1, 1]]; -map['SUB']['SHADOW'][GFX_TYPE.EAST] = [[5, 1], [4, 1]]; +map['SUB']['SHADOW'].offset = { x: 0, y: 5 }; +map['SUB']['SHADOW'][GFX_TYPE.NORTH] = [ + [2, 0], + [3, 0], +]; +map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NW] = [ + [0, 0], + [1, 0], +]; +map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NE] = [ + [4, 0], + [5, 0], +]; +map['SUB']['SHADOW'][GFX_TYPE.WEST] = [ + [0, 1], + [1, 1], +]; +map['SUB']['SHADOW'][GFX_TYPE.EAST] = [ + [5, 1], + [4, 1], +]; -map['SUB']['BACK_WALL'].offset = {x: 0, y: 7}; -map['SUB']['BACK_WALL'][GFX_TYPE.NORTH] = [[2, 0], [3, 0]]; -map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_NW] = [[0, 0], [1, 0]]; -map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_NE] = [[4, 0], [5, 0]]; -map['SUB']['BACK_WALL'][GFX_TYPE.WEST] = [[0, 1], [1, 1]]; -map['SUB']['BACK_WALL'][GFX_TYPE.EAST] = [[5, 1], [4, 1]]; +map['SUB']['BACK_WALL'].offset = { x: 0, y: 7 }; +map['SUB']['BACK_WALL'][GFX_TYPE.NORTH] = [ + [2, 0], + [3, 0], +]; +map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_NW] = [ + [0, 0], + [1, 0], +]; +map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_NE] = [ + [4, 0], + [5, 0], +]; +map['SUB']['BACK_WALL'][GFX_TYPE.WEST] = [ + [0, 1], + [1, 1], +]; +map['SUB']['BACK_WALL'][GFX_TYPE.EAST] = [ + [5, 1], + [4, 1], +]; map['SUB']['BORDER'][GFX_TYPE.NORTH] = [[[2, 5]], [[3, 5]]]; map['SUB']['BORDER'][GFX_TYPE.EAST] = [[[4, 5]], [[4, 6]]]; @@ -161,23 +272,23 @@ map['SUB']['BORDER'][GFX_TYPE.SOUTH] = [[[2, 6]], [[3, 6]]]; map['SUB']['BORDER'][GFX_TYPE.WEST] = [[[1, 5]], [[1, 6]]]; map = { - BASE: {}, - ALT: {}, - SHADOW: {}, - CHASM: {}, - DARK_WALL: {}, - CHASM_FLOOR: {}, - BACK_WALL: {}, - + BASE: {} as any, + ALT: {} as any, + SHADOW: {} as any, + CHASM: {} as any, + DARK_WALL: {} as any, + CHASM_FLOOR: {} as any, + BACK_WALL: {} as any, + SUB: { - BASE: {}, - SHADOW: {}, - BACK_WALL: {}, - BORDER: {} + BASE: {} as any, + SHADOW: {} as any, + BACK_WALL: {} as any, + BORDER: {} as any, }, - + hasShadowSide: false, - chasmTileAdd: 0 + chasmTileAdd: 0, }; GFX_MAPS['TYPE2'] = map; @@ -212,21 +323,21 @@ map['BASE'][GFX_TYPE.WALL_END_WEST] = [[3, 0]]; map['BASE'][GFX_TYPE.WALL_END_WEST_BASE] = [[3, 1]]; map['BASE'][GFX_TYPE.WALL_END_EAST] = [[4, 0]]; map['BASE'][GFX_TYPE.WALL_END_EAST_BASE] = [[4, 1]]; -map['BASE'].wallYVariance = {}; -map['BASE'].wallYVariance![GFX_TYPE.WALL_SOUTH] = {loop: [1, 2], end: [0]}; -map['BASE'].wallYVariance![GFX_TYPE.WALL_SE] = {loop: [1, 2], end: [0]}; -map['BASE'].wallYVariance![GFX_TYPE.WALL_SW] = {loop: [1, 2], end: [0]}; -map['BASE'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = {loop: [1, 2], end: [0]}; -map['BASE'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = {loop: [1, 2], end: [0]}; - -map['SHADOW'].offset = {x: 0, y: 0}; +map['BASE'].wallYVariance = {} as any; +map['BASE'].wallYVariance![GFX_TYPE.WALL_SOUTH] = { loop: [1, 2], end: [0] }; +map['BASE'].wallYVariance![GFX_TYPE.WALL_SE] = { loop: [1, 2], end: [0] }; +map['BASE'].wallYVariance![GFX_TYPE.WALL_SW] = { loop: [1, 2], end: [0] }; +map['BASE'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = { loop: [1, 2], end: [0] }; +map['BASE'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = { loop: [1, 2], end: [0] }; + +map['SHADOW'].offset = { x: 0, y: 0 }; map['SHADOW'][GFX_TYPE.FILL] = [[1, 1]]; map['SHADOW'][GFX_TYPE.INVISIBLE_WALL] = [[1, 2]]; map['SHADOW'][GFX_TYPE.EAST] = [[1, 1]]; map['SHADOW'][GFX_TYPE.WEST] = [[1, 1]]; -map['SHADOW'].wallYVariance = {}; +map['SHADOW'].wallYVariance = {} as any; -map['CHASM'].offset = {x: 0, y: 8}; +map['CHASM'].offset = { x: 0, y: 8 }; map['CHASM'][GFX_TYPE.WALL_SE_BASE] = [[3, 2]]; map['CHASM'][GFX_TYPE.WALL_SW_BASE] = [[4, 2]]; map['CHASM'][GFX_TYPE.WALL_SOUTH] = [[1, 2]]; @@ -234,15 +345,14 @@ map['CHASM'][GFX_TYPE.WALL_SQR_SW] = [[0, 2]]; map['CHASM'][GFX_TYPE.WALL_SQR_SE] = [[2, 2]]; map['CHASM'][GFX_TYPE.WALL_SE] = [[3, 3]]; map['CHASM'][GFX_TYPE.WALL_SW] = [[4, 3]]; -map['CHASM'].wallYVariance = {}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SOUTH] = {start: [2, 1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SE] = {start: [2, 1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SW] = {start: [2, 1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = {start: [2, 1, 0]}; -map['CHASM'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = {start: [2, 1, 0]}; - - -map['CHASM_FLOOR'].offset = {x: 0, y: 8}; +map['CHASM'].wallYVariance = {} as any; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SOUTH] = { start: [2, 1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SE] = { start: [2, 1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SW] = { start: [2, 1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = { start: [2, 1, 0] }; +map['CHASM'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = { start: [2, 1, 0] }; + +map['CHASM_FLOOR'].offset = { x: 0, y: 8 }; map['CHASM_FLOOR'][GFX_TYPE.DIAGONAL_SE] = [[3, 0]]; map['CHASM_FLOOR'][GFX_TYPE.DIAGONAL_SW] = [[4, 0]]; map['CHASM_FLOOR'][GFX_TYPE.WALL_SOUTH] = [[1, 1]]; @@ -250,15 +360,14 @@ map['CHASM_FLOOR'][GFX_TYPE.WALL_SQR_SW] = [[0, 1]]; map['CHASM_FLOOR'][GFX_TYPE.WALL_SQR_SE] = [[2, 1]]; map['CHASM_FLOOR'][GFX_TYPE.WALL_SE] = [[3, 1]]; map['CHASM_FLOOR'][GFX_TYPE.WALL_SW] = [[4, 1]]; -map['CHASM_FLOOR'].wallYVariance = {}; -map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SOUTH] = {start: [3, 2, 0]}; -map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SE] = {start: [4, 3, 0]}; -map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SW] = {start: [4, 3, 0]}; -map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = {start: [3, 2, 0]}; -map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = {start: [3, 2, 0]}; - - -map['DARK_WALL'].offset = {x: 0, y: 13}; +map['CHASM_FLOOR'].wallYVariance = {} as any; +map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SOUTH] = { start: [3, 2, 0] }; +map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SE] = { start: [4, 3, 0] }; +map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SW] = { start: [4, 3, 0] }; +map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = { start: [3, 2, 0] }; +map['CHASM_FLOOR'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = { start: [3, 2, 0] }; + +map['DARK_WALL'].offset = { x: 0, y: 13 }; map['DARK_WALL'][GFX_TYPE.NORTH] = [[3, 1]]; map['DARK_WALL'][GFX_TYPE.DIAGONAL_NE] = [[1, 0]]; map['DARK_WALL'][GFX_TYPE.DIAGONAL_NW] = [[0, 0]]; @@ -270,8 +379,7 @@ map['DARK_WALL'][GFX_TYPE.CORNER_NW] = [[2, 0]]; map['DARK_WALL'][GFX_TYPE.WEST] = [[0, 1]]; map['DARK_WALL'][GFX_TYPE.EAST] = [[1, 1]]; - -map['BACK_WALL'].offset = {x: 0, y: 2}; +map['BACK_WALL'].offset = { x: 0, y: 2 }; map['BACK_WALL'][GFX_TYPE.WALL_SOUTH_BASE] = [[1, 6]]; map['BACK_WALL'][GFX_TYPE.WALL_SE_BASE] = [[2, 6]]; map['BACK_WALL'][GFX_TYPE.WALL_SW_BASE] = [[0, 6]]; @@ -279,12 +387,12 @@ map['BACK_WALL'][GFX_TYPE.WALL_SQR_SE_BASE] = [[4, 5]]; map['BACK_WALL'][GFX_TYPE.WALL_SQR_SW_BASE] = [[3, 5]]; map['BACK_WALL'][GFX_TYPE.EAST] = [[1, 0]]; map['BACK_WALL'][GFX_TYPE.WEST] = [[1, 0]]; -map['BACK_WALL'].wallYVariance = {}; -map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SOUTH] = {loop: [1], end: [0]}; -map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SE] = {loop: [1], end: [0]}; -map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SW] = {loop: [1], end: [0]}; -map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = {loop: [1], end: [0]}; -map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = {loop: [1], end: [0]}; +map['BACK_WALL'].wallYVariance = {} as any; +map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SOUTH] = { loop: [1], end: [0] }; +map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SE] = { loop: [1], end: [0] }; +map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SW] = { loop: [1], end: [0] }; +map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SQR_SE] = { loop: [1], end: [0] }; +map['BACK_WALL'].wallYVariance![GFX_TYPE.WALL_SQR_SW] = { loop: [1], end: [0] }; map['SUB'].ignoreTerrain = [ GFX_TYPE.WALL_SOUTH, @@ -294,20 +402,26 @@ map['SUB'].ignoreTerrain = [ GFX_TYPE.WALL_SQR_SE, GFX_TYPE.WALL_SQR_SE_BASE, GFX_TYPE.WALL_SQR_SW, - GFX_TYPE.WALL_SQR_SW_BASE + GFX_TYPE.WALL_SQR_SW_BASE, +]; +map['SUB'].ignoreTerrainKeepWallBase = [ + GFX_TYPE.WALL_SOUTH, + GFX_TYPE.WALL_SE, + GFX_TYPE.WALL_SW, + GFX_TYPE.WALL_SQR_SE, + GFX_TYPE.WALL_SQR_SW, ]; -map['SUB'].ignoreTerrainKeepWallBase = [GFX_TYPE.WALL_SOUTH, GFX_TYPE.WALL_SE, GFX_TYPE.WALL_SW, GFX_TYPE.WALL_SQR_SE, GFX_TYPE.WALL_SQR_SW]; map['SUB']['BASE'][GFX_TYPE.WALL_SE_BASE] = [[2, 4]]; map['SUB']['BASE'][GFX_TYPE.WALL_SW_BASE] = [[0, 4]]; map['SUB']['BASE'][GFX_TYPE.WALL_SOUTH_BASE] = [[1, 4]]; map['SUB']['BASE'][GFX_TYPE.WALL_SQR_SE_BASE] = [[1, 2]]; map['SUB']['BASE'][GFX_TYPE.WALL_SQR_SW_BASE] = [[1, 1]]; -map['SUB']['SHADOW'].offset = {x: 0, y: 0}; +map['SUB']['SHADOW'].offset = { x: 0, y: 0 }; map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NW] = [[3, 3]]; map['SUB']['SHADOW'][GFX_TYPE.DIAGONAL_NE] = [[4, 3]]; -map['SUB']['BACK_WALL'].offset = {x: 0, y: 0}; +map['SUB']['BACK_WALL'].offset = { x: 0, y: 0 }; map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_SE] = [[3, 4]]; map['SUB']['BACK_WALL'][GFX_TYPE.DIAGONAL_SW] = [[4, 4]]; @@ -316,36 +430,47 @@ map['SUB']['BORDER'][GFX_TYPE.EAST] = [[[4, 5]], [[4, 6]]]; map['SUB']['BORDER'][GFX_TYPE.SOUTH] = [[[2, 6]], [[3, 6]]]; map['SUB']['BORDER'][GFX_TYPE.WEST] = [[[1, 5]], [[1, 6]]]; -map['SUB']['CHASM_FLOOR'] = {}; +map['SUB']['CHASM_FLOOR'] = {} as any; map['SUB']['CHASM_FLOOR']![GFX_TYPE.DIAGONAL_SE] = [[0, 5]]; map['SUB']['CHASM_FLOOR']![GFX_TYPE.DIAGONAL_SW] = [[0, 6]]; +export const BASE_GFX = [ + GFX_TYPE.WALL_SOUTH_BASE, + GFX_TYPE.WALL_SE_BASE, + GFX_TYPE.WALL_SW_BASE, +]; -export const BASE_GFX = [GFX_TYPE.WALL_SOUTH_BASE, GFX_TYPE.WALL_SE_BASE, GFX_TYPE.WALL_SW_BASE]; - -export const BACK_WALL_MAP: { [key in GFX_TYPE]: GFX_TYPE } = {}; +export const BACK_WALL_MAP: Record = {} as any; BACK_WALL_MAP[GFX_TYPE.WEST] = GFX_TYPE.WEST; BACK_WALL_MAP[GFX_TYPE.EAST] = GFX_TYPE.EAST; BACK_WALL_MAP[GFX_TYPE.DIAGONAL_NW] = GFX_TYPE.DIAGONAL_SE; BACK_WALL_MAP[GFX_TYPE.DIAGONAL_NE] = GFX_TYPE.DIAGONAL_SW; -export const SHADOW_GROUND = [GFX_TYPE.DIAGONAL_NE, GFX_TYPE.DIAGONAL_NW, GFX_TYPE.NORTH, GFX_TYPE.EAST, GFX_TYPE.WEST, GFX_TYPE.CORNER_NE, GFX_TYPE.CORNER_NW]; +export const SHADOW_GROUND = [ + GFX_TYPE.DIAGONAL_NE, + GFX_TYPE.DIAGONAL_NW, + GFX_TYPE.NORTH, + GFX_TYPE.EAST, + GFX_TYPE.WEST, + GFX_TYPE.CORNER_NE, + GFX_TYPE.CORNER_NW, +]; -export const BLOCK_MAP: { [key in FILL_TYPE]: number } = {}; +export const BLOCK_MAP: Record = {} as any; BLOCK_MAP[FILL_TYPE.SQUARE] = 2; BLOCK_MAP[FILL_TYPE.NORTHEAST] = 8; BLOCK_MAP[FILL_TYPE.SOUTHEAST] = 9; BLOCK_MAP[FILL_TYPE.SOUTHWEST] = 10; BLOCK_MAP[FILL_TYPE.NORTHWEST] = 11; -export const HOLE_MAP: { [key in FILL_TYPE]: number } = {}; +export const HOLE_MAP: Record = {} as any; HOLE_MAP[FILL_TYPE.SQUARE] = 1; HOLE_MAP[FILL_TYPE.NORTHEAST] = 6; HOLE_MAP[FILL_TYPE.SOUTHEAST] = 7; HOLE_MAP[FILL_TYPE.SOUTHWEST] = 4; HOLE_MAP[FILL_TYPE.NORTHWEST] = 5; -export const HOLE_BLOCK_MAP: { [key in FILL_TYPE]: number } = {}; +export const HOLE_BLOCK_MAP: Record = {} as any; HOLE_BLOCK_MAP[FILL_TYPE.SQUARE] = 2; HOLE_BLOCK_MAP[FILL_TYPE.NORTHEAST] = 24; HOLE_BLOCK_MAP[FILL_TYPE.SOUTHEAST] = 25; diff --git a/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.ts b/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.ts index 9a2fefa7..64e87a8a 100644 --- a/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.ts +++ b/webapp/src/app/services/height-map/gfx-mapper/gfx-mapper.ts @@ -1,6 +1,12 @@ import { Point } from '../../../models/cross-code-map'; import { GFX_TYPE } from '../heightmap.constants'; -import { ChipsetBase, ChipsetConfig, GfxMap, GfxMaps, GFX_MAPS } from './gfx-mapper.constants'; +import { + ChipsetBase, + ChipsetConfig, + GFX_MAPS, + GfxMap, + GfxMaps, +} from './gfx-mapper.constants'; interface ChipsetMapping extends ChipsetBase { mapping: GfxMaps; @@ -10,7 +16,7 @@ export class GfxMapper { private tileCountX = 0; private base: ChipsetMapping; private terrains: ChipsetMapping[] = []; - + constructor(settings: ChipsetConfig) { this.tileCountX = settings['tileCountX']; this.base = this.copySettings(settings['base']); @@ -19,17 +25,18 @@ export class GfxMapper { this.terrains[i] = this.copySettings(settings['terrains'][i]); } } - } - + private copySettings(mappingSettings: ChipsetBase) { - const result: ChipsetMapping = JSON.parse(JSON.stringify(mappingSettings)); + const result: ChipsetMapping = JSON.parse( + JSON.stringify(mappingSettings), + ); if (mappingSettings.mappingType) { result.mapping = GFX_MAPS[mappingSettings.mappingType]; } return result; } - + private getMappingMain(terrain: number): ChipsetMapping { if (terrain && this.terrains[terrain - 1]) { const terrainEntry = this.terrains[terrain - 1]; @@ -42,52 +49,69 @@ export class GfxMapper { } return this.base; } - + hasShadowSide(terrain: number) { const mapping = this.getMappingMain(terrain); return mapping.mapping.hasShadowSide; } - + getChasmHeight(terrain: number) { const mapping = this.getMappingMain(terrain); let minHeight = 1; if (mapping.mapping['CHASM'].wallYVariance) { - minHeight = mapping.mapping['CHASM'].wallYVariance[GFX_TYPE.WALL_SOUTH].start.length; + minHeight = + mapping.mapping['CHASM'].wallYVariance[GFX_TYPE.WALL_SOUTH] + .start.length; } return minHeight; } - + getChasmTileAdd(terrain: number) { const mapping = this.getMappingMain(terrain); return mapping.mapping.chasmTileAdd || 0; } - + hasFloorChasm(terrain: number) { return this.getChasmTileAdd(terrain) === 0; } - + hasShadow() { return this.base.shadow && !this.base.chasmOnly; } - + hasChasm() { return this.base.shadow; } - + isFill(gfxType: GFX_TYPE, terrain?: number) { - return gfxType === GFX_TYPE.FILL || - (terrain && this.terrains[terrain - 1].blockedTypes ? - this.terrains[terrain - 1].blockedTypes!.indexOf(gfxType) !== -1 : - this.base.blockedTypes && this.base.blockedTypes.indexOf(gfxType) !== -1); + return ( + gfxType === GFX_TYPE.FILL || + (terrain && this.terrains[terrain - 1].blockedTypes + ? this.terrains[terrain - 1].blockedTypes!.indexOf(gfxType) !== + -1 + : this.base.blockedTypes && + this.base.blockedTypes.indexOf(gfxType) !== -1) + ); } - + isWallTerrainFromTop(lower: number, upper: number) { - const lowerTerrain = lower && this.terrains[lower - 1] || this.base; - const upperTerrain = upper && this.terrains[upper - 1] || this.base; - return (upperTerrain && upperTerrain.wallTerrainPrio || 0) > (lowerTerrain && lowerTerrain.wallTerrainPrio || 0); + const lowerTerrain = (lower && this.terrains[lower - 1]) || this.base; + const upperTerrain = (upper && this.terrains[upper - 1]) || this.base; + return ( + ((upperTerrain && upperTerrain.wallTerrainPrio) || 0) > + ((lowerTerrain && lowerTerrain.wallTerrainPrio) || 0) + ); } - - getGfx(gfxType: GFX_TYPE, x: number, y: number, subType: keyof GfxMaps | null, terrain: number, terrainBorder = -1, wallProps?: { start: number, end: number }) { + + getGfx( + gfxType: GFX_TYPE, + x: number, + y: number, + subType: keyof GfxMaps | null, + terrain: number, + terrainBorder = -1, + wallProps?: { start: number; end: number }, + ) { if (terrain && !this.terrains[terrain - 1]) { terrain = 0; } @@ -101,24 +125,26 @@ export class GfxMapper { mainMapping = this.terrains[subMapping.baseTerrain - 1]; } } - - + let offset = null; const mapping = mainMapping.mapping; let tiles = mapping['BASE'][gfxType]; let ground: Point | undefined = mainMapping.ground; let cliff: Point | undefined = mainMapping.cliff; let wallYVariance = mapping['BASE'].wallYVariance; - const ignore = subMapping && subMapping.overrideWallBase ? mapping['SUB'].ignoreTerrainKeepWallBase : mapping['SUB'].ignoreTerrain; - + const ignore = + subMapping && subMapping.overrideWallBase + ? mapping['SUB'].ignoreTerrainKeepWallBase + : mapping['SUB'].ignoreTerrain; + if (!ignore) { throw new Error('ignore should be defined'); } - + if (subMapping && ignore.indexOf(gfxType) !== -1) { subMapping = null; } - + if (subType) { const gfxMap = mapping[subType] as GfxMap; offset = gfxMap.offset; @@ -126,14 +152,18 @@ export class GfxMapper { wallYVariance = gfxMap.wallYVariance || wallYVariance; ground = undefined; cliff = mainMapping.shadow; - } else if (mainMapping.cliffAlt && mapping['ALT'][gfxType] && Math.random() < 0.5) { + } else if ( + mainMapping.cliffAlt && + mapping['ALT'][gfxType] && + Math.random() < 0.5 + ) { offset = mapping['ALT'].offset; tiles = mapping['ALT'][gfxType]; } - + let hasTerrainBorder = false; if (subMapping) { - // @ts-ignore + // @ts-expect-error sub not considered for type const subTypeSubMapping = subType && mapping['SUB'][subType]; if (subTypeSubMapping && subTypeSubMapping[gfxType]) { cliff = subMapping.cliff; @@ -146,15 +176,15 @@ export class GfxMapper { hasTerrainBorder = !!subMapping.border; } } - + // const offX = offset && offset.x || 0; - let offY = offset && offset.y || 0; - + let offY = (offset && offset.y) || 0; + if (this.isFill(gfxType)) { if (ground) { return this.getTile(ground.x, ground.y); } - + if (tiles) { if (!cliff) { throw new Error('cliff should be defined by now'); @@ -163,22 +193,30 @@ export class GfxMapper { } return 0; } - + if (wallProps && wallYVariance && wallYVariance[gfxType]) { const variance = wallYVariance[gfxType]; if (variance.end && wallProps.end < variance.end.length) { offY += variance.end[wallProps.end]; - } else if (variance.start && wallProps.start < variance.start.length) { + } else if ( + variance.start && + wallProps.start < variance.start.length + ) { offY += variance.start[wallProps.start]; } else if (variance.loop) { offY += variance.loop[wallProps.start % variance.loop.length]; } } - - if (hasTerrainBorder && terrain && terrainBorder !== -1 && mapping['SUB']['BORDER'][gfxType]) { + + if ( + hasTerrainBorder && + terrain && + terrainBorder !== -1 && + mapping['SUB']['BORDER'][gfxType] + ) { tiles = mapping['SUB']['BORDER'][gfxType][terrainBorder]; } - + if (!tiles || tiles.length === 0) { return 0; } @@ -187,19 +225,27 @@ export class GfxMapper { } return this.getMappingTile(cliff, tiles, x, y, offY); } - - private getMappingTile(base: Point, mapping: number[][], x: number, y: number, deltaY?: number) { - const variation = (mapping.length > 1 && this.getVariation(x, y)); - const coords = (variation ? mapping[1] : mapping[0]); - return this.getTile(base.x + coords[0], base.y + coords[1] + (deltaY || 0)); + + private getMappingTile( + base: Point, + mapping: number[][], + x: number, + y: number, + deltaY?: number, + ) { + const variation = mapping.length > 1 && this.getVariation(x, y); + const coords = variation ? mapping[1] : mapping[0]; + return this.getTile( + base.x + coords[0], + base.y + coords[1] + (deltaY || 0), + ); } - + private getTile(x: number, y: number) { return y * this.tileCountX + x + 1; } - + private getVariation(x: number, y: number) { return (x + y) % 2; } - } diff --git a/webapp/src/app/services/height-map/height-map.service.ts b/webapp/src/app/services/height-map/height-map.service.ts index 4b198b6b..d6e72523 100644 --- a/webapp/src/app/services/height-map/height-map.service.ts +++ b/webapp/src/app/services/height-map/height-map.service.ts @@ -1,16 +1,24 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { EventManager } from '@angular/platform-browser'; import { StateHistoryService } from '../../components/dialogs/floating-window/history/state-history.service'; import { AutotileService } from '../autotile/autotile.service'; import { GlobalEventsService } from '../global-events.service'; import { Globals } from '../globals'; +import { JsonLoaderService } from '../json-loader.service'; import { MapLoaderService } from '../map-loader.service'; import { Helper } from '../phaser/helper'; import { CCMap } from '../phaser/tilemap/cc-map'; import { CCMapLayer } from '../phaser/tilemap/cc-map-layer'; +import { customPutTileAt } from '../phaser/tilemap/layer-helper'; import { GfxMapper } from './gfx-mapper/gfx-mapper'; -import { BACK_WALL_MAP, BLOCK_MAP, ChipsetConfig, HOLE_BLOCK_MAP, HOLE_MAP } from './gfx-mapper/gfx-mapper.constants'; +import { + BACK_WALL_MAP, + BLOCK_MAP, + ChipsetConfig, + HOLE_BLOCK_MAP, + HOLE_MAP, +} from './gfx-mapper/gfx-mapper.constants'; import { CHECK_DIR, CHECK_ITERATE, @@ -25,14 +33,10 @@ import { SQUARE_CORNER_CHECK, SUB_TYPE, WALL_LINK, - WallLink + WallLink, } from './heightmap.constants'; -import { customPutTileAt } from '../phaser/tilemap/layer-helper'; -import { JsonLoaderService } from '../json-loader.service'; -interface TilesetJson { - [key: string]: ChipsetConfig; -} +type TilesetJson = Record; interface TileData { level: number; @@ -46,9 +50,15 @@ interface TileData { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class HeightMapService { + private events = inject(GlobalEventsService); + private mapLoader = inject(MapLoaderService); + private stateHistory = inject(StateHistoryService); + private autotile = inject(AutotileService); + private jsonLoader = inject(JsonLoaderService); + private data: (TileData | null)[][] = []; private lastData: (TileData | null)[][] = []; private minLevel = 0; @@ -56,43 +66,46 @@ export class HeightMapService { private width = 0; private height = 0; private tilesetConfig: TilesetJson = {}; - - private c_wallProps = {start: 0, end: 0}; - - constructor( - private events: GlobalEventsService, - private mapLoader: MapLoaderService, - private stateHistory: StateHistoryService, - private autotile: AutotileService, - private jsonLoader: JsonLoaderService, - eventManager: EventManager - ) { - - eventManager.addEventListener(document as any, 'keydown', (event: KeyboardEvent) => { - if (Helper.isInputFocused()) { - return; - } - if (event.ctrlKey && event.key.toLowerCase() === 'h') { - event.preventDefault(); - this.generateHeights(event.shiftKey); - } - }); + + private c_wallProps = { start: 0, end: 0 }; + + constructor() { + const eventManager = inject(EventManager); + + eventManager.addEventListener( + document as any, + 'keydown', + (event: KeyboardEvent) => { + if (Helper.isInputFocused()) { + return; + } + if (event.ctrlKey && event.key.toLowerCase() === 'h') { + event.preventDefault(); + this.generateHeights(event.shiftKey); + } + }, + ); } - + public async init() { - this.events.generateHeights.subscribe(forceAll => this.generateHeights(forceAll)); - this.mapLoader.tileMap.subscribe(map => this.onMapLoad(map)); - - this.tilesetConfig = await this.jsonLoader.loadJsonMerged('tilesets.json'); - + this.events.generateHeights.subscribe((forceAll) => + this.generateHeights(forceAll), + ); + this.mapLoader.tileMap.subscribe((map) => this.onMapLoad(map)); + + this.tilesetConfig = + await this.jsonLoader.loadJsonMerged('tilesets.json'); + // TODO: add shortcuts for generation } - + private onMapLoad(inputMap?: CCMap) { if (!inputMap) { return; } - const heightmap = inputMap.layers.find(layer => layer.details.type === 'HeightMap'); + const heightmap = inputMap.layers.find( + (layer) => layer.details.type === 'HeightMap', + ); if (!heightmap) { console.warn(`current map [${inputMap.name}] has no height map`); return; @@ -103,12 +116,16 @@ export class HeightMapService { this.lastData = this.data; this.data = []; } - + generateHeights(forceAll: boolean) { const map = Globals.map; - const heightmap = map.layers.find(layer => layer.details.type === 'HeightMap'); + const heightmap = map.layers.find( + (layer) => layer.details.type === 'HeightMap', + ); if (!heightmap) { - console.warn(`cannot generate heights, current map [${map.name}] has no height map`); + console.warn( + `cannot generate heights, current map [${map.name}] has no height map`, + ); return; } this.storeTileData(heightmap.exportLayer().data); @@ -116,16 +133,17 @@ export class HeightMapService { this.setGfxType(); this.applyOnLayers(forceAll); this.lastData = this.data; - + this.stateHistory.saveState({ name: 'Height Generation', - icon: 'landscape' + icon: 'landscape', }); } - + private storeTileData(tiles: number[][]) { this.data = []; - const width = tiles[0].length, height = tiles.length; + const width = tiles[0].length, + height = tiles.length; this.minLevel = 1000; this.maxLevel = 0; this.width = width; @@ -140,16 +158,16 @@ export class HeightMapService { } const terrain = Math.floor(tile / (FILL_COUNT * LEVEL_COUNT)); tile = tile % (FILL_COUNT * LEVEL_COUNT); - + const entry = { level: Math.floor(tile / 8) || -1, - fill: (tile % 8), + fill: tile % 8, terrain: terrain, gfx: GFX_TYPE.FILL, lowerLevel: 0, lowerTerrain: 0, upperLevel: 0, - terrainBorder: -1 + terrainBorder: -1, }; this.minLevel = Math.min(entry.level, this.minLevel); this.maxLevel = Math.max(entry.level, this.maxLevel); @@ -157,7 +175,7 @@ export class HeightMapService { } } } - + private convertRoundTiles() { for (let level = this.maxLevel; level >= this.minLevel; level--) { for (let y = 0; y < this.height; ++y) { @@ -177,7 +195,7 @@ export class HeightMapService { } } } - + private setGfxType() { for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { @@ -193,7 +211,7 @@ export class HeightMapService { } } } - + private applyOnLayers(forceAll: boolean) { const layers = Globals.map.layers; const len = layers.length; @@ -202,13 +220,22 @@ export class HeightMapService { const layer = layers[i]; // TODO: use level strings, levelName is currently set on load and then ignored in the editor const details = layer.details; - if (details.levelName === 'first' || details.levelName === 'last' || (details.levelName && details.levelName.indexOf('object') !== -1)) { + if ( + details.levelName === 'first' || + details.levelName === 'last' || + (details.levelName && + details.levelName.indexOf('object') !== -1) + ) { continue; } if (details.distance !== 1) { continue; } - if (details.type === 'Background' && this.tilesetConfig[details.tilesetName] && lastLevel !== details.level) { + if ( + details.type === 'Background' && + this.tilesetConfig[details.tilesetName] && + lastLevel !== details.level + ) { lastLevel = details.level; this.applyOnBackground(layer, forceAll); } else if (details.type === 'Collision') { @@ -216,7 +243,7 @@ export class HeightMapService { } } } - + private getLevelHeight(levelIdx: number) { if (levelIdx < 1) { levelIdx = 1; @@ -227,36 +254,49 @@ export class HeightMapService { return level.height / Globals.TILE_SIZE; } const maxLevel = levels[levels.length - 1]; - return (maxLevel.height / Globals.TILE_SIZE) + (levelIdx - levels.length) * 2; + return ( + maxLevel.height / Globals.TILE_SIZE + (levelIdx - levels.length) * 2 + ); } - + private getLevelDistance(levelStart: number, levelEnd: number) { return this.getLevelHeight(levelEnd) - this.getLevelHeight(levelStart); } - + private applyOnBackground(layer: CCMapLayer, forceAll: boolean) { const config = this.tilesetConfig[layer.details.tilesetName]; if (!config) { return; } const gfxMapper = new GfxMapper(config); - + const details = layer.details; - + const levels = Globals.map.levels; const currentLevel = levels[details.level]; const nextLevel = levels[details.level * 1 + 1]; const masterLevel = levels[Globals.map.masterLevel]; const maxLevel = levels.length; - const yOff = (currentLevel.height - masterLevel.height) / Globals.TILE_SIZE; - const yHeight = nextLevel ? (nextLevel.height - currentLevel.height) / Globals.TILE_SIZE : 0; + const yOff = + (currentLevel.height - masterLevel.height) / Globals.TILE_SIZE; + const yHeight = nextLevel + ? (nextLevel.height - currentLevel.height) / Globals.TILE_SIZE + : 0; const levelIdx: number = details.level + 1; const masterLevelIdx = Globals.map.masterLevel + 1; - + const c_wallProps = this.c_wallProps; - - if (typeof details.level === 'string' || typeof Globals.map.masterLevel === 'string') { - throw new Error('some level is a string:\nlevel: ' + details.level + '\nmasterLevel: ' + Globals.map.masterLevel); + + if ( + typeof details.level === 'string' || + typeof Globals.map.masterLevel === 'string' + ) { + throw new Error( + 'some level is a string:\nlevel: ' + + details.level + + '\nmasterLevel: ' + + Globals.map.masterLevel, + ); } for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { @@ -264,111 +304,219 @@ export class HeightMapService { if (!entry) { continue; } - const doShadow = (gfxMapper.hasShadow() && (entry.level > maxLevel && entry.lowerLevel <= maxLevel)); - + const doShadow = + gfxMapper.hasShadow() && + entry.level > maxLevel && + entry.lowerLevel <= maxLevel; + let wallLink: WallLink | null = WALL_LINK[entry.gfx]; - if (wallLink && wallLink.shadowOnly && (!doShadow || levelIdx < masterLevelIdx)) { + if ( + wallLink && + wallLink.shadowOnly && + (!doShadow || levelIdx < masterLevelIdx) + ) { wallLink = null; } - + if (entry.level === levelIdx) { if (!forceAll && !this.hasTileAreaChanged(x, y)) { continue; } - + let subType: SUB_TYPE | null = null; - if (gfxMapper.hasFloorChasm(entry.terrain) - && (entry.gfx === GFX_TYPE.DIAGONAL_SE || entry.gfx === GFX_TYPE.DIAGONAL_SW) - && entry.level === masterLevelIdx && entry.lowerLevel === -1) { + if ( + gfxMapper.hasFloorChasm(entry.terrain) && + (entry.gfx === GFX_TYPE.DIAGONAL_SE || + entry.gfx === GFX_TYPE.DIAGONAL_SW) && + entry.level === masterLevelIdx && + entry.lowerLevel === -1 + ) { subType = SUB_TYPE.CHASM_FLOOR; } - const newTile = gfxMapper.getGfx(entry.gfx, x, y - yOff, subType, entry.terrain, entry.terrainBorder); + const newTile = gfxMapper.getGfx( + entry.gfx, + x, + y - yOff, + subType, + entry.terrain, + entry.terrainBorder, + ); this.setLayerTile(layer, x, y - yOff, newTile); - } else if (wallLink && wallLink.toMaster && masterLevelIdx <= levelIdx && levelIdx < entry.lowerLevel) { + } else if ( + wallLink && + wallLink.toMaster && + masterLevelIdx <= levelIdx && + levelIdx < entry.lowerLevel + ) { const actualYHeight = yHeight; const deltaY = wallLink.deltaY || 0; - if (!forceAll && !this.hasTileLineChanged(x, y, actualYHeight)) { + if ( + !forceAll && + !this.hasTileLineChanged(x, y, actualYHeight) + ) { continue; } - c_wallProps.start = this.getLevelDistance(masterLevelIdx, levelIdx); - c_wallProps.end = this.getLevelDistance(levelIdx, entry.lowerLevel) - 1; + c_wallProps.start = this.getLevelDistance( + masterLevelIdx, + levelIdx, + ); + c_wallProps.end = + this.getLevelDistance(levelIdx, entry.lowerLevel) - 1; for (let yAdd = 0; yAdd < actualYHeight; ++yAdd) { - const gfxType = yAdd === 0 && masterLevelIdx === levelIdx ? wallLink.base : wallLink.wall; - const newTile = gfxMapper.getGfx(gfxType, x, y - yOff - yAdd + deltaY, SUB_TYPE.BACK_WALL, entry.lowerTerrain, -1, c_wallProps); - this.setLayerTile(layer, x, y - yOff - yAdd + deltaY, newTile); + const gfxType = + yAdd === 0 && masterLevelIdx === levelIdx + ? wallLink.base + : wallLink.wall; + const newTile = gfxMapper.getGfx( + gfxType, + x, + y - yOff - yAdd + deltaY, + SUB_TYPE.BACK_WALL, + entry.lowerTerrain, + -1, + c_wallProps, + ); + this.setLayerTile( + layer, + x, + y - yOff - yAdd + deltaY, + newTile, + ); c_wallProps.start++; c_wallProps.end--; } if (deltaY) { - const newTile = gfxMapper.getGfx(GFX_TYPE.FILL, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); + const newTile = gfxMapper.getGfx( + GFX_TYPE.FILL, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); this.setLayerTile(layer, x, y - yOff, newTile); } - - } else if (wallLink - && !wallLink.toMaster - && (entry.lowerLevel <= levelIdx || (doShadow && levelIdx === masterLevelIdx)) - && entry.level > levelIdx) { - - const doChasm = (!wallLink.shadowOnly && gfxMapper.hasChasm() && entry.lowerLevel === -1 && levelIdx < masterLevelIdx); + } else if ( + wallLink && + !wallLink.toMaster && + (entry.lowerLevel <= levelIdx || + (doShadow && levelIdx === masterLevelIdx)) && + entry.level > levelIdx + ) { + const doChasm = + !wallLink.shadowOnly && + gfxMapper.hasChasm() && + entry.lowerLevel === -1 && + levelIdx < masterLevelIdx; let actualYHeight = yHeight; let yStart = 0; let doShadowWall = false; const chasmHeight = gfxMapper.getChasmHeight(entry.terrain); - const chasmPadding = gfxMapper.getChasmTileAdd(entry.terrain); - + const chasmPadding = gfxMapper.getChasmTileAdd( + entry.terrain, + ); + if (doChasm) { - yStart = this.getLevelDistance(levelIdx, masterLevelIdx) - chasmHeight - chasmPadding; + yStart = + this.getLevelDistance(levelIdx, masterLevelIdx) - + chasmHeight - + chasmPadding; } else if (doShadow && levelIdx === masterLevelIdx) { - actualYHeight = this.getLevelDistance(levelIdx, entry.level); + actualYHeight = this.getLevelDistance( + levelIdx, + entry.level, + ); doShadowWall = true; - yStart = this.getLevelDistance(levelIdx, entry.lowerLevel); + yStart = this.getLevelDistance( + levelIdx, + entry.lowerLevel, + ); } - - if (!forceAll && !this.hasTileLineChanged(x, y, actualYHeight)) { + + if ( + !forceAll && + !this.hasTileLineChanged(x, y, actualYHeight) + ) { continue; } - - const terrain = entry.lowerLevel === -1 || gfxMapper.isWallTerrainFromTop(entry.lowerTerrain, entry.terrain) ? entry.terrain : entry.lowerTerrain; - + + const terrain = + entry.lowerLevel === -1 || + gfxMapper.isWallTerrainFromTop( + entry.lowerTerrain, + entry.terrain, + ) + ? entry.terrain + : entry.lowerTerrain; + if (doShadow && levelIdx > masterLevelIdx) { - const newTile = gfxMapper.getGfx(GFX_TYPE.INVISIBLE_WALL, x, y - yOff, SUB_TYPE.SHADOW, terrain); + const newTile = gfxMapper.getGfx( + GFX_TYPE.INVISIBLE_WALL, + x, + y - yOff, + SUB_TYPE.SHADOW, + terrain, + ); this.setLayerTile(layer, x, y - yOff, newTile); continue; } - - c_wallProps.start = this.getLevelDistance(entry.lowerLevel, levelIdx); - c_wallProps.end = this.getLevelDistance(levelIdx, entry.level) - 1 - yStart; + + c_wallProps.start = this.getLevelDistance( + entry.lowerLevel, + levelIdx, + ); + c_wallProps.end = + this.getLevelDistance(levelIdx, entry.level) - + 1 - + yStart; if (doChasm) { c_wallProps.start = Math.max(-yStart, 0); } if (yStart && doShadowWall) { c_wallProps.start = 0; } - + for (let yAdd = 0; yAdd < actualYHeight; ++yAdd) { - let gfxType: GFX_TYPE | undefined; let subType: SUB_TYPE | null = null; if (yAdd < yStart) { let newTile = 0; if (doShadow) { - newTile = gfxMapper.getGfx(GFX_TYPE.FILL, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + GFX_TYPE.FILL, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); } - this.setLayerTile(layer, x, y - yOff - yAdd, newTile); + this.setLayerTile( + layer, + x, + y - yOff - yAdd, + newTile, + ); continue; } if (yAdd === 0 && entry.lowerLevel === levelIdx) { gfxType = wallLink.base; } else if (doShadowWall && yAdd === yStart) { gfxType = wallLink.base; - } else if (yAdd === 0 && gfxMapper.hasFloorChasm(terrain) && entry.lowerLevel === -1 && levelIdx === masterLevelIdx) { + } else if ( + yAdd === 0 && + gfxMapper.hasFloorChasm(terrain) && + entry.lowerLevel === -1 && + levelIdx === masterLevelIdx + ) { gfxType = wallLink.base; - if (entry.gfx === GFX_TYPE.DIAGONAL_SE || entry.gfx === GFX_TYPE.DIAGONAL_SW) { + if ( + entry.gfx === GFX_TYPE.DIAGONAL_SE || + entry.gfx === GFX_TYPE.DIAGONAL_SW + ) { subType = SUB_TYPE.CHASM; } } else { gfxType = wallLink.wall; - if (doShadowWall && ((actualYHeight - yAdd) === 1)) { + if (doShadowWall && actualYHeight - yAdd === 1) { subType = SUB_TYPE.SHADOW; } } @@ -376,22 +524,49 @@ export class HeightMapService { subType = SUB_TYPE.SHADOW; } if (doChasm && yAdd - yStart < chasmHeight) { - if (gfxMapper.hasFloorChasm(terrain) && entry.level === masterLevelIdx) { + if ( + gfxMapper.hasFloorChasm(terrain) && + entry.level === masterLevelIdx + ) { subType = SUB_TYPE.CHASM_FLOOR; } else { subType = SUB_TYPE.CHASM; } } if (gfxType) { - const newTile = gfxMapper.getGfx(gfxType, x, y - yOff - yAdd, subType, terrain, -1, c_wallProps); - this.setLayerTile(layer, x, y - yOff - yAdd, newTile); + const newTile = gfxMapper.getGfx( + gfxType, + x, + y - yOff - yAdd, + subType, + terrain, + -1, + c_wallProps, + ); + this.setLayerTile( + layer, + x, + y - yOff - yAdd, + newTile, + ); } c_wallProps.start++; c_wallProps.end--; } if (wallLink.wall && doShadowWall) { - const newTile = gfxMapper.getGfx(entry.gfx, x, y - yOff - actualYHeight, SUB_TYPE.SHADOW, terrain); - this.setLayerTile(layer, x, y - yOff - actualYHeight, newTile); + const newTile = gfxMapper.getGfx( + entry.gfx, + x, + y - yOff - actualYHeight, + SUB_TYPE.SHADOW, + terrain, + ); + this.setLayerTile( + layer, + x, + y - yOff - actualYHeight, + newTile, + ); } } else if (entry.lowerLevel === levelIdx) { if (!forceAll && !this.hasTileChanged(x, y)) { @@ -401,21 +576,59 @@ export class HeightMapService { if (gfxMapper.hasShadow()) { if (details.level > Globals.map.masterLevel) { if (BACK_WALL_MAP[entry.gfx]) { - newTile = gfxMapper.getGfx(BACK_WALL_MAP[entry.gfx], x, y - yOff, SUB_TYPE.BACK_WALL, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + BACK_WALL_MAP[entry.gfx], + x, + y - yOff, + SUB_TYPE.BACK_WALL, + entry.lowerTerrain, + ); } else { - newTile = gfxMapper.getGfx(GFX_TYPE.INVISIBLE_WALL, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + GFX_TYPE.INVISIBLE_WALL, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); } } else { - newTile = gfxMapper.getGfx(entry.gfx, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); - if (!gfxMapper.hasShadowSide(entry.lowerTerrain) && SHADOW_CORNER_EXCEPTION[entry.gfx]) { - const upperEntry = this.data[y - 1] && this.data[y - 1][x]; - if (upperEntry && SHADOW_CORNER_EXCEPTION[entry.gfx].test === upperEntry.gfx) { - newTile = gfxMapper.getGfx(SHADOW_CORNER_EXCEPTION[entry.gfx].set, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + entry.gfx, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); + if ( + !gfxMapper.hasShadowSide(entry.lowerTerrain) && + SHADOW_CORNER_EXCEPTION[entry.gfx] + ) { + const upperEntry = + this.data[y - 1] && this.data[y - 1][x]; + if ( + upperEntry && + SHADOW_CORNER_EXCEPTION[entry.gfx].test === + upperEntry.gfx + ) { + newTile = gfxMapper.getGfx( + SHADOW_CORNER_EXCEPTION[entry.gfx].set, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); } } } } else { - newTile = gfxMapper.getGfx(GFX_TYPE.FILL, x, y - yOff, null, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + GFX_TYPE.FILL, + x, + y - yOff, + null, + entry.lowerTerrain, + ); } this.setLayerTile(layer, x, y - yOff, newTile); } else { @@ -423,12 +636,31 @@ export class HeightMapService { continue; } let newTile = 0; - if (gfxMapper.hasShadow() && details.level === Globals.map.masterLevel && entry.level > levelIdx) { - - if (doShadow && entry.lowerLevel && entry.lowerLevel < masterLevelIdx) { - newTile = gfxMapper.getGfx(entry.gfx, x, y - yOff, SUB_TYPE.DARK_WALL, entry.lowerTerrain); + if ( + gfxMapper.hasShadow() && + details.level === Globals.map.masterLevel && + entry.level > levelIdx + ) { + if ( + doShadow && + entry.lowerLevel && + entry.lowerLevel < masterLevelIdx + ) { + newTile = gfxMapper.getGfx( + entry.gfx, + x, + y - yOff, + SUB_TYPE.DARK_WALL, + entry.lowerTerrain, + ); } else { - newTile = gfxMapper.getGfx(GFX_TYPE.FILL, x, y - yOff, SUB_TYPE.SHADOW, entry.lowerTerrain); + newTile = gfxMapper.getGfx( + GFX_TYPE.FILL, + x, + y - yOff, + SUB_TYPE.SHADOW, + entry.lowerTerrain, + ); } } this.setLayerTile(layer, x, y - yOff, newTile); @@ -436,16 +668,17 @@ export class HeightMapService { } } } - + private applyOnCollision(layer: CCMapLayer, forceAll: boolean) { const details = layer.details; const levels = Globals.map.levels; const currentLevel = levels[details.level]; const masterLevel = levels[Globals.map.masterLevel]; - const yOff = (currentLevel.height - masterLevel.height) / Globals.TILE_SIZE; + const yOff = + (currentLevel.height - masterLevel.height) / Globals.TILE_SIZE; const levelIdx = details.level * 1 + 1; - const belowMaster = (details.level <= Globals.map.masterLevel); - const smallerMaster = (details.level < Globals.map.masterLevel); + const belowMaster = details.level <= Globals.map.masterLevel; + const smallerMaster = details.level < Globals.map.masterLevel; for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { if (!forceAll && !this.hasTileChanged(x, y)) { @@ -457,7 +690,7 @@ export class HeightMapService { } let newTile = 0; if (entry.level < levelIdx) { - newTile = (belowMaster ? HOLE_MAP[FILL_TYPE.SQUARE] : 0); + newTile = belowMaster ? HOLE_MAP[FILL_TYPE.SQUARE] : 0; } else if (entry.fill === FILL_TYPE.SQUARE) { if (!smallerMaster && entry.level > levelIdx) { newTile = BLOCK_MAP[entry.fill]; @@ -467,7 +700,9 @@ export class HeightMapService { newTile = BLOCK_MAP[FILL_TYPE.SQUARE]; } else if (entry.level > levelIdx) { if (entry.lowerLevel < levelIdx && belowMaster) { - newTile = (smallerMaster ? HOLE_MAP[entry.fill] : HOLE_BLOCK_MAP[entry.fill]); + newTile = smallerMaster + ? HOLE_MAP[entry.fill] + : HOLE_BLOCK_MAP[entry.fill]; } else if (!smallerMaster) { newTile = BLOCK_MAP[entry.fill]; } @@ -479,7 +714,7 @@ export class HeightMapService { } } } - + private hasTileAreaChanged(x: number, y: number) { if (this.hasTileChanged(x, y)) { return true; @@ -493,7 +728,7 @@ export class HeightMapService { } return false; } - + private hasTileLineChanged(x: number, y: number, yAdd: number) { let i = yAdd + 1; while (i--) { @@ -503,7 +738,7 @@ export class HeightMapService { } return false; } - + private hasTileLineShadowChanged(x: number, y: number) { const ySub = 10; let i = ySub + 1; @@ -515,10 +750,15 @@ export class HeightMapService { } const entry = this.data[y + i][x]; if (!entry) { - throw new Error('entry should be defined. x: ' + x + ' y: ' + (y + 1)); + throw new Error( + 'entry should be defined. x: ' + x + ' y: ' + (y + 1), + ); } if (entry.level > levels.length) { - let height = this.getLevelDistance(Globals.map.masterLevel + 1, levels.length); + let height = this.getLevelDistance( + Globals.map.masterLevel + 1, + levels.length, + ); const delta = entry.level - levels.length; height += delta * 2; if (i <= height) { @@ -529,7 +769,7 @@ export class HeightMapService { } return false; } - + private hasTileChanged(x: number, y: number) { if (!this.lastData) { return true; @@ -542,13 +782,23 @@ export class HeightMapService { if (!lastEntry || !newEntry) { return true; } - return lastEntry.level !== newEntry.level || lastEntry.fill !== newEntry.fill - || lastEntry.gfx !== newEntry.gfx || lastEntry.lowerLevel !== newEntry.lowerLevel - || lastEntry.terrain !== newEntry.terrain || lastEntry.lowerTerrain !== newEntry.lowerTerrain - || lastEntry.terrainBorder !== newEntry.terrainBorder; + return ( + lastEntry.level !== newEntry.level || + lastEntry.fill !== newEntry.fill || + lastEntry.gfx !== newEntry.gfx || + lastEntry.lowerLevel !== newEntry.lowerLevel || + lastEntry.terrain !== newEntry.terrain || + lastEntry.lowerTerrain !== newEntry.lowerTerrain || + lastEntry.terrainBorder !== newEntry.terrainBorder + ); } - - private setLayerTile(layer: CCMapLayer, x: number, y: number, tileValue: number) { + + private setLayerTile( + layer: CCMapLayer, + x: number, + y: number, + tileValue: number, + ) { if (y < 0 || y >= this.height) { return; } @@ -560,10 +810,10 @@ export class HeightMapService { if (oldValue !== tileValue) { customPutTileAt(tileValue, x, y, phaserLayer.layer); } - + this.autotile.drawTile(layer, x, y, tileValue); } - + private getRoundTileReplace(x: number, y: number, level: number) { const n = this.getOtherLevel(x, y, level, CHECK_DIR.NORTH), e = this.getOtherLevel(x, y, level, CHECK_DIR.EAST), @@ -581,16 +831,21 @@ export class HeightMapService { return FILL_TYPE.SQUARE; } } - + private setSquareGfx(x: number, y: number, entry: TileData) { const level = entry.level; - entry.gfx = null; - for (let i = 0; i < SQUARE_CORNER_CHECK.length; ++i) { - const sqrCheck = SQUARE_CORNER_CHECK[i]; - const check1 = CHECK_DIR[sqrCheck.dir1], check2 = CHECK_DIR[sqrCheck.dir2]; + entry.gfx = null as any; + for (const sqrCheck of SQUARE_CORNER_CHECK) { + const check1 = CHECK_DIR[sqrCheck.dir1], + check2 = CHECK_DIR[sqrCheck.dir2]; const otherLevel1 = this.getOtherLevel(x, y, level, check1); const otherLevel2 = this.getOtherLevel(x, y, level, check2); - if (otherLevel1 && otherLevel2 && otherLevel1 < level && otherLevel2 < level) { + if ( + otherLevel1 && + otherLevel2 && + otherLevel1 < level && + otherLevel2 < level + ) { entry.lowerLevel = otherLevel1; entry.lowerTerrain = this.getTerrain(x, y, check1) || 0; entry.gfx = sqrCheck.gfx; @@ -598,15 +853,21 @@ export class HeightMapService { return; } } - - for (let i = 0; i < CHECK_ITERATE.length; ++i) { - const check = CHECK_DIR[CHECK_ITERATE[i]]; + + for (const checkIterate of CHECK_ITERATE) { + const check = CHECK_DIR[checkIterate]; const otherLevel = this.getOtherLevel(x, y, level, check); if (otherLevel && otherLevel < level) { entry.lowerLevel = otherLevel; entry.lowerTerrain = this.getTerrain(x, y, check) || 0; entry.gfx = check.gfx; - entry.terrainBorder = this.getTerrainBorder(x, y, check, entry.terrain, level); + entry.terrainBorder = this.getTerrainBorder( + x, + y, + check, + entry.terrain, + level, + ); return; } } @@ -614,12 +875,13 @@ export class HeightMapService { entry.gfx = GFX_TYPE.FILL; } } - + private setDiagonalGfx(x: number, y: number, entry: TileData) { const checks = SECOND_LEVEL_CHECK[entry.fill]; let i = checks.length; - - const nLevels = [], nTerrains = []; + + const nLevels = [], + nTerrains = []; while (i--) { nLevels[i] = this.getOtherLevel(x, y, entry.level, checks[i]); nTerrains[i] = this.getTerrain(x, y, checks[i]) || 0; @@ -649,7 +911,10 @@ export class HeightMapService { } } if (lowerLevel > entry.level) { - entry.fill = entry.fill > 3 ? entry.fill - 2 : entry.fill + 2; + entry.fill = + entry.fill > FILL_TYPE.SOUTHEAST + ? entry.fill - 2 + : entry.fill + 2; entry.lowerLevel = entry.level; entry.lowerTerrain = entry.terrain; entry.level = lowerLevel; @@ -660,25 +925,26 @@ export class HeightMapService { } entry.gfx = DIAG_GFX[entry.fill]; } - + private getOtherLevel(x: number, y: number, level: number, dir: CheckDir) { y += dir.dy; x += dir.dx; const entry = this.data[y] && this.data[y][x]; - + if (!entry) { return 0; } - - const isBlockType = (entry.fill === dir.blockType1 || entry.fill === dir.blockType2); - + + const isBlockType = + entry.fill === dir.blockType1 || entry.fill === dir.blockType2; + if (entry.level === level) { return isBlockType ? entry.level : 0; } else { return !isBlockType ? entry.level : 0; } } - + private getTerrain(x: number, y: number, dir: CheckDir) { y += dir.dy; x += dir.dx; @@ -688,8 +954,13 @@ export class HeightMapService { } return entry.terrain; } - - private getOtherTerrain(x: number, y: number, dir: { dx: number, dy: number }, level: number) { + + private getOtherTerrain( + x: number, + y: number, + dir: { dx: number; dy: number }, + level: number, + ) { y += dir.dy; x += dir.dx; const entry = this.data[y] && this.data[y][x]; @@ -701,8 +972,14 @@ export class HeightMapService { } return entry.terrain; } - - private getTerrainBorder(x: number, y: number, dir: CheckDir, terrain: number, level: number) { + + private getTerrainBorder( + x: number, + y: number, + dir: CheckDir, + terrain: number, + level: number, + ) { if (!terrain) { return -1; } @@ -722,5 +999,4 @@ export class HeightMapService { } return -1; } - } diff --git a/webapp/src/app/services/height-map/heightmap.constants.ts b/webapp/src/app/services/height-map/heightmap.constants.ts index b1ca06cd..15c80cf2 100644 --- a/webapp/src/app/services/height-map/heightmap.constants.ts +++ b/webapp/src/app/services/height-map/heightmap.constants.ts @@ -1,4 +1,3 @@ - export interface WallLink { wall: GFX_TYPE; base: GFX_TYPE; @@ -36,54 +35,54 @@ export enum FILL_TYPE { NORTHEAST, SOUTHEAST, SOUTHWEST, - NORTHWEST + NORTHWEST, } export enum GFX_TYPE { FILL = 'FILL', - + DIAGONAL_NE = 'DIAGONAL_NE', DIAGONAL_SE = 'DIAGONAL_SE', DIAGONAL_SW = 'DIAGONAL_SW', DIAGONAL_NW = 'DIAGONAL_NW', - + SQUARE_NE = 'SQUARE_NE', SQUARE_SE = 'SQUARE_SE', SQUARE_SW = 'SQUARE_SW', SQUARE_NW = 'SQUARE_NW', - + NORTH = 'NORTH', EAST = 'EAST', SOUTH = 'SOUTH', WEST = 'WEST', - + CORNER_NE = 'CORNER_NE', CORNER_SE = 'CORNER_SE', CORNER_SW = 'CORNER_SW', CORNER_NW = 'CORNER_NW', - + WALL_SOUTH = 'WALL_SOUTH', WALL_SOUTH_BASE = 'WALL_SOUTH_BASE', - + WALL_SE = 'WALL_SE', WALL_SE_BASE = 'WALL_SE_BASE', - + WALL_SW = 'WALL_SW', WALL_SW_BASE = 'WALL_SW_BASE', - + WALL_SQR_SW = 'WALL_SQR_SW', WALL_SQR_SW_BASE = 'WALL_SQR_SW_BASE', - + WALL_SQR_SE = 'WALL_SQR_SE', WALL_SQR_SE_BASE = 'WALL_SQR_SE_BASE', - + WALL_END_WEST = 'WALL_END_WEST', WALL_END_WEST_BASE = 'WALL_END_WEST_BASE', - + WALL_END_EAST = 'WALL_END_EAST', WALL_END_EAST_BASE = 'WALL_END_EAST_BASE', - - INVISIBLE_WALL = 'INVISIBLE_WALL' + + INVISIBLE_WALL = 'INVISIBLE_WALL', } export enum SUB_TYPE { @@ -91,52 +90,75 @@ export enum SUB_TYPE { CHASM = 'CHASM', CHASM_FLOOR = 'CHASM_FLOOR', DARK_WALL = 'DARK_WALL', - BACK_WALL = 'BACK_WALL' + BACK_WALL = 'BACK_WALL', } -export const WALL_LINK: { [key in GFX_TYPE]: WallLink } = {}; -WALL_LINK[GFX_TYPE.SOUTH] = {wall: GFX_TYPE.WALL_SOUTH, base: GFX_TYPE.WALL_SOUTH_BASE}; -WALL_LINK[GFX_TYPE.DIAGONAL_SW] = {wall: GFX_TYPE.WALL_SW, base: GFX_TYPE.WALL_SW_BASE}; -WALL_LINK[GFX_TYPE.DIAGONAL_SE] = {wall: GFX_TYPE.WALL_SE, base: GFX_TYPE.WALL_SE_BASE}; -WALL_LINK[GFX_TYPE.SQUARE_SW] = {wall: GFX_TYPE.WALL_SQR_SW, base: GFX_TYPE.WALL_SQR_SW_BASE}; -WALL_LINK[GFX_TYPE.SQUARE_SE] = {wall: GFX_TYPE.WALL_SQR_SE, base: GFX_TYPE.WALL_SQR_SE_BASE}; -WALL_LINK[GFX_TYPE.CORNER_SW] = {shadowOnly: true, wall: GFX_TYPE.WALL_END_WEST, base: GFX_TYPE.WALL_END_WEST_BASE}; -WALL_LINK[GFX_TYPE.CORNER_SE] = {shadowOnly: true, wall: GFX_TYPE.WALL_END_EAST, base: GFX_TYPE.WALL_END_EAST_BASE}; +export const WALL_LINK: Record = {} as any; +WALL_LINK[GFX_TYPE.SOUTH] = { + wall: GFX_TYPE.WALL_SOUTH, + base: GFX_TYPE.WALL_SOUTH_BASE, +}; +WALL_LINK[GFX_TYPE.DIAGONAL_SW] = { + wall: GFX_TYPE.WALL_SW, + base: GFX_TYPE.WALL_SW_BASE, +}; +WALL_LINK[GFX_TYPE.DIAGONAL_SE] = { + wall: GFX_TYPE.WALL_SE, + base: GFX_TYPE.WALL_SE_BASE, +}; +WALL_LINK[GFX_TYPE.SQUARE_SW] = { + wall: GFX_TYPE.WALL_SQR_SW, + base: GFX_TYPE.WALL_SQR_SW_BASE, +}; +WALL_LINK[GFX_TYPE.SQUARE_SE] = { + wall: GFX_TYPE.WALL_SQR_SE, + base: GFX_TYPE.WALL_SQR_SE_BASE, +}; +WALL_LINK[GFX_TYPE.CORNER_SW] = { + shadowOnly: true, + wall: GFX_TYPE.WALL_END_WEST, + base: GFX_TYPE.WALL_END_WEST_BASE, +}; +WALL_LINK[GFX_TYPE.CORNER_SE] = { + shadowOnly: true, + wall: GFX_TYPE.WALL_END_EAST, + base: GFX_TYPE.WALL_END_EAST_BASE, +}; WALL_LINK[GFX_TYPE.NORTH] = { shadowOnly: true, toMaster: true, deltaY: -1, wall: GFX_TYPE.WALL_SOUTH, - base: GFX_TYPE.WALL_SOUTH_BASE + base: GFX_TYPE.WALL_SOUTH_BASE, }; WALL_LINK[GFX_TYPE.DIAGONAL_NE] = { shadowOnly: true, toMaster: true, wall: GFX_TYPE.WALL_SW, - base: GFX_TYPE.WALL_SW_BASE + base: GFX_TYPE.WALL_SW_BASE, }; WALL_LINK[GFX_TYPE.DIAGONAL_NW] = { shadowOnly: true, toMaster: true, wall: GFX_TYPE.WALL_SE, - base: GFX_TYPE.WALL_SE_BASE + base: GFX_TYPE.WALL_SE_BASE, }; WALL_LINK[GFX_TYPE.SQUARE_NE] = { shadowOnly: true, toMaster: true, deltaY: -1, wall: GFX_TYPE.WALL_SOUTH, - base: GFX_TYPE.WALL_SOUTH_BASE + base: GFX_TYPE.WALL_SOUTH_BASE, }; WALL_LINK[GFX_TYPE.SQUARE_NW] = { shadowOnly: true, toMaster: true, deltaY: -1, wall: GFX_TYPE.WALL_SOUTH, - base: GFX_TYPE.WALL_SOUTH_BASE + base: GFX_TYPE.WALL_SOUTH_BASE, }; -export const DIAG_GFX: { [key in FILL_TYPE]: GFX_TYPE } = {}; +export const DIAG_GFX: Record = {} as any; DIAG_GFX[FILL_TYPE.NORTHEAST] = GFX_TYPE.DIAGONAL_NE; DIAG_GFX[FILL_TYPE.SOUTHEAST] = GFX_TYPE.DIAGONAL_SE; DIAG_GFX[FILL_TYPE.SOUTHWEST] = GFX_TYPE.DIAGONAL_SW; @@ -147,13 +169,12 @@ export const SQUARE_CORNER_CHECK: { dir2: keyof AllCheckDirs; gfx: GFX_TYPE; }[] = [ - {dir1: 'NORTH', dir2: 'EAST', gfx: GFX_TYPE.SQUARE_NE}, - {dir1: 'NORTH', dir2: 'WEST', gfx: GFX_TYPE.SQUARE_NW}, - {dir1: 'SOUTH', dir2: 'EAST', gfx: GFX_TYPE.SQUARE_SE}, - {dir1: 'SOUTH', dir2: 'WEST', gfx: GFX_TYPE.SQUARE_SW} + { dir1: 'NORTH', dir2: 'EAST', gfx: GFX_TYPE.SQUARE_NE }, + { dir1: 'NORTH', dir2: 'WEST', gfx: GFX_TYPE.SQUARE_NW }, + { dir1: 'SOUTH', dir2: 'EAST', gfx: GFX_TYPE.SQUARE_SE }, + { dir1: 'SOUTH', dir2: 'WEST', gfx: GFX_TYPE.SQUARE_SW }, ]; - export const CHECK_DIR: AllCheckDirs = { NORTH: { dx: 0, @@ -161,7 +182,10 @@ export const CHECK_DIR: AllCheckDirs = { blockType1: FILL_TYPE.SOUTHEAST, blockType2: FILL_TYPE.SOUTHWEST, gfx: GFX_TYPE.NORTH, - terrainBorder: [{dx: -1, dy: 0}, {dx: 1, dy: 0}] + terrainBorder: [ + { dx: -1, dy: 0 }, + { dx: 1, dy: 0 }, + ], }, EAST: { dx: 1, @@ -169,7 +193,10 @@ export const CHECK_DIR: AllCheckDirs = { blockType1: FILL_TYPE.SOUTHWEST, blockType2: FILL_TYPE.NORTHWEST, gfx: GFX_TYPE.EAST, - terrainBorder: [{dx: 0, dy: -1}, {dx: 0, dy: 1}] + terrainBorder: [ + { dx: 0, dy: -1 }, + { dx: 0, dy: 1 }, + ], }, SOUTH: { dx: 0, @@ -177,7 +204,10 @@ export const CHECK_DIR: AllCheckDirs = { blockType1: FILL_TYPE.NORTHEAST, blockType2: FILL_TYPE.NORTHWEST, gfx: GFX_TYPE.SOUTH, - terrainBorder: [{dx: -1, dy: 0}, {dx: 1, dy: 0}] + terrainBorder: [ + { dx: -1, dy: 0 }, + { dx: 1, dy: 0 }, + ], }, WEST: { dx: -1, @@ -185,12 +215,39 @@ export const CHECK_DIR: AllCheckDirs = { blockType1: FILL_TYPE.NORTHEAST, blockType2: FILL_TYPE.SOUTHEAST, gfx: GFX_TYPE.WEST, - terrainBorder: [{dx: 0, dy: -1}, {dx: 0, dy: 1}] + terrainBorder: [ + { dx: 0, dy: -1 }, + { dx: 0, dy: 1 }, + ], + }, + NE: { + dx: 1, + dy: -1, + blockType1: FILL_TYPE.SOUTHWEST, + blockType2: FILL_TYPE.SOUTHWEST, + gfx: GFX_TYPE.CORNER_NE, + }, + SE: { + dx: 1, + dy: 1, + blockType1: FILL_TYPE.NORTHWEST, + blockType2: FILL_TYPE.NORTHWEST, + gfx: GFX_TYPE.CORNER_SE, + }, + SW: { + dx: -1, + dy: 1, + blockType1: FILL_TYPE.NORTHEAST, + blockType2: FILL_TYPE.NORTHEAST, + gfx: GFX_TYPE.CORNER_SW, + }, + NW: { + dx: -1, + dy: -1, + blockType1: FILL_TYPE.SOUTHEAST, + blockType2: FILL_TYPE.SOUTHEAST, + gfx: GFX_TYPE.CORNER_NW, }, - NE: {dx: 1, dy: -1, blockType1: FILL_TYPE.SOUTHWEST, blockType2: FILL_TYPE.SOUTHWEST, gfx: GFX_TYPE.CORNER_NE}, - SE: {dx: 1, dy: 1, blockType1: FILL_TYPE.NORTHWEST, blockType2: FILL_TYPE.NORTHWEST, gfx: GFX_TYPE.CORNER_SE}, - SW: {dx: -1, dy: 1, blockType1: FILL_TYPE.NORTHEAST, blockType2: FILL_TYPE.NORTHEAST, gfx: GFX_TYPE.CORNER_SW}, - NW: {dx: -1, dy: -1, blockType1: FILL_TYPE.SOUTHEAST, blockType2: FILL_TYPE.SOUTHEAST, gfx: GFX_TYPE.CORNER_NW} }; export const CHECK_ITERATE: (keyof AllCheckDirs)[] = []; @@ -200,20 +257,48 @@ for (const name in CHECK_DIR) { } } -export const SECOND_LEVEL_CHECK: { [key in FILL_TYPE]: CheckDir[] } = {}; -SECOND_LEVEL_CHECK[FILL_TYPE.NORTHEAST] = [CHECK_DIR.NORTH, CHECK_DIR.EAST, CHECK_DIR.NE]; -SECOND_LEVEL_CHECK[FILL_TYPE.SOUTHEAST] = [CHECK_DIR.SOUTH, CHECK_DIR.EAST, CHECK_DIR.SE]; -SECOND_LEVEL_CHECK[FILL_TYPE.SOUTHWEST] = [CHECK_DIR.SOUTH, CHECK_DIR.WEST, CHECK_DIR.SW]; -SECOND_LEVEL_CHECK[FILL_TYPE.NORTHWEST] = [CHECK_DIR.NORTH, CHECK_DIR.WEST, CHECK_DIR.NW]; - +export const SECOND_LEVEL_CHECK: Record = {} as any; +SECOND_LEVEL_CHECK[FILL_TYPE.NORTHEAST] = [ + CHECK_DIR.NORTH, + CHECK_DIR.EAST, + CHECK_DIR.NE, +]; +SECOND_LEVEL_CHECK[FILL_TYPE.SOUTHEAST] = [ + CHECK_DIR.SOUTH, + CHECK_DIR.EAST, + CHECK_DIR.SE, +]; +SECOND_LEVEL_CHECK[FILL_TYPE.SOUTHWEST] = [ + CHECK_DIR.SOUTH, + CHECK_DIR.WEST, + CHECK_DIR.SW, +]; +SECOND_LEVEL_CHECK[FILL_TYPE.NORTHWEST] = [ + CHECK_DIR.NORTH, + CHECK_DIR.WEST, + CHECK_DIR.NW, +]; -export const SHADOW_CORNER_EXCEPTION: { [key in GFX_TYPE]: { test: GFX_TYPE, set: GFX_TYPE } } = {}; -SHADOW_CORNER_EXCEPTION[GFX_TYPE.CORNER_NE] = {test: GFX_TYPE.EAST, set: GFX_TYPE.FILL}; -SHADOW_CORNER_EXCEPTION[GFX_TYPE.CORNER_NW] = {test: GFX_TYPE.WEST, set: GFX_TYPE.FILL}; -SHADOW_CORNER_EXCEPTION[GFX_TYPE.EAST] = {test: GFX_TYPE.DIAGONAL_NE, set: GFX_TYPE.CORNER_NE}; -SHADOW_CORNER_EXCEPTION[GFX_TYPE.WEST] = {test: GFX_TYPE.DIAGONAL_NW, set: GFX_TYPE.CORNER_NW}; +export const SHADOW_CORNER_EXCEPTION: Record< + GFX_TYPE, + { test: GFX_TYPE; set: GFX_TYPE } +> = {} as any; +SHADOW_CORNER_EXCEPTION[GFX_TYPE.CORNER_NE] = { + test: GFX_TYPE.EAST, + set: GFX_TYPE.FILL, +}; +SHADOW_CORNER_EXCEPTION[GFX_TYPE.CORNER_NW] = { + test: GFX_TYPE.WEST, + set: GFX_TYPE.FILL, +}; +SHADOW_CORNER_EXCEPTION[GFX_TYPE.EAST] = { + test: GFX_TYPE.DIAGONAL_NE, + set: GFX_TYPE.CORNER_NE, +}; +SHADOW_CORNER_EXCEPTION[GFX_TYPE.WEST] = { + test: GFX_TYPE.DIAGONAL_NW, + set: GFX_TYPE.CORNER_NW, +}; export const FILL_COUNT = 8; export const LEVEL_COUNT = 16; - - diff --git a/webapp/src/app/services/http-client.service.ts b/webapp/src/app/services/http-client.service.ts index c063bfc3..151031af 100644 --- a/webapp/src/app/services/http-client.service.ts +++ b/webapp/src/app/services/http-client.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { api } from 'cc-map-editor-common'; import { lastValueFrom, Observable } from 'rxjs'; import { FileInfos } from '../models/file-infos'; @@ -8,19 +8,15 @@ import { Globals } from './globals'; import { SettingsService } from './settings.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class HttpClientService { + private http = inject(HttpClient); + private electron = inject(ElectronService); + private settingsService = inject(SettingsService); private readonly fileName = 'config.json'; - constructor( - private http: HttpClient, - private electron: ElectronService, - private settingsService: SettingsService - ) { - } - getAllFiles(): Observable { return this.request('api/allFiles', api.getAllFiles); } @@ -30,40 +26,70 @@ export class HttpClientService { } getMaps(): Observable { - const includeVanillaMaps = this.settingsService.getSettings().includeVanillaMaps; - return this.request(`api/allMaps?includeVanillaMaps=${includeVanillaMaps}`, api.getAllMaps, includeVanillaMaps); + const includeVanillaMaps = + this.settingsService.getSettings().includeVanillaMaps; + return this.request( + `api/allMaps?includeVanillaMaps=${includeVanillaMaps}`, + api.getAllMaps, + includeVanillaMaps, + ); } getProps(): Observable { const folder = 'data/props/'; const extension = 'json'; - return this.request(`api/allFilesInFolder?folder=${folder}&extension=${extension}`, api.getAllFilesInFolder, folder, extension); + return this.request( + `api/allFilesInFolder?folder=${folder}&extension=${extension}`, + api.getAllFilesInFolder, + folder, + extension, + ); } getScalableProps(): Observable { const folder = 'data/scale-props/'; const extension = 'json'; - return this.request(`api/allFilesInFolder?folder=${folder}&extension=${extension}`, api.getAllFilesInFolder, folder, extension); + return this.request( + `api/allFilesInFolder?folder=${folder}&extension=${extension}`, + api.getAllFilesInFolder, + folder, + extension, + ); } getEnemies(): Observable { const folder = 'data/enemies/'; const extension = 'json'; - return this.request(`api/allFilesInFolder?folder=${folder}&extension=${extension}`, api.getAllFilesInFolder, folder, extension); + return this.request( + `api/allFilesInFolder?folder=${folder}&extension=${extension}`, + api.getAllFilesInFolder, + folder, + extension, + ); } getCharacters(): Observable { const folder = 'data/characters/'; const extension = 'json'; - return this.request(`api/allFilesInFolder?folder=${folder}&extension=${extension}`, api.getAllFilesInFolder, folder, extension); + return this.request( + `api/allFilesInFolder?folder=${folder}&extension=${extension}`, + api.getAllFilesInFolder, + folder, + extension, + ); } - getMods(): Observable<{ id: string, displayName: string }[]> { + getMods(): Observable<{ id: string; displayName: string }[]> { return this.request('api/allMods', api.getAllMods); } - + getModMapEditorConfigs() { - return lastValueFrom(this.request('api/allModMapEditorConfigs', api.getAllModMapEditorConfigs)); + return lastValueFrom( + this.request( + 'api/allModMapEditorConfigs', + api.getAllModMapEditorConfigs, + ), + ); } getAssetsFile(path: string): Observable { @@ -76,7 +102,9 @@ export class HttpClientService { resolveFile(path: string): Observable { if (!Globals.isElectron) { - return this.http.post(Globals.URL + 'api/resolve', { path }); + return this.http.post(Globals.URL + 'api/resolve', { + path, + }); } const cc = this.electron.getAssetsPath(); return this.toObservable(api.resolve(cc, path)); @@ -88,7 +116,9 @@ export class HttpClientService { return this.http.post(Globals.URL + 'api/saveFile', file); } - return this.toObservable(api.saveFile(this.electron.getAssetsPath(), file)); + return this.toObservable( + api.saveFile(this.electron.getAssetsPath(), file), + ); } /** @@ -97,7 +127,11 @@ export class HttpClientService { * @param common Direct call to common * @param args Additional arguments for common */ - private request(url: string, common: (path: string, ...args: A) => Promise, ...args: A): Observable { + private request( + url: string, + common: (path: string, ...args: A) => Promise, + ...args: A + ): Observable { if (!Globals.isElectron) { return this.http.get(Globals.URL + url); } @@ -106,10 +140,10 @@ export class HttpClientService { } private toObservable(promise: Promise): Observable { - return new Observable(subsriber => { + return new Observable((subsriber) => { promise - .then(value => subsriber.next(value)) - .catch(err => subsriber.error(err)) + .then((value) => subsriber.next(value)) + .catch((err) => subsriber.error(err)) .finally(() => subsriber.complete()); }); } diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts index 05335ac4..6c49863b 100644 --- a/webapp/src/app/services/json-loader.service.ts +++ b/webapp/src/app/services/json-loader.service.ts @@ -1,30 +1,29 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClientService } from './http-client.service'; import { HttpClient } from '@angular/common/http'; import { lastValueFrom } from 'rxjs'; import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class JsonLoaderService { - + private http = inject(HttpClientService); + private angularHttp = inject(HttpClient); + private snackbar = inject(MatSnackBar); + private readonly initialized?: Promise; - private configs = new Map; - + private configs = new Map(); + private cache: Record = {}; - - constructor( - private http: HttpClientService, - private angularHttp: HttpClient, - private snackbar: MatSnackBar, - ) { + + constructor() { this.initialized = this.init(); } - + async init() { const configs = await this.http.getModMapEditorConfigs(); - + for (const config of configs) { let parsedFile: unknown; try { @@ -34,7 +33,7 @@ export class JsonLoaderService { this.snackbar.open( `Failed to parse mod config: ${config.mod}/map-editor/${config.filename}`, 'close', - {panelClass: 'snackbar-error'} + { panelClass: 'snackbar-error' }, ); continue; } @@ -43,18 +42,17 @@ export class JsonLoaderService { configs.push(parsedFile); } } - + async loadJson(file: string): Promise { await this.initialized; - const json = await lastValueFrom(this.angularHttp.get(`./assets/${file}`)); - const modJson = (this.configs.get(file) ?? []) as T[]; - - return [ - json as T, - ...modJson - ]; + const json = await lastValueFrom( + this.angularHttp.get(`./assets/${file}`), + ); + const modJson = (this.configs.get(file) ?? []) as T[]; + + return [json as T, ...modJson]; } - + async loadJsonMerged(file: string): Promise { const cached = this.cache[file] as T | undefined; if (cached) { @@ -66,7 +64,7 @@ export class JsonLoaderService { this.cache[file] = base; return base; } - + loadJsonMergedSync(file: string): T { const cached = this.cache[file] as T | undefined; if (cached) { diff --git a/webapp/src/app/services/map-loader.service.ts b/webapp/src/app/services/map-loader.service.ts index c9d7046b..b1eaf044 100644 --- a/webapp/src/app/services/map-loader.service.ts +++ b/webapp/src/app/services/map-loader.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { BehaviorSubject, Observable } from 'rxjs'; import { CrossCodeMap } from '../models/cross-code-map'; @@ -10,39 +10,35 @@ import { CCMap } from './phaser/tilemap/cc-map'; import { CCMapLayer } from './phaser/tilemap/cc-map-layer'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MapLoaderService { - + private snackBar = inject(MatSnackBar); + private http = inject(HttpClientService); + private electron = inject(ElectronService); + tileMap = new BehaviorSubject(undefined); selectedLayer = new BehaviorSubject(undefined); - + private _map = new BehaviorSubject(undefined as any); get map(): Observable { return this._map.asObservable(); } - - constructor( - private snackBar: MatSnackBar, - private http: HttpClientService, - private electron: ElectronService - ) { - } - + loadMap(event: Event) { const files: FileList = (event.target as HTMLInputElement).files!; if (files.length === 0) { return; } - + const file = files[0]; const reader = new FileReader(); - reader.onload = (e: any) => { + reader.onload = async (e: any) => { try { - const map = JSON.parse(e.target.result); + const map = JSON.parse(e.target.result) as CrossCodeMap; let path: string | undefined; if (file && Globals.isElectron) { - const {webUtils} = require('electron'); + const { webUtils } = await import('electron'); const filePath = webUtils.getPathForFile(file); path = filePath.split(this.electron.getAssetsPath())[1]; } @@ -51,15 +47,15 @@ export class MapLoaderService { console.error(e); this.snackBar.open('Error: ' + e.message, undefined, { duration: 2500, - panelClass: 'snackbar-error' + panelClass: 'snackbar-error', }); return; } }; - + reader.readAsText(file); } - + loadRawMap(map: CrossCodeMap, name?: string, path?: string) { if (!map.mapHeight) { throw new Error('Invalid map'); @@ -68,12 +64,16 @@ export class MapLoaderService { map.path = path; this._map.next(map); } - + loadMapByName(name: string) { - const path = PathResolver.convertToPath(BasePath.MAPS, name, FileExtension.JSON); + const path = PathResolver.convertToPath( + BasePath.MAPS, + name, + FileExtension.JSON, + ); const filename = PathResolver.convertToFileName(name); - - this.http.getAssetsFile(path).subscribe(map => { + + this.http.getAssetsFile(path).subscribe((map) => { this.loadRawMap(map, filename, path); }); } diff --git a/webapp/src/app/services/path-resolver.ts b/webapp/src/app/services/path-resolver.ts index 49ec5efc..363f38f9 100644 --- a/webapp/src/app/services/path-resolver.ts +++ b/webapp/src/app/services/path-resolver.ts @@ -1,24 +1,27 @@ export enum BasePath { MAPS = 'data/maps/', ENEMIES = 'data/enemies/', - ANIMATIONS = 'data/animations/' + ANIMATIONS = 'data/animations/', } export enum FileExtension { NONE = '', PNG = '.png', JSON = '.json', - OGG = '.ogg' + OGG = '.ogg', } export class PathResolver { - /** * @param base The path in relation to assets. * @param name The name of the asset in relation to base using '.' as separator. * @param extension */ - public static convertToPath(base: BasePath, name: string, extension: FileExtension) { + public static convertToPath( + base: BasePath, + name: string, + extension: FileExtension, + ) { return base + name.replace(/\./g, '/') + extension; } diff --git a/webapp/src/app/services/phaser/BaseTileDrawer.ts b/webapp/src/app/services/phaser/BaseTileDrawer.ts index c4fa8429..dde1bf3a 100644 --- a/webapp/src/app/services/phaser/BaseTileDrawer.ts +++ b/webapp/src/app/services/phaser/BaseTileDrawer.ts @@ -13,14 +13,14 @@ export class BaseTileDrawer extends BaseObject { private rightPointerDown = false; private rightClickStart?: Point; private rightClickEnd?: Point; - + private selection?: Phaser.GameObjects.Container; - + private previewTileMap!: Phaser.Tilemaps.Tilemap; private previewLayer?: Phaser.Tilemaps.TilemapLayer; - + private layer?: CCMapLayer; - + /** * @param scene * @param leftClick @@ -29,34 +29,45 @@ export class BaseTileDrawer extends BaseObject { constructor( scene: Phaser.Scene, private leftClick?: boolean, - private container?: Phaser.GameObjects.Container + private container?: Phaser.GameObjects.Container, ) { super(scene, 'baseTileDrawer'); } - + protected override init(): void { - this.previewTileMap = this.scene.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); + this.previewTileMap = this.scene.add.tilemap( + undefined, + Globals.TILE_SIZE, + Globals.TILE_SIZE, + ); } - + preUpdate(): void { if (!this.layer) { return; } const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); - + const p = Helper.worldToTile( + pointer.worldX - this.layer.x, + pointer.worldY - this.layer.y, + ); + // render selection border if (this.rightClickStart) { Helper.clampToBounds(this.layer, p); - - if (this.rightClickEnd && this.rightClickEnd.x === p.x && this.rightClickEnd.y === p.y) { + + if ( + this.rightClickEnd && + this.rightClickEnd.x === p.x && + this.rightClickEnd.y === p.y + ) { // shortcut to avoid redrawing rectangle every frame return; } - + this.rightClickEnd = p; const diff = Vec2.sub(p, this.rightClickStart, true); - const start = {x: 0, y: 0}; + const start = { x: 0, y: 0 }; if (diff.x >= 0) { diff.x++; } else { @@ -69,158 +80,200 @@ export class BaseTileDrawer extends BaseObject { start.y = 1; diff.y--; } - + if (!this.container) { Vec2.add(start, this.rightClickStart); } - this.drawRect(diff.x, diff.y, start.x * Globals.TILE_SIZE, start.y * Globals.TILE_SIZE, true); + this.drawRect( + diff.x, + diff.y, + start.x * Globals.TILE_SIZE, + start.y * Globals.TILE_SIZE, + true, + ); return; } - - + // position tile drawer border to cursor if (this.container) { const container = this.container; container.x = pointer.worldX; container.y = pointer.worldY; - + if (container.x < this.layer.x) { container.x -= Globals.TILE_SIZE; } if (container.y < this.layer.y) { container.y -= Globals.TILE_SIZE; } - + container.x -= (container.x - this.layer.x) % Globals.TILE_SIZE; container.y -= (container.y - this.layer.y) % Globals.TILE_SIZE; - + if (this.previewLayer) { Vec2.assign(this.previewLayer, container); } } } - + protected override activate(): void { this.setVisibility(true); const pointerDown = (pointer: Phaser.Input.Pointer) => { - if (pointer.rightButtonDown() || this.leftClick && pointer.leftButtonDown()) { + if ( + pointer.rightButtonDown() || + (this.leftClick && pointer.leftButtonDown()) + ) { this.onMouseRightDown(); } }; - + const pointerUp = (pointer: Phaser.Input.Pointer) => { - if ((pointer.rightButtonReleased() || this.leftClick && pointer.leftButtonReleased()) && this.rightPointerDown) { + if ( + (pointer.rightButtonReleased() || + (this.leftClick && pointer.leftButtonReleased())) && + this.rightPointerDown + ) { this.onMouseRightUp(); } }; - this.addKeybinding({event: 'pointerdown', fun: pointerDown, emitter: this.scene.input}); - this.addKeybinding({event: 'pointerup', fun: pointerUp, emitter: this.scene.input}); - this.addKeybinding({event: 'pointerupoutside', fun: pointerUp, emitter: this.scene.input}); - - this.addSubscription(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => this.updateSelectedTiles(tiles))); + this.addKeybinding({ + event: 'pointerdown', + fun: pointerDown, + emitter: this.scene.input, + }); + this.addKeybinding({ + event: 'pointerup', + fun: pointerUp, + emitter: this.scene.input, + }); + this.addKeybinding({ + event: 'pointerupoutside', + fun: pointerUp, + emitter: this.scene.input, + }); + + this.addSubscription( + Globals.phaserEventsService.changeSelectedTiles.subscribe((tiles) => + this.updateSelectedTiles(tiles), + ), + ); } - + protected override deactivate(): void { this.setVisibility(false); } - + private setVisibility(selection: boolean, preview = selection) { this.selection?.setVisible(selection && !!this.layer); this.previewLayer?.setVisible(preview && !!this.layer); } - + public async setLayer(layer?: CCMapLayer) { this.layer = layer; if (!layer) { this.setVisibility(false); return; } - - const exists = await Helper.loadTexture(layer.details.tilesetName, this.scene); + + const exists = await Helper.loadTexture( + layer.details.tilesetName, + this.scene, + ); if (!exists) { this.setVisibility(true, false); return; } this.setVisibility(true); - - const tileset = this.previewTileMap.addTilesetImage('only', layer.details.tilesetName); + + const tileset = this.previewTileMap.addTilesetImage( + 'only', + layer.details.tilesetName, + ); if (tileset) { tileset.firstgid = 1; } - } - + public resetSelectedTiles() { - Globals.phaserEventsService.changeSelectedTiles.next([{id: 0, offset: {x: 0, y: 0}}]); + Globals.phaserEventsService.changeSelectedTiles.next([ + { id: 0, offset: { x: 0, y: 0 } }, + ]); } - - + private onMouseRightDown() { this.rightPointerDown = true; if (!this.layer) { return; } - + // only start tile copy when cursor in bounds const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); + const p = Helper.worldToTile( + pointer.worldX - this.layer.x, + pointer.worldY - this.layer.y, + ); if (!Helper.isInBounds(this.layer, p)) { return; } - + this.resetSelectedTiles(); this.rightClickStart = p; } - + private onMouseRightUp() { this.rightPointerDown = false; if (!this.layer) { return; } - + // cancel current selection when out of bounds const phaserLayer = this.layer.getPhaserLayer(); if (!this.rightClickStart || !this.rightClickEnd || !phaserLayer) { this.resetSelectedTiles(); return; } - + // select tiles const start = this.rightClickStart; const end = this.rightClickEnd; - + const smaller = { x: Math.min(start.x, end.x), - y: Math.min(start.y, end.y) + y: Math.min(start.y, end.y), }; - + const bigger = { x: Math.max(start.x, end.x), - y: Math.max(start.y, end.y) + y: Math.max(start.y, end.y), }; - + const width = bigger.x - smaller.x + 1; const height = bigger.y - smaller.y + 1; - - const tilesWithin = phaserLayer.getTilesWithin(smaller.x, smaller.y, width, height); - - const tiles: SelectedTile[] = tilesWithin.map(tile => ({ + + const tilesWithin = phaserLayer.getTilesWithin( + smaller.x, + smaller.y, + width, + height, + ); + + const tiles: SelectedTile[] = tilesWithin.map((tile) => ({ id: tile.index, - offset: Vec2.sub(tile, smaller, true) + offset: Vec2.sub(tile, smaller, true), })); Globals.phaserEventsService.changeSelectedTiles.next(tiles); - + this.rightClickStart = undefined; this.rightClickEnd = undefined; } - + private updateSelectedTiles(selected: SelectedTile[]) { this.selectedTiles = selected; this.renderPreview(); - + let x = 0; let y = 0; - selected.forEach(tile => { + selected.forEach((tile) => { const o = tile.offset; if (o.x > x) { x = o.x; @@ -229,40 +282,51 @@ export class BaseTileDrawer extends BaseObject { y = o.y; } }); - + if (this.container) { this.drawRect(x + 1, y + 1, 0, 0); return; } } - - public drawRect(width: number, height: number, x = 0, y = 0, renderSize = false) { + + public drawRect( + width: number, + height: number, + x = 0, + y = 0, + renderSize = false, + ) { if (this.selection) { this.selection.destroy(); } - + let textColor = 'rgba(0,0,0,0.6)'; let backgroundColor = 0xffffff; if (Globals.settingsService.getSettings().selectionBoxDark) { textColor = 'rgba(255,255,255,0.9)'; backgroundColor = 0x333333; } - + this.selection = this.scene.add.container(x, y); this.selection.setDepth(10); - - const rect = this.scene.add.rectangle(0, 0, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); + + const rect = this.scene.add.rectangle( + 0, + 0, + width * Globals.TILE_SIZE, + height * Globals.TILE_SIZE, + ); rect.setOrigin(0, 0); rect.setStrokeStyle(1, backgroundColor, this.container ? 0.6 : 0.9); - + this.selection.add(rect); this.container?.add(this.selection); - + if (!renderSize) { Globals.globalEventsService.updateTileSelectionSize.next(undefined); return; } - + const makeText = (pos: Point, val: number) => { const text = this.scene.add.text(pos.x, pos.y, Math.abs(val) + '', { font: '400 10px Roboto', @@ -270,44 +334,66 @@ export class BaseTileDrawer extends BaseObject { resolution: window.devicePixelRatio * 3, }); text.setOrigin(0.5, 0); - const background = this.scene.add.rectangle(pos.x, pos.y + 2, 14, 10, backgroundColor, 0.6); + const background = this.scene.add.rectangle( + pos.x, + pos.y + 2, + 14, + 10, + backgroundColor, + 0.6, + ); background.setOrigin(0.5, 0); - + this.selection?.add(background); this.selection?.add(text); }; - + if (Math.abs(width) >= 3) { - makeText({ - x: width * Globals.TILE_SIZE / 2, - y: (height > 0 ? 0 : height * Globals.TILE_SIZE) - 1 - }, width); + makeText( + { + x: (width * Globals.TILE_SIZE) / 2, + y: (height > 0 ? 0 : height * Globals.TILE_SIZE) - 1, + }, + width, + ); } - + if (Math.abs(height) >= 3) { - makeText({ - x: Globals.TILE_SIZE / 2 + (width > 0 ? 0 : width * Globals.TILE_SIZE), - y: (height - 1) * Globals.TILE_SIZE / 2, - }, height); + makeText( + { + x: + Globals.TILE_SIZE / 2 + + (width > 0 ? 0 : width * Globals.TILE_SIZE), + y: ((height - 1) * Globals.TILE_SIZE) / 2, + }, + height, + ); } - + Globals.globalEventsService.updateTileSelectionSize.next({ x: Math.abs(width), - y: Math.abs(height) + y: Math.abs(height), }); } - + private renderPreview() { if (!this.container) { return; } this.previewTileMap.removeAllLayers(); - const layer = this.previewTileMap.createBlankLayer('layer', 'only', 0, 0, 40, 40)!; - - this.selectedTiles.forEach(tile => { + const layer = this.previewTileMap.createBlankLayer( + 'layer', + 'only', + 0, + 0, + 40, + 40, + )!; + + this.selectedTiles.forEach((tile) => { customPutTileAt(tile.id, tile.offset.x, tile.offset.y, layer.layer); }); - + this.previewLayer = layer; this.previewLayer.depth = this.container.depth - 1; this.previewLayer.alpha = 0.6; diff --git a/webapp/src/app/services/phaser/base-object.ts b/webapp/src/app/services/phaser/base-object.ts index 991f88f7..26171678 100644 --- a/webapp/src/app/services/phaser/base-object.ts +++ b/webapp/src/app/services/phaser/base-object.ts @@ -4,40 +4,44 @@ import { PreUpdate } from './pre-update'; export interface KeyBinding { event: string; + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type fun: Function; emitter: Phaser.Events.EventEmitter; } -export abstract class BaseObject extends Phaser.GameObjects.GameObject implements PreUpdate { +export abstract class BaseObject + extends Phaser.GameObjects.GameObject + implements PreUpdate +{ private subs: Subscription[] = []; - + private keyBindings: KeyBinding[] = []; - + protected constructor(scene: Phaser.Scene, type: string, active = true) { super(scene, type); this.active = false; this.init(); - + if (active) { this.active = true; this.activate(); } } - + protected abstract init(): void; - + abstract preUpdate(time: number, delta: number): void; - + protected abstract activate(): void; - + protected abstract deactivate(): void; - + public override setActive(value: boolean): this { if (this.active === value) { return this; } super.setActive(value); - + if (value) { this.activateInternal(); } else { @@ -45,33 +49,32 @@ export abstract class BaseObject extends Phaser.GameObjects.GameObject implement } return this; } - + private deactivateInternal() { - this.keyBindings.forEach(binding => { + this.keyBindings.forEach((binding) => { binding.emitter.removeListener(binding.event, binding.fun); }); this.keyBindings = []; - this.subs.forEach(sub => sub.unsubscribe()); + this.subs.forEach((sub) => sub.unsubscribe()); this.subs = []; this.deactivate(); } - + private activateInternal() { this.activate(); } - - + protected addKeybinding(binding: KeyBinding) { if (!this.active) { - throw new Error('tried to add keybinding on gameobject that is not active'); + throw new Error( + 'tried to add keybinding on gameobject that is not active', + ); } this.keyBindings.push(binding); binding.emitter.addListener(binding.event, binding.fun); } - + protected addSubscription(sub: Subscription) { this.subs.push(sub); } - - } diff --git a/webapp/src/app/services/phaser/coords-reporter.ts b/webapp/src/app/services/phaser/coords-reporter.ts index e6cebea9..2db4a264 100644 --- a/webapp/src/app/services/phaser/coords-reporter.ts +++ b/webapp/src/app/services/phaser/coords-reporter.ts @@ -5,64 +5,67 @@ import { Globals } from '../globals'; import { BaseObject } from './base-object'; export class CoordsReporter extends BaseObject { - private rawCoords: Point = {x: 0, y: 0}; - + private rawCoords: Point = { x: 0, y: 0 }; + private sub!: Subscription; - + constructor(scene: Phaser.Scene, active = true) { super(scene, 'coordsReporter', active); } - + protected init() { combineLatest([ Globals.globalEventsService.currentView, - Globals.globalEventsService.is3D + Globals.globalEventsService.is3D, ]).subscribe(([view, is3d]) => { this.setActive(view === EditorView.Entities && !is3d); }); } - + protected activate() {} - + protected deactivate() { Globals.globalEventsService.updateCoords.next(undefined); } - + override destroy(fromScene?: boolean) { this.sub.unsubscribe(); super.destroy(fromScene); } - + private get coords(): Point { return { x: this.rawCoords.x, y: this.rawCoords.y + this.entityLevel, }; } - + private get isEntityMode() { - return Globals.globalEventsService.currentView.value === EditorView.Entities; + return ( + Globals.globalEventsService.currentView.value === + EditorView.Entities + ); } - + private get entityLevel() { const maybeLevel = Globals.globalEventsService.selectedEntity.value?.details.level; - + return maybeLevel ? Globals.map.levels[maybeLevel.level]?.height + maybeLevel.offset : 0; } - + preUpdate() { const pointer = this.scene.input.activePointer; - + const newRaw = { x: Math.floor(pointer.worldX), y: Math.floor(pointer.worldY), }; - + this.rawCoords = newRaw; - + // send event Globals.globalEventsService.updateCoords.next({ ...this.coords, diff --git a/webapp/src/app/services/phaser/entities/cc-entity.ts b/webapp/src/app/services/phaser/entities/cc-entity.ts index bbecdb44..71f7e603 100644 --- a/webapp/src/app/services/phaser/entities/cc-entity.ts +++ b/webapp/src/app/services/phaser/entities/cc-entity.ts @@ -1,5 +1,10 @@ import * as Phaser from 'phaser'; -import { MapEntity, PartialPoint3, Point, Point3 } from '../../../models/cross-code-map'; +import { + MapEntity, + PartialPoint3, + Point, + Point3, +} from '../../../models/cross-code-map'; import { Helper } from '../helper'; import { CCMap } from '../tilemap/cc-map'; import { Vec2 } from '../vec2'; @@ -9,7 +14,6 @@ import { AbstractWidget } from '../../../components/widgets/abstract-widget'; import { Globals } from '../../globals'; import { BaseObject } from '../base-object'; - export interface ScaleSettings { scalableX: boolean; scalableY: boolean; @@ -17,25 +21,23 @@ export interface ScaleSettings { scalableStep: number; } -export interface EntityAttributes { - [key: string]: AttributeValue; -} +export type EntityAttributes = Record; export interface AttributeValue { type: string; description: string; - options?: { [key: string]: any }; + options?: Record; withNull?: boolean; large?: boolean; optional?: boolean; - + // TODO: not needed anymore, cc source is not obfuscated [key: string]: any; } export interface DetailSettings { mapId: number; - + [key: string]: any; } @@ -55,7 +57,7 @@ export interface Fix { flipY?: boolean; tint?: number; alpha?: number; - + wallY?: number; shape?: string; aboveZ?: string | number; @@ -65,7 +67,7 @@ export interface Fix { off?: null; wall?: number; shapeType?: string; - + // reduces width/height on scalable props to allow space for end sprites offsetWidth?: number; offsetHeight?: number; @@ -74,47 +76,45 @@ export interface Fix { ignoreBoundingboxY?: boolean; } - export abstract class CCEntity extends BaseObject { - private static renderBackground?: Phaser.GameObjects.Graphics; - + private map: CCMap; private levelOffset = 0; private visible = true; - + public container!: Phaser.GameObjects.Container; - + private text?: Phaser.GameObjects.Text; private images: Phaser.GameObjects.Image[] = []; - + private readonly filterSubscription: Subscription; - - + protected widgets: Record = {}; - + // input (is handled mostly by entity manager) private collisionImage!: Phaser.GameObjects.Graphics; private inputZone!: Phaser.GameObjects.Zone; - + private _selected = false; - + get selected(): boolean { return this._selected; } - + // drag public isDragged = false; - public startOffset: Point = {x: 0, y: 0}; - + public startOffset: Point = { x: 0, y: 0 }; + // zIndex: number; details: { level: { level: number; offset: number; - }; type: string; + }; + type: string; settings: DetailSettings; - } = {}; + } = {} as any; entitySettings: { collType?: string; baseSize: PartialPoint3; @@ -129,81 +129,87 @@ export abstract class CCEntity extends BaseObject { scalableY?: boolean; scalableStep?: number; pivot?: Point; - } = {}; - - protected constructor(scene: Phaser.Scene, map: CCMap, x: number, y: number, typeName: string) { + } = {} as any; + + protected constructor( + scene: Phaser.Scene, + map: CCMap, + x: number, + y: number, + typeName: string, + ) { super(scene, typeName, false); scene.add.existing(this); this.map = map; this.container.x = Math.round(x); this.container.y = Math.round(y); - this.details = { - type: typeName - }; - - this.filterSubscription = Globals.globalEventsService.filterEntity.subscribe(filter => this.setVisible(this.filter(filter))); + this.details = { + type: typeName, + } as any; + + this.filterSubscription = + Globals.globalEventsService.filterEntity.subscribe((filter) => + this.setVisible(this.filter(filter)), + ); } - - + protected init(): void { this.container = this.scene.add.container(0, 0); - + const collImg = this.scene.add.graphics(); this.container.add(collImg); this.collisionImage = collImg; - + collImg.alpha = 0; - + this.inputZone = this.scene.add.zone(0, 0, 50, 50); this.inputZone.setOrigin(0); this.inputZone.setData('entity', this); this.container.add(this.inputZone); - - + this.inputZone.on('pointerover', () => this.inputOver()); this.inputZone.on('pointerout', () => this.inputOut()); } - - + public inputOver() { if (!this._selected) { this.collisionImage.alpha = 0.35; } } - + public inputOut() { if (!this._selected) { this.collisionImage.alpha = 0; } } - + protected activate(): void { this.inputZone.setInteractive(); } - - + protected deactivate(): void { this.inputZone.disableInteractive(); } - - + preUpdate(): void { if (this.isDragged) { const container = this.container; const p = this.scene.input.activePointer; container.x = Math.round(p.worldX - this.startOffset.x); container.y = Math.round(p.worldY - this.startOffset.y); - + const settings = Globals.gridSettings(); if (settings.enableGrid) { - const diffX = (container.x - settings.offset.x) % settings.size.x; + const diffX = + (container.x - settings.offset.x) % settings.size.x; if (diffX * 2 < settings.size.x) { container.x -= diffX; } else { container.x += settings.size.x - diffX; } - - const diffY = (container.y - settings.offset.y) % settings.size.y; + + const diffY = + (container.y - settings.offset.y) % settings.size.y; if (diffY * 2 < settings.size.y) { container.y -= diffY; } else { @@ -213,182 +219,204 @@ export abstract class CCEntity extends BaseObject { this.updateZIndex(); } } - + addPosition(x: number, y: number) { this.container.x += x; this.container.y += y; } - + updateSettings() { const s = this.entitySettings; const settings = this.details.settings; - - this.images.forEach(img => this.container.remove(img, true)); + + this.images.forEach((img) => this.container.remove(img, true)); this.images = []; - + // bound box offset - const boundBoxOffset = {x: 0, y: 0}; + const boundBoxOffset = { x: 0, y: 0 }; if (s.baseSize) { boundBoxOffset.x = s.baseSize.x / 2; boundBoxOffset.y = s.baseSize.y; } - + // setup sprite if (s.sheets && s.sheets.fix) { for (const fix of s.sheets.fix) { if (!this.scene.textures.exists(fix.gfx.trim())) { - console.error(`texture not loaded: [${fix.gfx}] in class: [${this.constructor.name}]`); + console.error( + `texture not loaded: [${fix.gfx}] in class: [${this.constructor.name}]`, + ); } } - + for (const fix of s.sheets.fix) { const gfx = fix.gfx.trim(); - if (!s.sheets.ignoreScalable && (s.scalableX || s.scalableY) && fix.scalable) { + if ( + !s.sheets.ignoreScalable && + (s.scalableX || s.scalableY) && + fix.scalable + ) { // scalable const offsetX = fix.offsetX ?? 0; const offsetY = fix.offsetY ?? 0; const offsetWidth = fix.offsetWidth ?? 0; const offsetHeight = fix.offsetHeight ?? 0; const width = settings['size'].x - offsetX - offsetWidth; - const height = (fix.renderHeight || s.baseSize.z) + settings['size'].y - offsetY - offsetHeight; - + const height = + (fix.renderHeight || s.baseSize.z) + + settings['size'].y - + offsetY - + offsetHeight; + for (let x = 0; x < width; x += fix.w) { const imgWidth = Math.min(fix.w, width - x); for (let y = 0; y < height; y += fix.h) { const imgHeight = Math.min(fix.h, height - y); - const img = this.scene.add.image(x, -y + settings['size'].y, gfx); + const img = this.scene.add.image( + x, + -y + settings['size'].y, + gfx, + ); img.setCrop(fix.x, fix.y, imgWidth, imgHeight); - + img.setOrigin(0, 0); - + img.alpha = fix.alpha ?? 1; - + // level offset img.y += this.levelOffset; - + // origin offset x=0, y=1 img.y -= imgHeight; - + // crop offset img.x -= fix.x; img.y -= fix.y; - + // fix offset img.x += offsetX; img.y -= offsetY; - + if (fix.renderMode === 'lighter') { img.blendMode = Phaser.BlendModes.ADD; } - + this.container.add(img); this.images.push(img); } } } else { // default - + fix.x = fix.x || 0; fix.y = fix.y || 0; fix.offsetX = fix.offsetX || 0; fix.offsetY = fix.offsetY || 0; - - const img = this.scene.add.image(fix.offsetX, fix.offsetY, gfx); + + const img = this.scene.add.image( + fix.offsetX, + fix.offsetY, + gfx, + ); img.setOrigin(0, 0); - + if (fix.tint !== undefined) { img.setTintFill(fix.tint); } - + img.alpha = fix.alpha ?? 1; - + // scale, used for single color img.scaleX = fix.scaleX || 1; img.scaleY = fix.scaleY || 1; - + // level offset img.y += this.levelOffset; - + // bounding box offset - + if (!fix.ignoreBoundingboxX) { img.x += boundBoxOffset.x; } if (!fix.ignoreBoundingboxY) { img.y += boundBoxOffset.y; } - + // origin offset x=0.5, y=1 img.x -= fix.w / 2; img.y -= fix.h; - + // flip crop offset let cropX = fix.x; if (fix.flipX) { cropX = img.displayWidth - fix.x - fix.w; } - + let cropY = fix.y; if (fix.flipY) { // TODO: untested cropY = img.displayWidth - fix.y - fix.h; } - + // crop offset img.x -= fix.x; img.y -= fix.y; - + img.setCrop(cropX, cropY, fix.w, fix.h); img.flipX = !!fix.flipX; img.flipY = !!fix.flipY; - + if (fix.renderMode === 'lighter') { img.blendMode = Phaser.BlendModes.ADD; } - + this.container.add(img); this.images.push(img); } if (s.sheets.offset) { - this.images.forEach(img => Vec2.add(img, s.sheets.offset!)); + this.images.forEach((img) => + Vec2.add(img, s.sheets.offset!), + ); } if (s.sheets.flipX) { - this.images.forEach(img => img.flipX = !img.flipX); + this.images.forEach((img) => (img.flipX = !img.flipX)); } } - + if (s.sheets.renderMode === 'lighter') { - this.images.forEach(img => img.blendMode = Phaser.BlendModes.ADD); + this.images.forEach( + (img) => (img.blendMode = Phaser.BlendModes.ADD), + ); } else if (s.sheets.renderMode === 'source-over') { // TODO: no idea what that actually is console.warn('renderMode source-over found'); } } - + this.container.bringToTop(this.collisionImage); if (this.text) { this.container.bringToTop(this.text); } - + this.drawBoundingBox(); Globals.globalEventsService.updateEntitySettings.next(this); } - + set level(level: any) { const details = this.details; - + if (typeof level === 'object') { details.level = level; } else { details.level = { level: level, - offset: 0 + offset: 0, }; } - + this.updateLevel(); } - + updateLevel() { this.updateZIndex(); const level = this.map.levels[this.details.level.level]; @@ -400,13 +428,13 @@ export abstract class CCEntity extends BaseObject { this.levelOffset = -(height + offset); this.updateSettings(); } - + // TODO: refactor async setSettings(settings: any) { this.details.settings = settings; await this.updateType(); } - + setSelected(selected: boolean) { this._selected = selected; if (this.collisionImage) { @@ -416,11 +444,11 @@ export abstract class CCEntity extends BaseObject { this.isDragged = false; } } - + setWidgets(widgets: Record) { this.widgets = widgets; } - + override destroy() { super.destroy(); this.container.destroy(); @@ -429,64 +457,72 @@ export abstract class CCEntity extends BaseObject { this.text.destroy(); } } - + updateZIndex() { let zIndex = this.details.level.level * 10 + 1; - + // TODO: hack to display OLPlatform over objects because right now Object Layer is always on level 100 - if (this.details.type === 'OLPlatform' || this.details.type === 'ObjectLayerView') { + if ( + this.details.type === 'OLPlatform' || + this.details.type === 'ObjectLayerView' + ) { zIndex += 1000; } - + // sort entities by y when on same level zIndex += this.container.y * 0.000001; - + this.container.depth = zIndex; } - + exportEntity(): MapEntity { const out = { type: this.details.type, x: this.container.x, y: this.container.y, - level: this.details.level.offset ? this.details.level : this.details.level.level, - settings: this.details.settings + level: this.details.level.offset + ? this.details.level + : this.details.level.level, + settings: this.details.settings, }; return JSON.parse(JSON.stringify(out)); } - + public abstract getScaleSettings(): ScaleSettings | undefined; - + public abstract getAttributes(): EntityAttributes; - + protected abstract setupType(settings: any): Promise; - - public doubleClick(): void { - - } - + + public doubleClick(): void {} + public async updateType() { const settings = this.details.settings; await this.setupType(settings); this.setActive(true); } - + public generateErrorImage() { - this.generateNoImageType(0xFF0000, 1); + this.generateNoImageType(0xff0000, 1); } - - public generateNoImageType(rgbTop = 0xc06040, aTop = 0.5, rgb = 0x800000, a = 0.5) { + + public generateNoImageType( + rgbTop = 0xc06040, + aTop = 0.5, + rgb = 0x800000, + a = 0.5, + ) { const settings = this.details.settings; - - const baseSize: Point3 = {x: 16, y: 16, z: 0}; + + const baseSize: Point3 = { x: 16, y: 16, z: 0 }; if (settings['size']) { baseSize.x = settings['size'].x; baseSize.y = settings['size'].y; } - + baseSize.z = settings['zHeight'] || settings['wallZHeight'] || 0; - - this.entitySettings = {}; + + this.entitySettings = {} as any; this.entitySettings.baseSize = baseSize; const scaleSettings = this.getScaleSettings(); if (scaleSettings) { @@ -502,66 +538,75 @@ export abstract class CCEntity extends BaseObject { settings['size'].y = scaleSettings.baseSize.y; } } - - + this.generateSingleColorSheet(rgb, a, rgbTop, aTop); this.updateSettings(); } - - private generateSingleColorSheet(rgb: number, a: number, rgbTop?: number, aTop?: number) { + + private generateSingleColorSheet( + rgb: number, + a: number, + rgbTop?: number, + aTop?: number, + ) { const size = this.getActualSize(); - + if (rgbTop === undefined) { rgbTop = rgb; } if (aTop === undefined) { aTop = a; } - + if (!size.z) { this.entitySettings.sheets = { ignoreScalable: true, - fix: [{ - gfx: 'pixel', - x: 0, - y: 0, - w: size.x, - h: size.y, - scaleX: size.x, - scaleY: size.y, - tint: rgbTop, - alpha: aTop - }], + fix: [ + { + gfx: 'pixel', + x: 0, + y: 0, + w: size.x, + h: size.y, + scaleX: size.x, + scaleY: size.y, + tint: rgbTop, + alpha: aTop, + }, + ], }; } else { this.entitySettings.sheets = { ignoreScalable: true, - fix: [{ - gfx: 'pixel', - x: 0, - y: 0, - w: size.x, - h: size.z, - scaleX: size.x, - scaleY: size.z, - tint: rgb, - alpha: a - }, { - gfx: 'pixel', - x: 0, - y: 0, - offsetY: -size.z, - w: size.x, - h: size.y, - scaleX: size.x, - scaleY: size.y, - tint: rgbTop, - alpha: aTop - }], + fix: [ + { + gfx: 'pixel', + x: 0, + y: 0, + w: size.x, + h: size.z, + scaleX: size.x, + scaleY: size.z, + tint: rgb, + alpha: a, + }, + { + gfx: 'pixel', + x: 0, + y: 0, + offsetY: -size.z, + w: size.x, + h: size.y, + scaleX: size.x, + scaleY: size.y, + tint: rgbTop, + alpha: aTop, + }, + ], }; } } - + protected replaceJsonParams(jsonInstance: any, prop: any) { Object.entries(jsonInstance).forEach(([key, value]: [string, any]) => { if (value['jsonPARAM']) { @@ -573,7 +618,7 @@ export abstract class CCEntity extends BaseObject { } }); } - + public getBoundingBox(): Phaser.Geom.Rectangle { if (!this.inputZone.input) { console.warn('no bounding box for: ' + this.details.type); @@ -584,74 +629,130 @@ export abstract class CCEntity extends BaseObject { this.inputZone.x + this.container.x, this.inputZone.y + this.container.y, hitArea.width, - hitArea.height + hitArea.height, ); return box; } - + public getActualSize(): Point3 { const s = this.entitySettings; - const size = Object.assign({}, this.details.settings['size'] || s.baseSize); + const size = Object.assign( + {}, + this.details.settings['size'] || s.baseSize, + ); try { size.x = Number(size.x); size.y = Number(size.y); if (size.z !== 0) { - size.z = Number(size.z || this.details.settings['zHeight'] || this.details.settings['wallZHeight'] || (s.baseSize ? s.baseSize.z || 0 : 0)); + size.z = Number( + size.z || + this.details.settings['zHeight'] || + this.details.settings['wallZHeight'] || + (s.baseSize ? s.baseSize.z || 0 : 0), + ); } } catch (e) { console.log(this); console.error(e); } - + return size; } - + private drawBoundingBox() { const collImg = this.collisionImage; - + collImg.clear(); - + const size = this.getActualSize(); - + const inputArea = new Phaser.Geom.Rectangle(0, 0, size.x, size.y); - + const outline = 0; const outlineAlpha = 1; - - const bottomRect = new Phaser.Geom.Rectangle(0, size.z, inputArea.width, inputArea.height - 1); - + + const bottomRect = new Phaser.Geom.Rectangle( + 0, + size.z, + inputArea.width, + inputArea.height - 1, + ); + // show middle and top part only if entity is not flat if (size.z > 0) { - const middleRect = new Phaser.Geom.Rectangle(0, inputArea.height, inputArea.width, size.z - 1); - Helper.drawRect(collImg, middleRect, 0xff0707, 0.5, outline, outlineAlpha); - - const topRect = new Phaser.Geom.Rectangle(0, 0, inputArea.width, inputArea.height); - Helper.drawRect(collImg, topRect, 0xffff07, 1, outline, outlineAlpha); - - Helper.drawRect(collImg, bottomRect, 0xffff07, 0.1, outline, outlineAlpha); + const middleRect = new Phaser.Geom.Rectangle( + 0, + inputArea.height, + inputArea.width, + size.z - 1, + ); + Helper.drawRect( + collImg, + middleRect, + 0xff0707, + 0.5, + outline, + outlineAlpha, + ); + + const topRect = new Phaser.Geom.Rectangle( + 0, + 0, + inputArea.width, + inputArea.height, + ); + Helper.drawRect( + collImg, + topRect, + 0xffff07, + 1, + outline, + outlineAlpha, + ); + + Helper.drawRect( + collImg, + bottomRect, + 0xffff07, + 0.1, + outline, + outlineAlpha, + ); } else { - Helper.drawRect(collImg, bottomRect, 0xffff07, 1, outline, outlineAlpha); + Helper.drawRect( + collImg, + bottomRect, + 0xffff07, + 1, + outline, + outlineAlpha, + ); } - + collImg.x = inputArea.x; collImg.y = inputArea.y - (size.z || 0) + this.levelOffset; - - const shape = new Phaser.Geom.Rectangle(0, 0, size.x, size.y + (size.z || 0)); - + + const shape = new Phaser.Geom.Rectangle( + 0, + 0, + size.x, + size.y + (size.z || 0), + ); + this.inputZone.x = collImg.x; this.inputZone.y = collImg.y; this.inputZone.setSize(shape.width, shape.height, true); - + this.generateText(this.details.settings['name'], size); } - + private generateText(name: string, size: Point) { if (name) { if (!this.text) { this.text = this.scene.add.text(0, 0, '', { font: '400 18pt Roboto', color: 'white', - resolution: window.devicePixelRatio * 3 + resolution: window.devicePixelRatio * 3, }); this.text.setOrigin(0.5, 0.5); this.text.setScale(0.3); @@ -663,24 +764,28 @@ export abstract class CCEntity extends BaseObject { this.text.destroy(); this.text = undefined; } - } - + protected filter(filter: string): boolean { const lower = filter.toLocaleLowerCase(); const attributes = this.getAttributes(); - + for (const name of Object.keys(attributes)) { const value = this.details.settings[name] || ''; - if (typeof value === 'string' && value.toLowerCase().includes(lower)) { + if ( + typeof value === 'string' && + value.toLowerCase().includes(lower) + ) { return true; } } - - return this.details.type.toLowerCase().includes(lower) - || (this.details.settings['name'] || '').toLowerCase().includes(lower); + + return ( + this.details.type.toLowerCase().includes(lower) || + (this.details.settings['name'] || '').toLowerCase().includes(lower) + ); } - + private setVisible(visible: boolean) { this.visible = visible; this.setActive(visible); @@ -690,14 +795,16 @@ export abstract class CCEntity extends BaseObject { this.container.alpha = 0.2; } } - + override setActive(value: boolean): this { return super.setActive(this.visible ? value : false); } - + private getRenderBackground(width: number, height: number) { if (!CCEntity.renderBackground) { - const g = this.scene.add.graphics({fillStyle: {color: 0x616161, alpha: 1}}); + const g = this.scene.add.graphics({ + fillStyle: { color: 0x616161, alpha: 1 }, + }); g.fillRect(0, 0, width, height); g.fillStyle(0, 0.15); for (let x = 0; x < width; x += 16) { @@ -714,48 +821,52 @@ export abstract class CCEntity extends BaseObject { } return CCEntity.renderBackground; } - - public async generateHtmlImage(withBackground = true, offsetY: number = 0, entityScale = 1) { + + public async generateHtmlImage( + withBackground = true, + offsetY = 0, + entityScale = 1, + ) { const width = 16 * 6 * entityScale; const height = 16 * 7 * entityScale; - + const scale = 3; - + const name = Math.random() + ''; - const texture = this.scene.textures.addDynamicTexture(name, width * scale, height * scale)!; - + const texture = this.scene.textures.addDynamicTexture( + name, + width * scale, + height * scale, + )!; + texture.clear(); if (withBackground) { - const g = this.getRenderBackground(width / entityScale, height / entityScale); + const g = this.getRenderBackground( + width / entityScale, + height / entityScale, + ); g.setScale(scale * entityScale); texture.draw(g); } - const x = (width - this.getActualSize().x) * scale / 2; - const y = scale * (height - ((32 - offsetY) * entityScale)); - + const x = ((width - this.getActualSize().x) * scale) / 2; + const y = scale * (height - (32 - offsetY) * entityScale); + // drawing container directly is broken: https://github.com/photonstorm/phaser/issues/6546 for (const img of this.images) { const sx = img.scaleX; const sy = img.scaleY; - - img.setScale( - sx * scale, - sy * scale - ); - texture.draw( - img, - x + img.x * scale, - y + img.y * scale - ); - + + img.setScale(sx * scale, sy * scale); + texture.draw(img, x + img.x * scale, y + img.y * scale); + img.setScale(sx, sy); } const res = await new Promise((res, rej) => { - texture!.snapshot(img => { + texture.snapshot((img) => { res(img as HTMLImageElement); }); }); - + this.scene.textures.remove(name); return res; } diff --git a/webapp/src/app/services/phaser/entities/entity-manager.ts b/webapp/src/app/services/phaser/entities/entity-manager.ts index 56108751..3d82d8e3 100644 --- a/webapp/src/app/services/phaser/entities/entity-manager.ts +++ b/webapp/src/app/services/phaser/entities/entity-manager.ts @@ -8,37 +8,36 @@ import { CCEntity } from './cc-entity'; import { SelectionBox } from './selection-box'; export class EntityManager extends BaseObject { - // TODO: If ? is really required, add a description why. private map?: CCMap; private _entities: CCEntity[] = []; get entities(): CCEntity[] { return this._entities; } - + private multiSelectKey!: Phaser.Input.Keyboard.Key; private deleteKey!: Phaser.Input.Keyboard.Key; private gridKey!: Phaser.Input.Keyboard.Key; private visibilityKey!: Phaser.Input.Keyboard.Key; private leftPointerDown = false; private rightPointerDown = false; - + private skipEdit = false; - + private copyListener = async () => { if (Helper.isInputFocused()) { return; } await this.copy(); }; - + private pasteListener = async () => { if (Helper.isInputFocused()) { return; } await this.paste(); }; - + private cutListener = async () => { if (Helper.isInputFocused()) { return; @@ -46,7 +45,7 @@ export class EntityManager extends BaseObject { await this.copy(); this.deleteSelectedEntities(); }; - + private leftClickOpts: { prevTimer: number; prevEntity: CCEntity | undefined; @@ -58,22 +57,19 @@ export class EntityManager extends BaseObject { prevEntity: undefined, entity: undefined, timer: 0, - pos: {x: 0, y: 0} + pos: { x: 0, y: 0 }, }; - + private selectedEntities: CCEntity[] = []; - + private gameObjectDown = false; - + private selectionBox!: SelectionBox; - - constructor( - scene: Phaser.Scene, - active = true - ) { + + constructor(scene: Phaser.Scene, active = true) { super(scene, 'entityManager', active); } - + protected init(): void { const keyboard = this.scene.input.keyboard; const keyCodes = Phaser.Input.Keyboard.KeyCodes; @@ -81,57 +77,68 @@ export class EntityManager extends BaseObject { this.deleteKey = keyboard!.addKey(keyCodes.DELETE, false); this.gridKey = keyboard!.addKey(keyCodes.G, false); this.visibilityKey = keyboard!.addKey(keyCodes.R, false); - + this.selectionBox = new SelectionBox(this.scene); - - Globals.globalEventsService.updateEntities.subscribe(() => this.resetEntities()); + + Globals.globalEventsService.updateEntities.subscribe(() => + this.resetEntities(), + ); } - - + protected deactivate() { document.removeEventListener('copy', this.copyListener); document.removeEventListener('paste', this.pasteListener); document.removeEventListener('cut', this.cutListener); - + this.selectEntity(); - this._entities.forEach(entity => { + this._entities.forEach((entity) => { entity.setActive(false); entity.setSelected(false); }); } - + protected activate() { document.addEventListener('copy', this.copyListener); document.addEventListener('paste', this.pasteListener); document.addEventListener('cut', this.cutListener); - this._entities.forEach(entity => { + this._entities.forEach((entity) => { entity.setActive(true); }); - const sub2 = Globals.globalEventsService.selectedEntity.subscribe(entity => { - if (this.selectedEntities.length > 0 && !this.skipEdit) { - Globals.stateHistoryService.saveState({ - name: 'Entity edited', - icon: 'build', - }, false); - } - this.skipEdit = false; - this.selectedEntities.forEach(e => e.setSelected(false)); - this.selectedEntities = []; - if (entity) { - entity.setSelected(true); - this.selectedEntities.push(entity); - } - }); + const sub2 = Globals.globalEventsService.selectedEntity.subscribe( + (entity) => { + if (this.selectedEntities.length > 0 && !this.skipEdit) { + Globals.stateHistoryService.saveState( + { + name: 'Entity edited', + icon: 'build', + }, + false, + ); + } + this.skipEdit = false; + this.selectedEntities.forEach((e) => e.setSelected(false)); + this.selectedEntities = []; + if (entity) { + entity.setSelected(true); + this.selectedEntities.push(entity); + } + }, + ); this.addSubscription(sub2); - - const sub3 = Globals.globalEventsService.generateNewEntity.subscribe(async entity => { - await this.generateNewEntity(entity); - }); + + const sub3 = Globals.globalEventsService.generateNewEntity.subscribe( + async (entity) => { + await this.generateNewEntity(entity); + }, + ); this.addSubscription(sub3); - + this.addKeybinding({ event: 'pointerdown', - fun: (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.GameObject[]) => { + fun: ( + pointer: Phaser.Input.Pointer, + gameObject: Phaser.GameObjects.GameObject[], + ) => { if (pointer.rightButtonDown()) { this.rightPointerDown = true; } @@ -139,89 +146,101 @@ export class EntityManager extends BaseObject { return; } this.leftPointerDown = true; - - + let entity; if (gameObject.length > 0) { entity = gameObject[0].getData('entity') as CCEntity; } - + this.leftClickOpts.prevTimer = this.leftClickOpts.timer; this.leftClickOpts.prevEntity = this.leftClickOpts.entity; this.leftClickOpts.timer = 0; this.leftClickOpts.entity = entity; this.leftClickOpts.pos.x = pointer.worldX; this.leftClickOpts.pos.y = pointer.worldY; - + // if panning return once entity as been selected to prevent dragging of selected entities if (Globals.panning) { return; } - + if (entity) { this.gameObjectDown = true; - + // to allow instant drag of a single entity if (this.selectedEntities.indexOf(entity) < 0) { if (!this.multiSelectKey.isDown) { this.selectEntity(entity); } } - this.selectedEntities.forEach(entity => { - entity.startOffset.x = pointer.worldX - entity.container.x; - entity.startOffset.y = pointer.worldY - entity.container.y; + this.selectedEntities.forEach((entity) => { + entity.startOffset.x = + pointer.worldX - entity.container.x; + entity.startOffset.y = + pointer.worldY - entity.container.y; entity.isDragged = true; }); } else { this.selectionBox.onInputDown(pointer); } }, - emitter: this.scene.input + emitter: this.scene.input, }); - - const pointerUpFun = (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.GameObject[]) => { + + const pointerUpFun = ( + pointer: Phaser.Input.Pointer, + gameObject: Phaser.GameObjects.GameObject[], + ) => { if (pointer.rightButtonReleased() && this.rightPointerDown) { this.rightPointerDown = false; this.selectEntity(); this.showAddEntityMenu(); } else if (pointer.leftButtonReleased() && this.leftPointerDown) { this.leftPointerDown = false; - this.selectedEntities.forEach(entity => { + this.selectedEntities.forEach((entity) => { entity.isDragged = false; }); - + // if panning do not deselect entities if (Globals.panning) { return; } - + if (this.gameObjectDown) { this.gameObjectDown = false; Globals.stateHistoryService.saveState({ name: 'Entity moved', - icon: 'open_with' + icon: 'open_with', }); } else { const entities = this.selectionBox.onInputUp(); - + if (!this.multiSelectKey.isDown) { this.selectEntity(); } - entities.forEach(entity => { + entities.forEach((entity) => { this.selectEntity(entity, true); }); } - + let entity; if (gameObject.length > 0) { entity = gameObject[0].getData('entity'); } if (entity) { - const p = {x: pointer.worldX, y: pointer.worldY}; - if (this.leftClickOpts.timer < 200 && Vec2.distance2(p, this.leftClickOpts.pos) < 10) { + const p = { x: pointer.worldX, y: pointer.worldY }; + if ( + this.leftClickOpts.timer < 200 && + Vec2.distance2(p, this.leftClickOpts.pos) < 10 + ) { this.selectEntity(entity, this.multiSelectKey.isDown); - - if (!this.multiSelectKey.isDown && this.leftClickOpts.prevEntity === this.leftClickOpts.entity && this.leftClickOpts.prevTimer < 500) { + + if ( + !this.multiSelectKey.isDown && + this.leftClickOpts.prevEntity === + this.leftClickOpts.entity && + this.leftClickOpts.prevTimer < 500 + ) { entity.doubleClick(); } } else { @@ -231,19 +250,19 @@ export class EntityManager extends BaseObject { } } }; - + this.addKeybinding({ event: 'pointerup', fun: pointerUpFun, - emitter: this.scene.input + emitter: this.scene.input, }); - + this.addKeybinding({ event: 'pointerupoutside', fun: pointerUpFun, - emitter: this.scene.input + emitter: this.scene.input, }); - + this.addKeybinding({ event: 'up', emitter: this.gridKey, @@ -251,13 +270,13 @@ export class EntityManager extends BaseObject { if (Helper.isInputFocused()) { return; } - Globals.gridSettings.update(settings => ({ + Globals.gridSettings.update((settings) => ({ ...settings, - enableGrid: !settings.enableGrid + enableGrid: !settings.enableGrid, })); - } + }, }); - + // TODO: still triggers when npc editor is open this.addKeybinding({ event: 'up', @@ -267,9 +286,9 @@ export class EntityManager extends BaseObject { return; } this.deleteSelectedEntities(); - } + }, }); - + this.addKeybinding({ event: 'up', emitter: this.visibilityKey, @@ -278,36 +297,33 @@ export class EntityManager extends BaseObject { return; } this.hideEntities(); - } + }, }); - - } - + preUpdate(time: number, delta: number): void { this.leftClickOpts.timer += delta; this.selectionBox.update(this._entities); } - - + hideEntities() { - this._entities.forEach(e => { + this._entities.forEach((e) => { e.container.visible = !e.container.visible; }); } - + /** generates all entities and adds proper input handling */ async initialize(map: CrossCodeMap, ccMap: CCMap) { this.map = ccMap; if (this._entities) { - this._entities.forEach(e => e.destroy()); + this._entities.forEach((e) => e.destroy()); } this._entities = []; - + if (!map.entities) { return; } - + // concurrent entity loading const promises: Promise[] = []; for (const entity of map.entities) { @@ -315,56 +331,73 @@ export class EntityManager extends BaseObject { } await Promise.all(promises); } - - + selectEntity(entity?: CCEntity, multiple = false) { if (multiple) { if (!entity) { - throw new Error('select entity is undefined, but multiple is true'); - + throw new Error( + 'select entity is undefined, but multiple is true', + ); } - const i = this.selectedEntities.indexOf(entity as CCEntity); + const i = this.selectedEntities.indexOf(entity); if (i >= 0) { entity.setSelected(false); this.selectedEntities.splice(i, 1); } else { entity.setSelected(true); - this.selectedEntities.push(entity!); + this.selectedEntities.push(entity); } - + if (this.selectedEntities.length === 1) { Globals.globalEventsService.selectedEntity.next(entity); } - } else if (this.selectedEntities[0] !== entity || this.selectedEntities.length !== 1) { + } else if ( + this.selectedEntities[0] !== entity || + this.selectedEntities.length !== 1 + ) { Globals.globalEventsService.selectedEntity.next(entity); } } - - public async generateNewEntity(entity: MapEntity): Promise { + + public async generateNewEntity( + entity: MapEntity, + ): Promise { if (!this.map) { return; } // TODO: better generate level from collision tiles entity.level = this.map.masterLevel; const e = await this.generateEntity(entity); - + // entity manager is activated e.setActive(true); this.selectEntity(e); - - Globals.stateHistoryService.saveState({ - name: 'Entity added', - icon: 'add' - }, true); - + + Globals.stateHistoryService.saveState( + { + name: 'Entity added', + icon: 'add', + }, + true, + ); + return e; } - + async generateEntity(entity: MapEntity): Promise { const entityClass = Globals.entityRegistry.getEntity(entity.type); - console.assert(this.map, 'I dont think map is ever undefined, but if it ever happens check the TODO on private map?: CCMap;'); + console.assert( + this.map, + 'I dont think map is ever undefined, but if it ever happens check the TODO on private map?: CCMap;', + ); const map = this.map!; - const ccEntity = new entityClass(this.scene, map, entity.x, entity.y, entity.type); + const ccEntity = new entityClass( + this.scene, + map, + entity.x, + entity.y, + entity.type, + ); if (!entity.settings.mapId) { entity.settings.mapId = map.getUniqueMapid(); } @@ -374,21 +407,26 @@ export class EntityManager extends BaseObject { this._entities.push(ccEntity); return ccEntity; } - + async copy() { - const entities = this.selectedEntities.map(v => v.exportEntity()); + const entities = this.selectedEntities.map((v) => v.exportEntity()); if (entities.length === 0) { return; } await navigator.clipboard.writeText(JSON.stringify(entities)); - Globals.snackbar.open('copied entity to clipboard', undefined, {duration: 1000}); - + Globals.snackbar.open('copied entity to clipboard', undefined, { + duration: 1000, + }); } - + isMapEntity(obj: Partial | undefined): obj is MapEntity { - return typeof obj?.type === 'string' && typeof obj.x === 'number' && typeof obj.settings === 'object'; + return ( + typeof obj?.type === 'string' && + typeof obj.x === 'number' && + typeof obj.settings === 'object' + ); } - + async paste() { if (!this.map) { return; @@ -400,12 +438,16 @@ export class EntityManager extends BaseObject { if (!Array.isArray(parsed)) { parsed = [parsed]; } - entities = (parsed as any[]).filter(v => this.isMapEntity(v)); + entities = (parsed as any[]).filter((v) => this.isMapEntity(v)); } catch (e) { - Globals.snackbar.open('could not parse entities from clipboard', undefined, { - duration: 2000, - panelClass: 'snackbar-error' - }); + Globals.snackbar.open( + 'could not parse entities from clipboard', + undefined, + { + duration: 2000, + panelClass: 'snackbar-error', + }, + ); return; } if (entities.length === 0) { @@ -422,7 +464,7 @@ export class EntityManager extends BaseObject { offset.y -= levelOffset; const mousePos = Helper.getPointerPos(this.scene.input.activePointer); this.selectEntity(); - + for (const e of entities) { e.settings.mapId = undefined; Vec2.sub(e, offset); @@ -431,9 +473,8 @@ export class EntityManager extends BaseObject { newEntity.setActive(true); this.selectEntity(newEntity, entities.length > 1); } - } - + private async resetEntities() { if (!this.map) { return; @@ -446,33 +487,37 @@ export class EntityManager extends BaseObject { } } } - + deleteSelectedEntities() { this.skipEdit = true; const saveHistory = this.selectedEntities.length > 0; - this.selectedEntities.forEach(e => { + this.selectedEntities.forEach((e) => { const i = this._entities.indexOf(e); this._entities.splice(i, 1); e.destroy(); }); this.selectEntity(); - + if (saveHistory) { - Globals.stateHistoryService.saveState({ - name: 'Entity deleted', - icon: 'delete' - }, true); + Globals.stateHistoryService.saveState( + { + name: 'Entity deleted', + icon: 'delete', + }, + true, + ); } } - + exportEntities(): MapEntity[] { const out: MapEntity[] = []; - this._entities.forEach(e => out.push(e.exportEntity())); + this._entities.forEach((e) => out.push(e.exportEntity())); return out; } - + private showAddEntityMenu() { - - Globals.globalEventsService.showAddEntityMenu.next(Helper.getPointerPos(this.scene.input.activePointer)); + Globals.globalEventsService.showAddEntityMenu.next( + Helper.getPointerPos(this.scene.input.activePointer), + ); } } diff --git a/webapp/src/app/services/phaser/entities/registry/default-entity.ts b/webapp/src/app/services/phaser/entities/registry/default-entity.ts index 3a3f597d..65094207 100644 --- a/webapp/src/app/services/phaser/entities/registry/default-entity.ts +++ b/webapp/src/app/services/phaser/entities/registry/default-entity.ts @@ -2,25 +2,23 @@ import { CCMap } from '../../tilemap/cc-map'; import { CCEntity, EntityAttributes, ScaleSettings } from '../cc-entity'; import { Globals } from '../../../globals'; -export interface EntitiesJson { - [key: string]: JsonEntityType; -} +export type EntitiesJson = Record; interface JsonEntityType { attributes: EntityAttributes; - + spawnable?: boolean; - + drawBox?: boolean; borderColor?: string; boxColor?: string; circleColor?: string; frontColor?: string; - + scalableX?: boolean; scalableY?: boolean; scalableStep?: number; - + alwaysRecreate?: boolean; noZLine?: boolean; } @@ -31,58 +29,60 @@ interface SizeOverride { } export class DefaultEntity extends CCEntity { - - private static BASE_SIZE_OVERRIDES: {[entityType: string]: SizeOverride} = { - 'WallHorizontal': { + private static BASE_SIZE_OVERRIDES: Record = { + WallHorizontal: { y: 8, }, - 'WallVertical': { + WallVertical: { x: 8, - } + }, }; - + constructor( scene: Phaser.Scene, map: CCMap, x: number, y: number, - private typeName: string + private typeName: string, ) { super(scene, map, x, y, typeName); - const entities = Globals.jsonLoader.loadJsonMergedSync('entities.json'); + const entities = + Globals.jsonLoader.loadJsonMergedSync( + 'entities.json', + ); this.typeDef = entities[typeName]; } - + private readonly typeDef?: JsonEntityType; private settings: any = {}; private scaleSettings?: ScaleSettings; - + getAttributes(): EntityAttributes { if (this.typeDef) { return this.typeDef.attributes; } - + const out: EntityAttributes = {}; - Object.keys(this.settings).forEach(key => { + Object.keys(this.settings).forEach((key) => { out[key] = { type: 'Unknown', - description: '' + description: '', }; }); return out; } - + getScaleSettings(): ScaleSettings | undefined { if (this.scaleSettings) { return this.scaleSettings; } - + if (!this.typeDef) { return undefined; } - + const step = this.typeDef.scalableStep || 1; - + this.scaleSettings = { scalableX: !!this.typeDef.scalableX, scalableY: !!this.typeDef.scalableY, @@ -90,36 +90,43 @@ export class DefaultEntity extends CCEntity { baseSize: { x: DefaultEntity.BASE_SIZE_OVERRIDES[this.typeName]?.x ?? step, y: DefaultEntity.BASE_SIZE_OVERRIDES[this.typeName]?.y ?? step, - } + }, }; - + return this.scaleSettings; } - + protected async setupType(settings: any) { this.settings = settings; if (!this.typeDef) { this.generateNoImageType(); return; } - + const boxColor = this.convertToColor(this.typeDef.boxColor); const frontColor = this.convertToColor(this.typeDef.frontColor); - this.generateNoImageType(boxColor.rgb, boxColor.a, frontColor.rgb, frontColor.a); - + this.generateNoImageType( + boxColor.rgb, + boxColor.a, + frontColor.rgb, + frontColor.a, + ); } - + private convertToColor(rgba?: string) { if (!rgba) { return {}; } - const numbers = rgba.replace(/[^0-9.]/g, ',').split(',').filter(v => v); + const numbers = rgba + .replace(/[^0-9.]/g, ',') + .split(',') + .filter((v) => v); const r = parseInt(numbers[0], 10); const g = parseInt(numbers[1], 10); const b = parseInt(numbers[2], 10); return { - rgb: b + (g * 2 ** 8) + (r * 2 ** 16), - a: parseFloat(numbers[3]) + rgb: b + g * 2 ** 8 + r * 2 ** 16, + a: parseFloat(numbers[3]), }; } } diff --git a/webapp/src/app/services/phaser/entities/registry/destructible.ts b/webapp/src/app/services/phaser/entities/registry/destructible.ts index 600551de..91319263 100644 --- a/webapp/src/app/services/phaser/entities/registry/destructible.ts +++ b/webapp/src/app/services/phaser/entities/registry/destructible.ts @@ -26,7 +26,8 @@ export class Destructible extends CCEntity { }, permaDestruct: { type: 'Boolean', - description: 'If true, then destructible stays destroyed after reentering map', + description: + 'If true, then destructible stays destroyed after reentering map', }, onDestructIncrease: { type: 'VarName', @@ -34,47 +35,51 @@ export class Destructible extends CCEntity { }, onPreDestructIncrease: { type: 'VarName', - description: 'Variable to increase by one when destroyed' - } + description: 'Variable to increase by one when destroyed', + }, }; - + public getAttributes(): EntityAttributes { return this.attributes; } - + public getScaleSettings(): ScaleSettings | undefined { return undefined; } - + protected async setupType(settings: any): Promise { - const types = await Globals.jsonLoader.loadJsonMerged('destructible-types.json'); - + const types = + await Globals.jsonLoader.loadJsonMerged( + 'destructible-types.json', + ); + this.attributes['desType'].options = {}; for (const name of Object.keys(types)) { this.attributes['desType'].options[name] = name; } - + const type = types[settings.desType]; if (!type) { - this.generateNoImageType(0xFF0000, 1); + this.generateNoImageType(0xff0000, 1); return; } - + const sheets: Fix[] = []; const defaultSheet = type.anims.sheet as AnimSheet; - - for (const sub of (type.anims.SUB as Anims[])) { - const sheet = defaultSheet || type.anims.namedSheets![sub.sheet as string]; + + for (const sub of type.anims.SUB as Anims[]) { + const sheet = + defaultSheet || type.anims.namedSheets![sub.sheet as string]; let frame = sub.frames ? sub.frames[0] : 0; - + // frame 1 is always glow, don't show in map editor if (frame === 1) { frame = 0; } - + const offset: Partial = { x: 0, - y: 0 + y: 0, }; if (type.anims.offset) { offset.x = type.anims.offset.x; @@ -91,17 +96,17 @@ export class Destructible extends CCEntity { h: sheet.height, offsetX: offset.x, offsetY: offset.y, - renderMode: sub.renderMode + renderMode: sub.renderMode, }); } - - this.entitySettings = { + + this.entitySettings = { sheets: { fix: sheets, }, baseSize: type.size, - }; - + } as any; + const mapStyle = Helper.getMapStyle(Globals.map, 'destruct'); for (const sheet of sheets) { if (!sheet.gfx) { @@ -112,19 +117,16 @@ export class Destructible extends CCEntity { console.error('sheet does not exist: ' + sheet.gfx); } } - + if (sheets.length === 0) { this.generateErrorImage(); } - + this.updateSettings(); } - } -interface DestructibleTypes { - [name: string]: DestructibleType; -} +type DestructibleTypes = Record; interface DestructibleType { hitCount: number; diff --git a/webapp/src/app/services/phaser/entities/registry/enemy.ts b/webapp/src/app/services/phaser/entities/registry/enemy.ts index 33377187..386a3fab 100644 --- a/webapp/src/app/services/phaser/entities/registry/enemy.ts +++ b/webapp/src/app/services/phaser/entities/registry/enemy.ts @@ -32,9 +32,7 @@ export interface EnemyInfo { hideEffect?: Effect; } -export interface Attribs { - [key: string]: any; -} +export type Attribs = Record; export interface LevelClass { varName: string; @@ -43,173 +41,227 @@ export interface LevelClass { export class Enemy extends DefaultEntity { protected override async setupType(settings: EnemyAttributes) { settings.enemyInfo = settings.enemyInfo || {}; - - const enemyPath = PathResolver.convertToPath(BasePath.ENEMIES, settings.enemyInfo.type || '', FileExtension.NONE); - const enemyData = await Helper.getJsonPromise(enemyPath) as EnemyData | undefined; + + const enemyPath = PathResolver.convertToPath( + BasePath.ENEMIES, + settings.enemyInfo.type || '', + FileExtension.NONE, + ); + const enemyData = (await Helper.getJsonPromise(enemyPath)) as + | EnemyData + | undefined; if (!enemyData) { this.generateErrorImage(); return; } - - const sheetPath = PathResolver.convertToPath(BasePath.ANIMATIONS, enemyData.anims, FileExtension.NONE); + + const sheetPath = PathResolver.convertToPath( + BasePath.ANIMATIONS, + enemyData.anims, + FileExtension.NONE, + ); const rawSheet = await Helper.getJsonPromise(sheetPath); if (!rawSheet) { this.generateErrorImage(); return; } - - const sheet = this.resolveSUB(rawSheet) as MultiDirAnimation[] | [MultiEntityAnimation]; + + const sheet = this.resolveSUB(rawSheet) as + | MultiDirAnimation[] + | [MultiEntityAnimation]; if (!sheet || sheet.length === 0) { this.generateErrorImage(); return; } - + this.entitySettings.sheets = { fix: [] }; - + if (this.isMultiDir(sheet)) { - const anims = sheet.filter(s => s.name === 'idle'); - if (!await this.renderMultiDirAnims(anims.length > 0 ? anims : [sheet[0]])) { + const anims = sheet.filter((s) => s.name === 'idle'); + if ( + !(await this.renderMultiDirAnims( + anims.length > 0 ? anims : [sheet[0]], + )) + ) { this.generateErrorImage(); return; } } else { - if (!await this.renderMultiEntityAnim(sheet[0])) { + if (!(await this.renderMultiEntityAnim(sheet[0]))) { this.generateErrorImage(); return; } } - + this.entitySettings.baseSize = enemyData.size; this.updateSettings(); } - + private resolveSUB(object: any): any[] { if (!object.SUB || !(object.SUB instanceof Array)) { return [object]; } const SUB: any[] = object.SUB; - + const result = []; for (const sub of SUB) { for (const subSub of this.resolveSUB(sub)) { - const combined = Object.assign(Object.assign({}, subSub), object); + const combined = Object.assign( + Object.assign({}, subSub), + object, + ); delete combined.SUB; result.push(combined); } } - + return result; } - - private isMultiDir(sheet: MultiDirAnimation[] | [MultiEntityAnimation]): sheet is MultiDirAnimation[] { + + private isMultiDir( + sheet: MultiDirAnimation[] | [MultiEntityAnimation], + ): sheet is MultiDirAnimation[] { return sheet[0].DOCTYPE === 'MULTI_DIR_ANIMATION'; } - - - private async renderMultiDirAnims(anims: MultiDirAnimation[]): Promise { - const results = await Promise.all(anims.map(anim => this.renderMultiDirAnim(anim))); - return results.every(r => r); //All results true + + private async renderMultiDirAnims( + anims: MultiDirAnimation[], + ): Promise { + const results = await Promise.all( + anims.map((anim) => this.renderMultiDirAnim(anim)), + ); + return results.every((r) => r); //All results true } - - private async renderMultiDirAnim(anim: MultiDirAnimation): Promise { - const tileSheet = typeof anim.sheet === 'string' ? anim.namedSheets[anim.sheet] : anim.sheet; - - if (!await Helper.loadTexture(tileSheet.src, this.scene)) { + + private async renderMultiDirAnim( + anim: MultiDirAnimation, + ): Promise { + const tileSheet = + typeof anim.sheet === 'string' + ? anim.namedSheets[anim.sheet] + : anim.sheet; + + if (!(await Helper.loadTexture(tileSheet.src, this.scene))) { return false; } - - const tileOffset = (anim.dirs && anim.tileOffsets) ? anim.tileOffsets[Math.floor(anim.dirs / 2)] : 0; - - this.render(tileSheet, {x: 0, y: 0}, anim.frames[0] + tileOffset); - + + const tileOffset = + anim.dirs && anim.tileOffsets + ? anim.tileOffsets[Math.floor(anim.dirs / 2)] + : 0; + + this.render(tileSheet, { x: 0, y: 0 }, anim.frames[0] + tileOffset); + return true; } - + private render(anim: TileSheet, pos: Point, frame: number): void { - const offsetX = (anim.offX || 0) + (anim.xCount ? (frame % anim.xCount * anim.width) : 0); - const offsetY = (anim.offY || 0) + (anim.xCount ? (Math.floor(frame / anim.xCount) * anim.height) : 0); - + const offsetX = + (anim.offX || 0) + + (anim.xCount ? (frame % anim.xCount) * anim.width : 0); + const offsetY = + (anim.offY || 0) + + (anim.xCount ? Math.floor(frame / anim.xCount) * anim.height : 0); + this.entitySettings.sheets.fix.push({ gfx: anim.src.trim(), h: anim.height, w: anim.width, x: offsetX, y: offsetY, - offsetX: pos.x, // + tileSheet.x + offsetX: pos.x, // + tileSheet.x offsetY: pos.y, // + tileSheet.y, }); } - - private async renderMultiEntityAnim(animation: MultiEntityAnimation): Promise { - const partAnims: Record> = {}; - + + private async renderMultiEntityAnim( + animation: MultiEntityAnimation, + ): Promise { + const partAnims: Record< + string, + Record + > = {}; + //Load const loading: Promise[] = []; if (animation.namedSheets) { for (const sheet of Object.values(animation.namedSheets)) { - loading.push((async (name: string) => { - if (!await Helper.loadTexture(name, this.scene)) { - throw new Error('Could not load texture: ' + name); - } - })(sheet.src)); + loading.push( + (async (name: string) => { + if (!(await Helper.loadTexture(name, this.scene))) { + throw new Error('Could not load texture: ' + name); + } + })(sheet.src), + ); } } - + for (const [partName, part] of Object.entries(animation.parts)) { partAnims[partName] = {}; const partAnim = partAnims[partName]; const anims = this.resolveSUB(part.anims) as SingleDirAnimation[]; - + for (const anim of anims) { - const entry = partAnim[anim.name] = partAnim[anim.name] || []; + const entry = (partAnim[anim.name] = partAnim[anim.name] || []); entry.push(anim); if (typeof anim.sheet === 'object') { - loading.push((async (name: string) => { - if (!await Helper.loadTexture(name, this.scene)) { - throw new Error('Could not load texture: ' + name); - } - })(anim.sheet.src)); + loading.push( + (async (name: string) => { + if (!(await Helper.loadTexture(name, this.scene))) { + throw new Error( + 'Could not load texture: ' + name, + ); + } + })(anim.sheet.src), + ); } else { anim.sheet = animation.namedSheets![anim.sheet]; } } } - + const success = await Promise.all(loading) .then(() => true) .catch(() => false); if (!success) { return false; } - + //Render - const anim = animation.anims['idle'] || animation.anims['default'] || animation.anims[Object.keys(animation.anims)[0]]; - + const anim = + animation.anims['idle'] || + animation.anims['default'] || + animation.anims[Object.keys(animation.anims)[0]]; + for (const [partName, partAnim] of Object.entries(anim.partAnims)) { const part = animation.parts[partName]; - + //back left bottom corner of entity const offset: Point3 = { x: (part.size.x - animation.baseSize.x) / 2, y: (part.size.y - animation.baseSize.y) / 2, z: 0, //(part.size.z - animation.baseSize.z) / 2, }; - + //position of part offset.x += part.pos.x; offset.y += part.pos.y; offset.z += part.pos.z; - + //frame offset offset.x += partAnim.posFrames[0]; offset.y += partAnim.posFrames[1]; offset.z += partAnim.posFrames[2]; - + for (const entry of partAnims[partName][partAnim.anim]) { //Move to top if wallY = 0 - this.renderSingleDirAnim(entry, this.toPoint(offset), part.size); + this.renderSingleDirAnim( + entry, + this.toPoint(offset), + part.size, + ); } - + /* this.drawBoundingBoxAt({ x: offset.x - (part.size.x - animation.baseSize.x) / 2, @@ -218,44 +270,50 @@ export class Enemy extends DefaultEntity { }, part.size); */ } - + return true; } - - private renderSingleDirAnim(anim: SingleDirAnimation, offset: Point, size: Point3): void { + + private renderSingleDirAnim( + anim: SingleDirAnimation, + offset: Point, + size: Point3, + ): void { //TODO: anim.framesAngle - + if (anim.framesSpriteOffset) { offset.x += anim.framesSpriteOffset[0]; offset.y += anim.framesSpriteOffset[1] - anim.framesSpriteOffset[2]; } - + const sheet = anim.sheet as TileSheet; - + //TODO: investigate wallY - this is probably wrong offset.y += (1 - anim.wallY) * (size.y - sheet.height); - + const offsetX = - (sheet.offX || 0) - + (sheet.xCount ? (anim.frames[0] % sheet.xCount * sheet.width) : 0); + (sheet.offX || 0) + + (sheet.xCount ? (anim.frames[0] % sheet.xCount) * sheet.width : 0); const offsetY = - (sheet.offY || 0) - + (sheet.xCount ? (Math.floor(anim.frames[0] / sheet.xCount) * sheet.height) : 0); - + (sheet.offY || 0) + + (sheet.xCount + ? Math.floor(anim.frames[0] / sheet.xCount) * sheet.height + : 0); + this.entitySettings.sheets.fix.push({ gfx: sheet.src.trim(), h: sheet.height, w: sheet.width, x: offsetX, y: offsetY, - offsetX: offset.x, // + tileSheet.x + offsetX: offset.x, // + tileSheet.x offsetY: offset.y, // + tileSheet.y, - + flipX: anim.flipX, flipY: anim.flipY, }); } - + /* private drawBoundingBoxAt(pos: Point3, size: Point3) { const collImg = this.scene.add.graphics(); @@ -280,7 +338,7 @@ export class Enemy extends DefaultEntity { collImg.x = pos2.x; collImg.y = pos2.y; }*/ - + private toPoint(point3: Point3): Point { return { x: point3.x, diff --git a/webapp/src/app/services/phaser/entities/registry/entity-registry.service.ts b/webapp/src/app/services/phaser/entities/registry/entity-registry.service.ts index 0f4275d5..e85efb2d 100644 --- a/webapp/src/app/services/phaser/entities/registry/entity-registry.service.ts +++ b/webapp/src/app/services/phaser/entities/registry/entity-registry.service.ts @@ -10,11 +10,11 @@ import { Prop } from './prop'; import { ScalableProp } from './scalable-prop'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EntityRegistryService { - private entities: { [type: string]: typeof CCEntity } = {}; - + private entities: Record = {}; + constructor() { this.register('Prop', Prop); this.register('ScalableProp', ScalableProp); @@ -24,11 +24,11 @@ export class EntityRegistryService { this.register('EventTrigger', EventTrigger); this.register('Enemy', Enemy); } - + private register(type: string, entity: typeof CCEntity) { this.entities[type] = entity; } - + public getDefaultEntity(): typeof DefaultEntity { return DefaultEntity; } @@ -36,7 +36,7 @@ export class EntityRegistryService { public getAll() { return this.entities; } - + // typed as DefaultEntity so constructor can be used with parameter checking. // CCEntity is abstract and doesn't allow using the constructor public getEntity(type: string): typeof DefaultEntity { diff --git a/webapp/src/app/services/phaser/entities/registry/event-trigger.ts b/webapp/src/app/services/phaser/entities/registry/event-trigger.ts index 09e5d96a..70021430 100644 --- a/webapp/src/app/services/phaser/entities/registry/event-trigger.ts +++ b/webapp/src/app/services/phaser/entities/registry/event-trigger.ts @@ -2,35 +2,36 @@ import { EventWidgetComponent } from '../../../../components/widgets/event-widge import { CCEntity, EntityAttributes, ScaleSettings } from '../cc-entity'; export class EventTrigger extends CCEntity { - private attributes: EntityAttributes = { eventType: { type: 'String', - description: 'Type of event. Cutscenes will stop the movement of the player and can\'t be executed in parallel. ' - + 'Auto Control events will take over the player\'s control.', + description: + "Type of event. Cutscenes will stop the movement of the player and can't be executed in parallel. " + + "Auto Control events will take over the player's control.", options: { PARALLEL: 1, CUTSCENE: 2, INTERRUPTABLE: 3, AUTO_CONTROL: 4, - COMBAT_CUTSCENE: 5 - } + COMBAT_CUTSCENE: 5, + }, }, startCondition: { type: 'VarCondition', description: 'Condition for the event to start', - bd: true + bd: true, }, endCondition: { type: 'VarCondition', - description: 'Condition for the event to not start (even if start condition is true)', + description: + 'Condition for the event to not start (even if start condition is true)', bd: true, - default: 'false' + default: 'false', }, event: { type: 'Event', description: 'Event to be performed', - bd: true + bd: true, }, triggerType: { type: 'String', @@ -38,24 +39,25 @@ export class EventTrigger extends CCEntity { options: { ALWAYS: 0, ONCE_PER_ENTRY: 1, - ONCE: 2 - } + ONCE: 2, + }, }, loadCondition: { type: 'EventLoadCondition', - description: 'If true: condition is checked on map entry and event is only loaded if true. onStart => Use start conditions, custom => use custom conditions', - I: true - } + description: + 'If true: condition is checked on map entry and event is only loaded if true. onStart => Use start conditions, custom => use custom conditions', + I: true, + }, }; - + public getAttributes(): EntityAttributes { return this.attributes; } - + getScaleSettings(): ScaleSettings | undefined { return undefined; } - + protected async setupType() { this.generateNoImageType(0x0032ff, 0.7); } diff --git a/webapp/src/app/services/phaser/entities/registry/item-destruct.ts b/webapp/src/app/services/phaser/entities/registry/item-destruct.ts index 0c52b2d7..f3b2ae46 100644 --- a/webapp/src/app/services/phaser/entities/registry/item-destruct.ts +++ b/webapp/src/app/services/phaser/entities/registry/item-destruct.ts @@ -8,9 +8,7 @@ import { SheetReference } from './destructible'; import { GlobalSettings } from '../../global-settings'; import { Globals } from '../../../globals'; -export interface ItemDestructTypes { - [name: string]: ItemDestructType; -} +export type ItemDestructTypes = Record; export interface ItemDestructType { size: Point3; @@ -33,7 +31,6 @@ export interface ItemDestructAttributes { } export class ItemDestruct extends DefaultEntity { - override getAttributes(): EntityAttributes { const attributes = super.getAttributes(); attributes['desType'].type = 'CustomDesType'; @@ -43,51 +40,58 @@ export class ItemDestruct extends DefaultEntity { }; const objOrder: { [key in keyof ItemDestructAttributes]: null } = { desType: null, - __GLOBAL__: null + __GLOBAL__: null, }; return Object.assign(objOrder, attributes); } - + protected override async setupType(settings: any) { - const globalSettings = await Helper.getJsonPromise('data/global-settings') as GlobalSettings.GlobalSettings; + const globalSettings = (await Helper.getJsonPromise( + 'data/global-settings', + )) as GlobalSettings.GlobalSettings; let desType = ''; if (settings.desType) { desType = settings.desType; } else { - const config = globalSettings.ENTITY.ItemDestruct[settings.__GLOBAL__]; + const config = + globalSettings.ENTITY.ItemDestruct[settings.__GLOBAL__]; if (config) { desType = config.desType; } } - const destructibles = await Globals.jsonLoader.loadJsonMerged('destructibles.json'); + const destructibles = + await Globals.jsonLoader.loadJsonMerged( + 'destructibles.json', + ); const type = destructibles[desType]; if (!type) { - this.generateNoImageType(0xFF0000, 1); + this.generateNoImageType(0xff0000, 1); return; } const animSheet = type.anims.sheet as AnimSheet; - const gfx = (typeof animSheet === 'string') ? animSheet : animSheet.src; - + const gfx = typeof animSheet === 'string' ? animSheet : animSheet.src; + const exists = await Helper.loadTexture(gfx, this.scene); - + if (!exists) { this.generateErrorImage(); return; } - - this.entitySettings = { + + this.entitySettings = { sheets: { - fix: [{ - gfx: gfx, - x: animSheet.offX || 0, - y: animSheet.offY || 0, - w: animSheet.width || 0, - h: animSheet.height || 0, - }] + fix: [ + { + gfx: gfx, + x: animSheet.offX || 0, + y: animSheet.offY || 0, + w: animSheet.width || 0, + h: animSheet.height || 0, + }, + ], }, - baseSize: type.size - }; + baseSize: type.size, + } as any; this.updateSettings(); - } } diff --git a/webapp/src/app/services/phaser/entities/registry/npc-templates.ts b/webapp/src/app/services/phaser/entities/registry/npc-templates.ts index fac10c61..9a6a53de 100644 --- a/webapp/src/app/services/phaser/entities/registry/npc-templates.ts +++ b/webapp/src/app/services/phaser/entities/registry/npc-templates.ts @@ -1,9 +1,7 @@ import { Anims, SubJsonParam } from '../../sheet-parser'; import { Configs, Face, WalkAnimSet } from './npc'; -export interface NPCTemplates { - [key: string]: NPCTemplate; -} +export type NPCTemplates = Record; export interface NPCTemplate { name: SubJsonParam; @@ -19,158 +17,246 @@ export interface NPCTemplate { export function getNPCTemplates(): NPCTemplates { return { NPCBasic: { - 'name': {'jsonPARAM': 'name', 'default': null}, - 'gender': {'jsonPARAM': 'gender', 'default': null}, - 'animSheet': { - 'DOCTYPE': 'MULTI_DIR_ANIMATION', - 'namedSheets': { - // @ts-ignore - 'move': {'src': {'jsonPARAM': 'img'}, 'width': 32, 'height': 40, 'xCount': 3, 'offX': {'jsonPARAM': 'x'}, 'offY': {'jsonPARAM': 'y'}}, - // @ts-ignore - 'sit': {'jsonIF': 'sitX', 'src': {'jsonPARAM': 'img'}, 'width': 32, 'height': 40, 'xCount': 1, 'offX': {'jsonPARAM': 'sitX'}, 'offY': {'jsonPARAM': 'sitY'}} + name: { jsonPARAM: 'name', default: null }, + gender: { jsonPARAM: 'gender', default: null }, + animSheet: { + DOCTYPE: 'MULTI_DIR_ANIMATION', + namedSheets: { + move: { + // @ts-expect-error types don't consider jsonPARAM + src: { jsonPARAM: 'img' }, + width: 32, + height: 40, + xCount: 3, + // @ts-expect-error types don't consider jsonPARAM + offX: { jsonPARAM: 'x' }, + // @ts-expect-error types don't consider jsonPARAM + offY: { jsonPARAM: 'y' }, + }, + sit: { + jsonIF: 'sitX', + // @ts-expect-error types don't consider jsonPARAM + src: { jsonPARAM: 'img' }, + width: 32, + height: 40, + xCount: 1, + // @ts-expect-error types don't consider jsonPARAM + offX: { jsonPARAM: 'sitX' }, + // @ts-expect-error types don't consider jsonPARAM + offY: { jsonPARAM: 'sitY' }, + }, }, - 'shapeType': 'Y_FLAT', - 'offset': {'x': 0, 'y': -2, 'z': 0}, - 'SUB': [ + shapeType: 'Y_FLAT', + offset: { x: 0, y: -2, z: 0 }, + SUB: [ { - 'sheet': 'move', - 'dirs': 4, - 'flipX': [0, 0, 0, 1], - 'tileOffsets': [0, 3, 6, 3], - 'SUB': [ - {'name': 'idle', 'time': 1, 'repeat': false, 'frames': [1]}, - {'name': 'walk', 'time': 0.133, 'repeat': true, 'frames': [0, 1, 2, 1]} - ] + sheet: 'move', + dirs: 4, + flipX: [0, 0, 0, 1], + tileOffsets: [0, 3, 6, 3], + SUB: [ + { + name: 'idle', + time: 1, + repeat: false, + frames: [1], + }, + { + name: 'walk', + time: 0.133, + repeat: true, + frames: [0, 1, 2, 1], + }, + ], }, { - 'jsonIF': 'sitX', - 'sheet': 'sit', - 'dirs': 4, - 'flipX': [0, 0, 0, 1], - 'tileOffsets': [0, 1, 2, 1], - 'SUB': [ - {'name': 'sit', 'time': 1, 'repeat': false, 'frames': [0]} - ] - } - ] + jsonIF: 'sitX', + sheet: 'sit', + dirs: 4, + flipX: [0, 0, 0, 1], + tileOffsets: [0, 1, 2, 1], + SUB: [ + { + name: 'sit', + time: 1, + repeat: false, + frames: [0], + }, + ], + }, + ], }, - 'walkAnimSet': { - 'normal': { - 'idle': 'idle', - 'move': 'walk' + walkAnimSet: { + normal: { + idle: 'idle', + move: 'walk', + }, + sit: { + jsonIF: 'sitX', + idle: 'sit', }, - 'sit': { - 'jsonIF': 'sitX', - 'idle': 'sit' - } }, - 'walkAnims': 'normal', - 'configs': { - 'normal': { - 'relativeVel': 0.5 + walkAnims: 'normal', + configs: { + normal: { + relativeVel: 0.5, + }, + sit: { + jsonIF: 'sitX', + walkAnims: 'sit', + shadow: 0, }, - 'sit': { - 'jsonIF': 'sitX', - 'walkAnims': 'sit', - 'shadow': 0 - } }, - 'face': {'ABSTRACT': {'jsonPARAM': 'face'}} + face: { ABSTRACT: { jsonPARAM: 'face' } }, }, NPCAvatarSimple: { - 'name': {'jsonPARAM': 'name', 'default': null}, - 'realname': {'jsonPARAM': 'realname', 'default': null}, - 'gender': {'jsonPARAM': 'gender', 'default': null}, - 'animSheet': { - 'DOCTYPE': 'MULTI_DIR_ANIMATION', - 'namedSheets': { - // @ts-ignore - 'move': {'src': {'jsonPARAM': 'img'}, 'width': 32, 'height': 40, 'xCount': 3, 'offX': {'jsonPARAM': 'x'}, 'offY': {'jsonPARAM': 'y'}}, - // @ts-ignore - 'offline': {'src': {'jsonPARAM': 'img'}, 'width': 32, 'height': 40, 'xCount': 3, 'offX': {'jsonPARAM': 'offlineX'}, 'offY': {'jsonPARAM': 'offlineY'}}, - // @ts-ignore - 'run': { - 'jsonIF': 'runSrc', - // @ts-ignore - 'src': {'jsonPARAM': 'runSrc'}, - 'width': 32, - 'height': 40, - 'xCount': 5, - // @ts-ignore - 'offX': {'jsonPARAM': 'runX'}, - // @ts-ignore - 'offY': {'jsonPARAM': 'runY'} - } + name: { jsonPARAM: 'name', default: null }, + realname: { jsonPARAM: 'realname', default: null }, + gender: { jsonPARAM: 'gender', default: null }, + animSheet: { + DOCTYPE: 'MULTI_DIR_ANIMATION', + namedSheets: { + move: { + // @ts-expect-error types don't consider jsonPARAM + src: { jsonPARAM: 'img' }, + width: 32, + height: 40, + xCount: 3, + // @ts-expect-error types don't consider jsonPARAM + offX: { jsonPARAM: 'x' }, + // @ts-expect-error types don't consider jsonPARAM + offY: { jsonPARAM: 'y' }, + }, + offline: { + // @ts-expect-error types don't consider jsonPARAM + src: { jsonPARAM: 'img' }, + width: 32, + height: 40, + xCount: 3, + // @ts-expect-error types don't consider jsonPARAM + offX: { jsonPARAM: 'offlineX' }, + // @ts-expect-error types don't consider jsonPARAM + offY: { jsonPARAM: 'offlineY' }, + }, + run: { + jsonIF: 'runSrc', + // @ts-expect-error types don't consider jsonPARAM + src: { jsonPARAM: 'runSrc' }, + width: 32, + height: 40, + xCount: 5, + // @ts-expect-error types don't consider jsonPARAM + offX: { jsonPARAM: 'runX' }, + // @ts-expect-error types don't consider jsonPARAM + offY: { jsonPARAM: 'runY' }, + }, }, - 'shapeType': 'Y_FLAT', - 'offset': {'x': 0, 'y': -2, 'z': 0}, - 'SUB': [ + shapeType: 'Y_FLAT', + offset: { x: 0, y: -2, z: 0 }, + SUB: [ { - 'sheet': 'move', - 'dirs': 4, - 'flipX': [0, 0, 0, 1], - 'tileOffsets': [0, 3, 6, 3], - 'SUB': [ - {'name': 'idle', 'time': 1, 'repeat': false, 'frames': [1]}, - {'name': 'walk', 'time': 0.133, 'repeat': true, 'frames': [0, 1, 2, 1]}, - {'sheet': 'offline', 'name': 'offline', 'time': 0.166, 'repeat': true, 'frames': [0, 1, 2]} - ] + sheet: 'move', + dirs: 4, + flipX: [0, 0, 0, 1], + tileOffsets: [0, 3, 6, 3], + SUB: [ + { + name: 'idle', + time: 1, + repeat: false, + frames: [1], + }, + { + name: 'walk', + time: 0.133, + repeat: true, + frames: [0, 1, 2, 1], + }, + { + sheet: 'offline', + name: 'offline', + time: 0.166, + repeat: true, + frames: [0, 1, 2], + }, + ], }, { - 'jsonIF': 'runSrc', - 'sheet': 'run', - 'dirs': 6, - 'flipX': [0, 0, 0, 1, 1, 1], - 'tileOffsets': [0, 5, 10, 10, 5, 0], - 'SUB': [ - {'name': 'run', 'time': 0.1, 'repeat': true, 'frames': [0, 1, 2, 3]}, - {'name': 'jump', 'time': 0.1, 'repeat': true, 'frames': [3]}, - {'name': 'fall', 'time': 0.1, 'repeat': true, 'frames': [4]} - ] + jsonIF: 'runSrc', + sheet: 'run', + dirs: 6, + flipX: [0, 0, 0, 1, 1, 1], + tileOffsets: [0, 5, 10, 10, 5, 0], + SUB: [ + { + name: 'run', + time: 0.1, + repeat: true, + frames: [0, 1, 2, 3], + }, + { + name: 'jump', + time: 0.1, + repeat: true, + frames: [3], + }, + { + name: 'fall', + time: 0.1, + repeat: true, + frames: [4], + }, + ], }, { - 'sheet': 'move', - 'dirs': 2, - 'flipX': [0, 1], - 'tileOffsets': [9, 9], - 'SUB': [ - {'name': 'ground', 'time': 1, 'repeat': false, 'frames': [0], 'offset': {'x': 0, 'y': 2, 'z': 0}} - ] - } - ] + sheet: 'move', + dirs: 2, + flipX: [0, 1], + tileOffsets: [9, 9], + SUB: [ + { + name: 'ground', + time: 1, + repeat: false, + frames: [0], + offset: { x: 0, y: 2, z: 0 }, + }, + ], + }, + ], }, - 'walkAnimSet': { - 'normal': { - 'idle': 'idle', - 'move': 'walk', - 'run': {'jsonIF': 'runSrc', 'jsonTHEN': 'run'}, - 'jump': {'jsonIF': 'runSrc', 'jsonTHEN': 'jump'}, - 'fall': {'jsonIF': 'runSrc', 'jsonTHEN': 'fall'} + walkAnimSet: { + normal: { + idle: 'idle', + move: 'walk', + run: { jsonIF: 'runSrc', jsonTHEN: 'run' }, + jump: { jsonIF: 'runSrc', jsonTHEN: 'jump' }, + fall: { jsonIF: 'runSrc', jsonTHEN: 'fall' }, }, - 'ground': { - 'idle': 'ground' + ground: { + idle: 'ground', + }, + offline: { + idle: 'offline', }, - 'offline': { - 'idle': 'offline' - } }, - 'walkAnims': 'normal', - 'configs': { - 'normal': { - 'relativeVel': 0.5 + walkAnims: 'normal', + configs: { + normal: { + relativeVel: 0.5, + }, + run: { + jsonIF: 'runSrc', + relativeVel: 1, }, - 'run': { - 'jsonIF': 'runSrc', - 'relativeVel': 1 + ground: { + walkAnims: 'ground', }, - 'ground': { - 'walkAnims': 'ground' + offline: { + walkAnims: 'offline', }, - 'offline': { - 'walkAnims': 'offline' - } }, - 'face': {'ABSTRACT': {'jsonPARAM': 'face'}} - } + face: { ABSTRACT: { jsonPARAM: 'face' } }, + }, }; } diff --git a/webapp/src/app/services/phaser/entities/registry/npc.ts b/webapp/src/app/services/phaser/entities/registry/npc.ts index a74097e3..1c1133da 100644 --- a/webapp/src/app/services/phaser/entities/registry/npc.ts +++ b/webapp/src/app/services/phaser/entities/registry/npc.ts @@ -1,11 +1,20 @@ -import { NPCState, NPCStatesWidgetComponent } from '../../../../components/widgets/npc-states-widget/npc-states-widget.component'; +import { + NPCState, + NPCStatesWidgetComponent, +} from '../../../../components/widgets/npc-states-widget/npc-states-widget.component'; import { Point, Point3 } from '../../../../models/cross-code-map'; +import { Label } from '../../../../models/events'; +import { Globals } from '../../../globals'; import { Helper } from '../../helper'; +import { + Anims, + flattenSUBs, + IfThen, + prepareSheet, + SubJsonParam, +} from '../../sheet-parser'; import { DefaultEntity } from './default-entity'; -import { Label } from '../../../../models/events'; -import { Anims, flattenSUBs, IfThen, prepareSheet, SubJsonParam } from '../../sheet-parser'; import { getNPCTemplates } from './npc-templates'; -import { Globals } from '../../../globals'; export interface CharacterSettings { jsonINSTANCE?: string; @@ -48,7 +57,7 @@ export interface CharacterSettings { export interface Face { [key: number]: string | undefined; - + width?: number; height?: number; centerX?: number; @@ -57,16 +66,12 @@ export interface Face { parts?: Part[]; expressions?: Expressions; ABSTRACT?: string | SubJsonParam; - subImages?: { [key: string]: string }; + subImages?: Record; } -export interface Part { - [key: string]: DetailPart | number; -} +export type Part = Record; -export interface Expressions { - [key: string]: Faces; -} +export type Expressions = Record; export interface Faces { faces: string[][]; @@ -85,18 +90,16 @@ export interface DetailPart { hideOnClip?: boolean; } -export interface WalkAnimSet { - [key: string]: { [key: string]: string | WalkAnimSetInner | undefined } | undefined; -} +export type WalkAnimSet = Record< + string, + Record | undefined +>; export interface WalkAnimSetInner extends IfThen { [key: string]: string | undefined; } - -export interface Configs { - [key: string]: ConfigSet; -} +export type Configs = Record; export interface ConfigSet { relativeVel?: number; @@ -127,10 +130,9 @@ export const FACE8 = { SOUTH: 4, SOUTH_WEST: 5, WEST: 6, - NORTH_WEST: 7 + NORTH_WEST: 7, }; - export interface NpcAttributes { characterName?: string; npcStates?: Partial[]; @@ -144,50 +146,69 @@ export interface Analyzable { } export class NPC extends DefaultEntity { - protected override async setupType(settings: NpcAttributes) { - - let charSettings = await Helper.getJsonPromise(this.getPath('data/characters/', settings.characterName)) as CharacterSettings | undefined; + let charSettings = (await Helper.getJsonPromise( + this.getPath('data/characters/', settings.characterName), + )) as CharacterSettings | undefined; if (!charSettings) { - console.warn(`no char settings found for character name: [${settings.characterName}]`); + console.warn( + `no char settings found for character name: [${settings.characterName}]`, + ); this.generateNoImageType(); return; } if (typeof charSettings.animSheet === 'string') { - const path = this.getPath('data/animations/', charSettings.animSheet); - charSettings.animSheet = await Helper.getJsonPromise(path) as Anims; + const path = this.getPath( + 'data/animations/', + charSettings.animSheet, + ); + const sheetName = charSettings.animSheet; + charSettings.animSheet = (await Helper.getJsonPromise( + path, + )) as Anims; if (!charSettings.animSheet) { - throw new Error('no anim sheet found for: ' + charSettings.animSheet + ' in path: ' + path); + throw new Error( + 'no anim sheet found for: ' + + sheetName + + ' in path: ' + + path, + ); } } - + charSettings.jsonTEMPLATES = getNPCTemplates(); charSettings = prepareSheet(charSettings); delete charSettings.jsonTEMPLATES; - + if (typeof charSettings.animSheet === 'string') { throw new Error('should never be string'); } - + const state = settings.npcStates?.[0] ?? {}; const config = state.config ?? 'normal'; const face = state.face || 'SOUTH'; const walkAnims = charSettings.configs?.[config]?.walkAnims ?? 'normal'; - const animSet = charSettings.walkAnimSet?.[walkAnims] || Object.values(charSettings.walkAnimSet ?? {})[0]; - - const usedSet = animSet?.['idle'] || animSet?.['move'] || Object.values(animSet ?? {})[0]; - + const animSet = + charSettings.walkAnimSet?.[walkAnims] || + Object.values(charSettings.walkAnimSet ?? {})[0]; + + const usedSet = + animSet?.['idle'] || + animSet?.['move'] || + Object.values(animSet ?? {})[0]; + const subName = usedSet as string; - - + if (!Array.isArray(charSettings.animSheet?.SUB)) { - console.warn(`animSheet is not an array, abort: [${settings.characterName}]`); + console.warn( + `animSheet is not an array, abort: [${settings.characterName}]`, + ); this.generateNoImageType(); return; } - const subs = flattenSUBs(charSettings.animSheet!, {}); - - let sub = subs.find(v => v.name === subName); + const subs = flattenSUBs(charSettings.animSheet, {}); + + let sub = subs.find((v) => v.name === subName); if (!sub) { sub = subs[0]; } @@ -197,27 +218,28 @@ export class NPC extends DefaultEntity { return; } const exists = await Helper.loadTexture(sheet?.src, this.scene); - + if (!exists) { this.generateErrorImage(); return; } - + let x = sheet.offX ?? 0; let y = sheet.offY ?? 0; let flipX = false; - + let dirIndex = 0; - const subDirs = typeof sub.dirs === 'string' ? parseInt(sub.dirs, 10) : sub.dirs; + const subDirs = + typeof sub.dirs === 'string' ? parseInt(sub.dirs, 10) : sub.dirs; if (subDirs === 8) { dirIndex = FACE8[face]; } else if (subDirs === 4) { dirIndex = FACE4[face as keyof typeof FACE4]; } - + const img = Globals.scene.textures.get(sheet.src).getSourceImage(); const xCount = sheet.xCount ?? img.width / sheet.width; - + // flip x with some serious type checking if (sub.flipX) { if (typeof sub.flipX === 'boolean') { @@ -229,29 +251,31 @@ export class NPC extends DefaultEntity { } } } - + const tileOffsets = sub.tileOffsets; const idleFrame = sub.frames?.[0] ?? 0; const offset = (tileOffsets?.[dirIndex] ?? 0) + idleFrame; - + x += (offset % xCount) * sheet.width; y += Math.floor(offset / xCount) * sheet.height; - + this.entitySettings.sheets = { - fix: [{ - gfx: sheet?.src, - x: x, - y: y, - w: sheet?.width ?? 16, - h: sheet?.height ?? 16, - flipX: flipX, - flipY: false - }] + fix: [ + { + gfx: sheet?.src, + x: x, + y: y, + w: sheet?.width ?? 16, + h: sheet?.height ?? 16, + flipX: flipX, + flipY: false, + }, + ], }; - this.entitySettings.baseSize = {x: 12, y: 12, z: 28}; + this.entitySettings.baseSize = { x: 12, y: 12, z: 28 }; this.updateSettings(); } - + private getPath(prefix: string, path?: string): string { if (!path) { path = ''; @@ -260,8 +284,7 @@ export class NPC extends DefaultEntity { const name = split.splice(-1, 1)[0]; return prefix + split.join('/') + '/' + name; } - - + public override doubleClick(): void { (this.widgets['npcStates'] as NPCStatesWidgetComponent).open(); } diff --git a/webapp/src/app/services/phaser/entities/registry/prop.ts b/webapp/src/app/services/phaser/entities/registry/prop.ts index f2feca20..a732604c 100644 --- a/webapp/src/app/services/phaser/entities/registry/prop.ts +++ b/webapp/src/app/services/phaser/entities/registry/prop.ts @@ -1,7 +1,13 @@ import { Point3 } from '../../../../models/cross-code-map'; import { Helper } from '../../helper'; +import { + Anims, + AnimSheet, + prepareProp, + PropDef, + PropSheet, +} from '../../sheet-parser'; import { Fix } from '../cc-entity'; -import { Anims, AnimSheet, prepareProp, PropDef, PropSheet } from '../../sheet-parser'; import { DefaultEntity } from './default-entity'; export interface PropType { @@ -32,21 +38,21 @@ interface PropSprite { } export class Prop extends DefaultEntity { - protected override async setupType(settings: PropAttributes) { if (!settings.propType) { console.warn('prop without prop type'); return this.generateErrorImage(); } - const sheet = await Helper.getJsonPromise('data/props/' + settings.propType.sheet) as PropSheet; + const sheet = (await Helper.getJsonPromise( + 'data/props/' + settings.propType.sheet, + )) as PropSheet; if (!sheet) { console.warn('prop without sheet', settings); return this.generateErrorImage(); } - + let prop: PropDef | undefined; - for (let i = 0; i < sheet.props.length; i++) { - const p = sheet.props[i]; + for (const p of sheet.props) { if (settings.propType.name === p.name) { prop = p; break; @@ -56,8 +62,8 @@ export class Prop extends DefaultEntity { console.error('prop not found: ' + settings.propType.name); return this.generateErrorImage(); } - - this.entitySettings = {sheets: {fix: []}} as any; + + this.entitySettings = { sheets: { fix: [] } } as any; if (prop.anims) { await this.setupAnims(settings, prop, sheet); } else if (prop.fix) { @@ -66,7 +72,7 @@ export class Prop extends DefaultEntity { console.error('prop image does not exist: ' + prop.fix.gfx); return this.generateErrorImage(); } - + this.entitySettings.sheets.fix[0] = prop.fix; this.entitySettings.sheets.renderMode = prop.fix.renderMode; } else { @@ -77,22 +83,31 @@ export class Prop extends DefaultEntity { this.entitySettings.collType = prop.collType; this.updateSettings(); } - - private async setupAnims(settings: PropAttributes, propDef: PropDef, sheetDef: PropSheet) { - + + private async setupAnims( + settings: PropAttributes, + propDef: PropDef, + sheetDef: PropSheet, + ) { const sprites: PropSprite[] = []; - + const anims: Anims = prepareProp(propDef, sheetDef); - + const propAnim = settings.propAnim || 'default'; - + if (propAnim === 'floor4') { console.log('as'); } - + if (Array.isArray(anims.SUB)) { - const firstName = this.setupAnim(propAnim, anims, propDef, {}, sprites); - + const firstName = this.setupAnim( + propAnim, + anims, + propDef, + {}, + sprites, + ); + // no sheet found with propAnim. Just take first one if (sprites.length === 0 && firstName) { this.setupAnim(firstName, anims, propDef, {}, sprites); @@ -103,38 +118,39 @@ export class Prop extends DefaultEntity { alpha: anims.framesAlpha?.[0] ?? 1, tileOffset: anims.tileOffset ?? 0, renderMode: anims.renderMode, - offset: anims.offset + offset: anims.offset, }); } - + if (sprites.length === 0) { console.warn('failed creating prop: ', settings); return this.generateErrorImage(); } - + this.entitySettings.sheets.fix = []; for (const sprite of sprites) { - if (!sprite.sheet) { console.error('prop sheet not found, ', propDef.name); return this.generateErrorImage(); } - + await Helper.loadTexture(sprite.sheet.src, this.scene); - + const fix: Fix = { gfx: sprite.sheet.src, w: sprite.sheet.width, h: sprite.sheet.height, - x: sprite.sheet.width * sprite.tileOffset + (sprite.sheet.offX || 0), + x: + sprite.sheet.width * sprite.tileOffset + + (sprite.sheet.offX || 0), y: sprite.sheet.offY || 0, alpha: sprite.alpha, offsetX: 0, offsetY: 0, flipX: sprite.flipX, - renderMode: sprite.renderMode + renderMode: sprite.renderMode, }; - + if (sprite.offset) { fix.offsetX = sprite.offset.x || 0; fix.offsetY = (sprite.offset.y || 0) - (sprite.offset.z || 0); @@ -142,19 +158,31 @@ export class Prop extends DefaultEntity { this.entitySettings.sheets.fix.push(fix); } } - - private setupAnim(propAnim: string, anims: Anims, propDef: PropDef, settings: Anims, sprites: PropSprite[]): string | undefined { + + private setupAnim( + propAnim: string, + anims: Anims, + propDef: PropDef, + settings: Anims, + sprites: PropSprite[], + ): string | undefined { let firstName = anims.name; if (anims.name && anims.name !== propAnim) { return firstName; } settings = { ...settings, - ...anims + ...anims, }; if (Array.isArray(anims.SUB)) { for (const sub of anims.SUB) { - const animName = this.setupAnim(propAnim, sub, propDef, settings, sprites); + const animName = this.setupAnim( + propAnim, + sub, + propDef, + settings, + sprites, + ); if (!firstName) { firstName = animName; } @@ -171,26 +199,26 @@ export class Prop extends DefaultEntity { console.error('anim sheet not found, skip: ', propDef); return firstName; } - + const offset: Point3 = { x: 0, y: 0, z: 0, - ...settings.offset + ...settings.offset, }; - + // not sure about this one, fixes chair in propType: "booth", sheet: "trading-autumn" if (settings.wallY) { offset.y += settings.wallY * (settings.size?.z ?? 0); } - + if (settings.gfxOffset) { offset.x += settings.gfxOffset.x ?? 0; offset.y += settings.gfxOffset.y ?? 0; } - + const frame = settings.frames?.[0] ?? 0; - + if (frame > 0) { const xCount = sheet.xCount || 999; const xOffset = (frame % xCount) * sheet.width; @@ -198,14 +226,16 @@ export class Prop extends DefaultEntity { sheet.offX = (sheet.offX ?? 0) + xOffset; sheet.offY = (sheet.offY ?? 0) + yOffset; } - + sprites.push({ sheet: sheet, alpha: settings.framesAlpha?.[frame] ?? 1, offset: offset, tileOffset: settings.tileOffset ?? 0, renderMode: settings.renderMode, - flipX: Array.isArray(settings.flipX) ? !!settings.flipX[frame] : settings.flipX + flipX: Array.isArray(settings.flipX) + ? !!settings.flipX[frame] + : settings.flipX, }); return firstName; } diff --git a/webapp/src/app/services/phaser/entities/registry/scalable-prop.ts b/webapp/src/app/services/phaser/entities/registry/scalable-prop.ts index 85d4a3f1..1a7429a2 100644 --- a/webapp/src/app/services/phaser/entities/registry/scalable-prop.ts +++ b/webapp/src/app/services/phaser/entities/registry/scalable-prop.ts @@ -1,7 +1,12 @@ import { Point, Point3 } from '../../../../models/cross-code-map'; import { Helper } from '../../helper'; import { Fix } from '../cc-entity'; -import { BallKill, Effects, prepareScalableProp, ScalablePropSheet } from '../../sheet-parser'; +import { + BallKill, + Effects, + prepareScalableProp, + ScalablePropSheet, +} from '../../sheet-parser'; import { DefaultEntity } from './default-entity'; export interface ScalablePropDef { @@ -54,9 +59,7 @@ export interface EntryGfxEnds { south?: GfxEndsDir; } -export interface GfxEndsDir { - [key: string]: Patterns; -} +export type GfxEndsDir = Record; export interface ScalablePropConfig { sheet?: string; @@ -68,7 +71,7 @@ export interface ScalablePropConfig { export interface ScalablePropAttributes { propConfig?: ScalablePropConfig; - + // TODO: not implemented, probably easier with Phaser.GameObjects.TileSprite patternOffset?: Point; timeOffset?: number; @@ -79,13 +82,12 @@ export interface ScalablePropAttributes { } export class ScalableProp extends DefaultEntity { - private _onlyEnds = false; - + set onlyEnds(val: boolean) { this._onlyEnds = val; } - + protected override async setupType(settings: ScalablePropAttributes) { const propConfig = settings.propConfig; if (!propConfig) { @@ -93,14 +95,16 @@ export class ScalableProp extends DefaultEntity { this.resetScaleSettings(); return this.generateErrorImage(); } - const sheet = await Helper.getJsonPromise('data/scale-props/' + propConfig.sheet) as ScalablePropSheet | undefined; - + const sheet = (await Helper.getJsonPromise( + 'data/scale-props/' + propConfig.sheet, + )) as ScalablePropSheet | undefined; + if (!sheet) { console.warn('sheet not found: ' + propConfig.sheet); this.resetScaleSettings(); return this.generateErrorImage(); } - + let prop: ScalablePropDef = sheet.entries[propConfig.name!]; if (!prop) { console.error('scale-prop not found: ' + propConfig.name); @@ -108,26 +112,28 @@ export class ScalableProp extends DefaultEntity { return this.generateErrorImage(); } prop = prepareScalableProp(prop, sheet); - - this.entitySettings = {}; - + + this.entitySettings = {} as any; + const scaleSettings = this.getScaleSettings()!; - + scaleSettings.scalableX = prop.scalableX!; scaleSettings.scalableY = prop.scalableY!; scaleSettings.scalableStep = prop.scalableStep!; scaleSettings.baseSize = prop.baseSize!; - - const size = (this.details.settings['size'] as Point | undefined) ?? {x: 1, y: 1}; - + + const size = (this.details.settings['size'] as Point | undefined) ?? { + x: 1, + y: 1, + }; + if (!scaleSettings.scalableX) { size.x = scaleSettings.baseSize.x; } if (!scaleSettings.scalableY) { size.y = scaleSettings.baseSize.y; } - - + let scaleableFix: Fix | undefined; if (prop.gfx) { await Helper.loadTexture(prop.gfx, this.scene); @@ -150,13 +156,13 @@ export class ScalableProp extends DefaultEntity { this.resetScaleSettings(); return this.generateErrorImage(); } - + const scale = { x: 0, y: 0, - ...this.details.settings['size'] + ...this.details.settings['size'], } as Point; - + for (const [d, key] of Object.entries(propConfig.ends ?? {})) { if (!key) { continue; @@ -201,19 +207,19 @@ export class ScalableProp extends DefaultEntity { break; } } - + Object.assign(this.entitySettings, scaleSettings); this.entitySettings.collType = prop.collType!; this.entitySettings.pivot = prop.pivot!; this.updateSettings(); } - + private resetScaleSettings() { const scaleSettings = this.getScaleSettings()!; - + scaleSettings.scalableX = true; scaleSettings.scalableY = true; scaleSettings.scalableStep = 1; - scaleSettings.baseSize = {x: 1, y: 1}; + scaleSettings.baseSize = { x: 1, y: 1 }; } } diff --git a/webapp/src/app/services/phaser/entities/selection-box.ts b/webapp/src/app/services/phaser/entities/selection-box.ts index a637c33d..4d3d3aa8 100644 --- a/webapp/src/app/services/phaser/entities/selection-box.ts +++ b/webapp/src/app/services/phaser/entities/selection-box.ts @@ -2,37 +2,36 @@ import { Point } from '../../../models/cross-code-map'; import { CCEntity } from './cc-entity'; export class SelectionBox { - private active = false; - private start: Point = {x: 0, y: 0}; + private start: Point = { x: 0, y: 0 }; private scene: Phaser.Scene; private graphics: Phaser.GameObjects.Graphics; private selectedEntities: Set; - + constructor(scene: Phaser.Scene) { this.scene = scene; this.selectedEntities = new Set(); this.graphics = scene.add.graphics({ fillStyle: { color: 0x3335ed, - alpha: 0.3 + alpha: 0.3, }, lineStyle: { width: 1, color: 0x3335ed, - alpha: 0.8 - } + alpha: 0.8, + }, }); this.graphics.depth = 10000; } - + public onInputDown(pointer: Phaser.Input.Pointer) { this.active = true; this.start.x = pointer.worldX; this.start.y = pointer.worldY; this.selectedEntities.clear(); } - + public update(entities: CCEntity[]) { if (!this.active) { return; @@ -40,7 +39,7 @@ export class SelectionBox { const posX = this.scene.input.activePointer.worldX; const posY = this.scene.input.activePointer.worldY; const start = this.start; - + let x1 = start.x; let y1 = start.y; let x2 = posX; @@ -55,15 +54,20 @@ export class SelectionBox { y1 = y2; y2 = tmp; } - + const rect = new Phaser.Geom.Rectangle(x1, y1, x2 - x1, y2 - y1); this.graphics.clear(); this.graphics.fillRect(rect.x, rect.y, rect.width, rect.height); this.graphics.strokeRect(rect.x, rect.y, rect.width, rect.height); - + // TODO: super inefficient - entities.forEach(e => { - if (Phaser.Geom.Intersects.RectangleToRectangle(rect, e.getBoundingBox())) { + entities.forEach((e) => { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + rect, + e.getBoundingBox(), + ) + ) { if (!e.active) { return; } @@ -75,7 +79,7 @@ export class SelectionBox { } }); } - + public onInputUp(): Set { if (!this.active) { return new Set(); diff --git a/webapp/src/app/services/phaser/entity-grid.ts b/webapp/src/app/services/phaser/entity-grid.ts index a3498333..6c5abb52 100644 --- a/webapp/src/app/services/phaser/entity-grid.ts +++ b/webapp/src/app/services/phaser/entity-grid.ts @@ -6,17 +6,19 @@ import { Point } from '../../models/cross-code-map'; export class EntityGrid extends Phaser.GameObjects.GameObject { private sub: Subscription; private grid?: Phaser.GameObjects.Grid; - + constructor(scene: Scene) { super(scene, 'EntityGrid'); - + this.sub = merge( Globals.globalEventsService.gridSettings, Globals.mapLoaderService.map, - Globals.globalEventsService.resizeMap - ).pipe(debounceTime(1)).subscribe(() => this.updateGrid()); + Globals.globalEventsService.resizeMap, + ) + .pipe(debounceTime(1)) + .subscribe(() => this.updateGrid()); } - + updateGrid() { const settings = Globals.gridSettings(); const scale = 4; @@ -26,24 +28,24 @@ export class EntityGrid extends Phaser.GameObjects.GameObject { } const pos: Point = { x: settings.offset.x % settings.size.x, - y: settings.offset.y % settings.size.y + y: settings.offset.y % settings.size.y, }; - + if (pos.x > 0) { pos.x -= settings.size.x; } if (pos.y > 0) { pos.y -= settings.size.y; } - + const width = Globals.map.mapWidth * Globals.TILE_SIZE - pos.x; const height = Globals.map.mapHeight * Globals.TILE_SIZE - pos.y; - + let color = parseInt(settings.color.substring(1), 16); if (isNaN(color)) { color = 0x222222; } - + this.grid = new Phaser.GameObjects.Grid( this.scene, pos.x, @@ -55,15 +57,15 @@ export class EntityGrid extends Phaser.GameObjects.GameObject { undefined, 0, color, - 0.6 + 0.6, ); this.grid.depth = 5000; this.grid.setOrigin(0, 0); this.grid.setScale(1 / scale, 1 / scale); - + this.scene.add.existing(this.grid); } - + override destroy(fromScene?: boolean) { super.destroy(fromScene); this.grid?.destroy(); diff --git a/webapp/src/app/services/phaser/global-settings.d.ts b/webapp/src/app/services/phaser/global-settings.d.ts index cf1c7fd3..ba64741b 100644 --- a/webapp/src/app/services/phaser/global-settings.d.ts +++ b/webapp/src/app/services/phaser/global-settings.d.ts @@ -2,17 +2,16 @@ import { Point } from '../../models/cross-code-map'; import { EnemyInfo } from './entities/registry/enemy'; export namespace GlobalSettings { - export interface GlobalSettings { ENTITY: Entity; } - + export interface Entity { - ItemDestruct: { [key: string]: ItemDestruct }; + ItemDestruct: Record; JumpPanel: JumpPanel; HiddenBlock: HiddenBlock; } - + export interface ItemDestruct { desType: string; items: Item[]; @@ -20,17 +19,17 @@ export namespace GlobalSettings { enemyInfo?: EnemyInfo; perma?: boolean; } - + export interface Item { id: string; prob: number; } - + // everything below here is probably useless export interface HiddenBlock { 'ramp-north': RampNorth; } - + export interface RampNorth { size: Point; shape: string; @@ -38,12 +37,12 @@ export namespace GlobalSettings { zHeight: number; terrain: string; } - + export interface JumpPanel { jump4: Jump; jump2: Jump; } - + export interface Jump { jumpHeight: string; } diff --git a/webapp/src/app/services/phaser/helper.ts b/webapp/src/app/services/phaser/helper.ts index 4de08ad6..808289e9 100644 --- a/webapp/src/app/services/phaser/helper.ts +++ b/webapp/src/app/services/phaser/helper.ts @@ -6,63 +6,79 @@ import { CCMapLayer } from './tilemap/cc-map-layer'; import Scene = Phaser.Scene; export class Helper { - public static worldToTile(x: number, y: number): Point { - const p: Point = {x: 0, y: 0}; - + const p: Point = { x: 0, y: 0 }; + p.x = Math.floor(x / Globals.TILE_SIZE); p.y = Math.floor(y / Globals.TILE_SIZE); - + return p; } - + public static getPointerPos(pointer: Phaser.Input.Pointer): Point { - return {x: pointer.worldX, y: pointer.worldY}; + return { x: pointer.worldX, y: pointer.worldY }; } - + public static getTilesetSize(scene: Phaser.Scene, tileset: string): Point { const img = scene.textures.get(tileset).source[0]; return { x: Math.ceil(img.width / Globals.TILE_SIZE), - y: Math.ceil(img.height / Globals.TILE_SIZE) + y: Math.ceil(img.height / Globals.TILE_SIZE), }; } - + public static indexToPoint(index: number, tileCountX: number): Point { index -= 1; return { x: index % tileCountX, - y: Math.floor(index / tileCountX) + y: Math.floor(index / tileCountX), }; } - + public static clamp(val: number, min: number, max: number) { return Math.min(Math.max(val, min), max); } - + public static clampToBounds(layer: CCMapLayer, p: Point) { p.x = Helper.clamp(p.x, 0, layer.details.width - 1); p.y = Helper.clamp(p.y, 0, layer.details.height - 1); } - + public static isInBounds(layer: CCMapLayer, p: Point): boolean { - return p.x >= 0 && p.y >= 0 && p.x < layer.details.width && p.y < layer.details.height; + return ( + p.x >= 0 && + p.y >= 0 && + p.x < layer.details.width && + p.y < layer.details.height + ); } - + public static isInBoundsP(bounds: Point, p: Point): boolean { return p.x >= 0 && p.y >= 0 && p.x < bounds.x && p.y < bounds.y; } - - public static drawRect(graphics: Phaser.GameObjects.Graphics, rect: Phaser.Geom.Rectangle, fillStyle: number, alpha: number, strokeStyle: number, strokeAlpha: number) { - const o = new Phaser.Geom.Rectangle(rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height); - + + public static drawRect( + graphics: Phaser.GameObjects.Graphics, + rect: Phaser.Geom.Rectangle, + fillStyle: number, + alpha: number, + strokeStyle: number, + strokeAlpha: number, + ) { + const o = new Phaser.Geom.Rectangle( + rect.x + 0.5, + rect.y + 0.5, + rect.width - 1, + rect.height, + ); + graphics.fillStyle(fillStyle, alpha); graphics.fillRect(o.x, o.y, o.width, o.height); - + graphics.lineStyle(1, strokeStyle, strokeAlpha); graphics.strokeRect(o.x, o.y, o.width, o.height); } - + /** copies obj via JSON.parse(JSON.stringify(obj)); */ public static copy(obj: T): T { if (!obj) { @@ -70,68 +86,77 @@ export class Helper { } return JSON.parse(JSON.stringify(obj)); } - - public static typedKeys(obj: T): (keyof T)[] { + + public static typedKeys(obj: T): (keyof T)[] { return Object.keys(obj) as (keyof T)[]; } - + public static getJson(key: string, callback: (json: any) => void) { const scene = Globals.scene; - + // get json from cache if (scene.cache.json.has(key)) { return callback(scene.cache.json.get(key)); } - - Globals.httpService.resolveFile(key + '.json').subscribe(file => { - // load json - scene.load.json(key, Globals.URL + file); - scene.load.once('complete', () => { - return callback(scene.cache.json.get(key)); - }); - scene.load.start(); - }, () => { - console.warn(`Failed to resolve resource: ${key}.json`); - callback(undefined); - }); + + Globals.httpService.resolveFile(key + '.json').subscribe( + (file) => { + // load json + scene.load.json(key, Globals.URL + file); + scene.load.once('complete', () => { + return callback(scene.cache.json.get(key)); + }); + scene.load.start(); + }, + () => { + console.warn(`Failed to resolve resource: ${key}.json`); + callback(undefined); + }, + ); } - + public static getJsonPromise(key: string) { - return new Promise(resolve => { - this.getJson(key, json => resolve(json)); + return new Promise((resolve) => { + this.getJson(key, (json) => resolve(json)); }); } - + /** * returns true if texture exists, false otherwise */ - public static async loadTexture(name: string | undefined, scene: Scene): Promise { + public static async loadTexture( + name: string | undefined, + scene: Scene, + ): Promise { if (!name) { return false; } - + //Sometimes RFG adds a whitespace after (or even in front) of the name. //This is invalid behaviour for loading a file but the game trims it as well. const key = name.trim(); - + if (scene.textures.exists(key)) { return true; } - + // TODO: save promise to avoid loading the same texture simultaneously - const file = await Globals.httpService.resolveFile(key).toPromise().catch(() => false); + const file = await Globals.httpService + .resolveFile(key) + .toPromise() + .catch(() => false); if (!file) { return false; } - - return new Promise(res => { + + return new Promise((res) => { scene.load.image(key, Globals.URL + file); scene.load.once('complete', () => res(true)); scene.load.once('loaderror', () => res(false)); scene.load.start(); }); } - + /** * every key listener should check this method and only proceed when * false is returned, so the user can write everything into input fields @@ -145,18 +170,25 @@ export class Helper { return false; } const tag = document.activeElement.tagName.toLowerCase(); - + return tag === 'input' || tag === 'textarea'; } - - public static getMapStyle(map: CCMap, type: keyof MapStyles): MapStyle | undefined { - const mapStyles = Globals.jsonLoader.loadJsonMergedSync('map-styles.json'); + + public static getMapStyle( + map: CCMap, + type: keyof MapStyles, + ): MapStyle | undefined { + const mapStyles = + Globals.jsonLoader.loadJsonMergedSync('map-styles.json'); const mapStyleName = map.attributes.mapStyle || 'default'; const mapStyle = mapStyles[mapStyleName]; return mapStyle?.[type] ?? mapStyles.default[type]; } - - public static async asyncFilter(arr: T[], predicate: (v: T) => Promise) { + + public static async asyncFilter( + arr: T[], + predicate: (v: T) => Promise, + ) { const results = await Promise.all(arr.map(predicate)); return arr.filter((_v, index) => results[index]); } diff --git a/webapp/src/app/services/phaser/ingame-preview.ts b/webapp/src/app/services/phaser/ingame-preview.ts index 77ecbcf1..8885916e 100644 --- a/webapp/src/app/services/phaser/ingame-preview.ts +++ b/webapp/src/app/services/phaser/ingame-preview.ts @@ -2,35 +2,38 @@ import { PreUpdate } from './pre-update'; import { Globals } from '../globals'; import { Subscription } from 'rxjs'; -export class IngamePreview extends Phaser.GameObjects.Image implements PreUpdate { - +export class IngamePreview + extends Phaser.GameObjects.Image + implements PreUpdate +{ private sub: Subscription; - + constructor(scene: Phaser.Scene) { super(scene, 0, 0, 'ingame'); this.depth = 99999; - this.sub = Globals.globalEventsService.showIngamePreview.subscribe(v => this.visible = v); + this.sub = Globals.globalEventsService.showIngamePreview.subscribe( + (v) => (this.visible = v), + ); } - + override destroy(fromScene?: boolean) { super.destroy(fromScene); this.sub.unsubscribe(); } - + preUpdate(time: number, delta: number): void { const cam = this.scene.cameras.main; - + const midX = cam.scrollX + cam.width * 0.5; const midY = cam.scrollY + cam.height * 0.5; - + const displayWidth = cam.width / cam.zoomX; const displayHeight = cam.height / cam.zoomY; - + const centerX = midX - displayWidth * 0.1; const centerY = midY - displayHeight * 0.06; - + this.x = centerX; this.y = centerY; } - } diff --git a/webapp/src/app/services/phaser/layer-parallax.ts b/webapp/src/app/services/phaser/layer-parallax.ts index 1d432a2d..f8b9b4fc 100644 --- a/webapp/src/app/services/phaser/layer-parallax.ts +++ b/webapp/src/app/services/phaser/layer-parallax.ts @@ -4,20 +4,23 @@ import { CCMap } from './tilemap/cc-map'; import { IngamePreview } from './ingame-preview'; export class LayerParallax extends BaseObject { - private map?: CCMap; - + constructor( scene: Phaser.Scene, - private preview: IngamePreview + private preview: IngamePreview, ) { super(scene, LayerParallax.name, true); } - + protected activate(): void { - this.addSubscription(Globals.mapLoaderService.tileMap.subscribe(map => this.map = map)); + this.addSubscription( + Globals.mapLoaderService.tileMap.subscribe( + (map) => (this.map = map), + ), + ); } - + protected deactivate(): void { if (!this.map) { return; @@ -26,38 +29,36 @@ export class LayerParallax extends BaseObject { layer.setOffset(0, 0); } } - - protected init(): void { - } - + + protected init(): void {} + preUpdate(time: number, delta: number) { if (!this.map) { return; } - + const layers = this.map.layers; - + const centerX = this.preview.x; const centerY = this.preview.y; - + // half of game resolution const offX = centerX - 284; const offY = centerY - 160; - + for (const layer of layers) { if (!layer.visible) { continue; } let x = offX / (layer.details?.distance ?? 1); let y = offY / (layer.details?.distance ?? 1); - + if (layer.details.distance < 1) { x += 16; y += 16; } - + layer.setOffset(offX - x, offY - y); } } - } diff --git a/webapp/src/app/services/phaser/main-scene.ts b/webapp/src/app/services/phaser/main-scene.ts index a703ac2b..a732d97d 100644 --- a/webapp/src/app/services/phaser/main-scene.ts +++ b/webapp/src/app/services/phaser/main-scene.ts @@ -4,34 +4,34 @@ import { EditorView } from '../../models/editor-view'; import { Globals } from '../globals'; import { CoordsReporter } from './coords-reporter'; import { EntityManager } from './entities/entity-manager'; +import { EntityGrid } from './entity-grid'; +import { IngamePreview } from './ingame-preview'; +import { LayerParallax } from './layer-parallax'; import { MapPan } from './map-pan'; import { CCMap } from './tilemap/cc-map'; import { TileDrawer } from './tilemap/tile-drawer'; -import { LayerParallax } from './layer-parallax'; -import { IngamePreview } from './ingame-preview'; -import { EntityGrid } from './entity-grid'; export class MainScene extends Phaser.Scene { - private sub?: Subscription; - + constructor() { - super({key: 'main'}); + super({ key: 'main' }); } - + preload() { - this.load.image('pixel', 'assets/pixel.png'); this.load.image('ingame', 'assets/ingame.png'); - + this.load.crossOrigin = 'anonymous'; - + // this.load.on('progress', (val: number) => console.log(val)); - + // this.load.maxParallelDownloads = this.res.images.length; - this.load.once('complete', () => Globals.globalEventsService.loadComplete.next()); + this.load.once('complete', () => + Globals.globalEventsService.loadComplete.next(), + ); } - + create() { const game = this.game; this.cameras.main.setBackgroundColor('#616161'); @@ -39,48 +39,51 @@ export class MainScene extends Phaser.Scene { game.canvas.oncontextmenu = function (e) { e.preventDefault(); }; - + game.scale.scaleMode = Phaser.Scale.ScaleModes.NONE; - + const entityManager = new EntityManager(this, false); - + const tileMap = new CCMap(game, this, entityManager); Globals.map = tileMap; - + this.sub = Globals.mapLoaderService.map.subscribe((map) => { if (map) { - tileMap.loadMap(map); - + const _ = tileMap.loadMap(map); + // reset camera position on map load const cam = this.cameras.main; cam.zoom = 1; - + // offset tile selector const s = Globals.TILE_SIZE; const offset = 400 * window.devicePixelRatio; - cam.centerOn(map.mapWidth * s / 2 + offset, map.mapHeight * s / 2); + cam.centerOn( + (map.mapWidth * s) / 2 + offset, + (map.mapHeight * s) / 2, + ); } }); - + const pan = new MapPan(this, 'mapPan'); this.add.existing(pan); - + const tileDrawer = new TileDrawer(this); this.add.existing(tileDrawer); - + this.add.existing(entityManager); - + const preview = new IngamePreview(this); this.add.existing(preview); this.add.existing(new LayerParallax(this, preview)); - + const coordsReporter = new CoordsReporter(this); this.add.existing(coordsReporter); - + const grid = new EntityGrid(this); this.add.existing(grid); - - Globals.globalEventsService.currentView.subscribe(view => { + + Globals.globalEventsService.currentView.subscribe((view) => { tileDrawer.setActive(false); entityManager.setActive(false); switch (view) { @@ -92,10 +95,10 @@ export class MainScene extends Phaser.Scene { break; } }); - + Globals.globalEventsService.currentView.next(EditorView.Layers); } - + destroy() { console.log('destroy scene'); if (this.sub) { diff --git a/webapp/src/app/services/phaser/map-pan.ts b/webapp/src/app/services/phaser/map-pan.ts index f7f3c909..8274e742 100644 --- a/webapp/src/app/services/phaser/map-pan.ts +++ b/webapp/src/app/services/phaser/map-pan.ts @@ -1,41 +1,53 @@ import { Point } from '../../models/cross-code-map'; import { Globals } from '../globals'; -import { Vec2 } from './vec2'; import { PreUpdate } from './pre-update'; +import { Vec2 } from './vec2'; -export class MapPan extends Phaser.GameObjects.GameObject implements PreUpdate{ +export class MapPan extends Phaser.GameObjects.GameObject implements PreUpdate { private isScrolling = false; - private startMouse: Point = {x: 0, y: 0}; - private startCam: Point = {x: 0, y: 0}; - + private startMouse: Point = { x: 0, y: 0 }; + private startCam: Point = { x: 0, y: 0 }; + private zoomKey: Phaser.Input.Keyboard.Key; private panKey: Phaser.Input.Keyboard.Key; - + constructor(scene: Phaser.Scene, type: string) { super(scene, type); - - this.zoomKey = scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.ALT, false); - this.panKey = scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.CTRL, false); + + this.zoomKey = scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.ALT, + false, + ); + this.panKey = scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.CTRL, + false, + ); // game.input.mouseWheel.callback = (v) => this.onMouseWheel(v); - scene.input.on('wheel', ( - pointer: Phaser.Input.Pointer, - gameObjects: any, - deltaX: number, - deltaY: number, - //deltaZ: number - ) => this.onMouseWheel(deltaY)); + scene.input.on( + 'wheel', + ( + pointer: Phaser.Input.Pointer, + gameObjects: any, + deltaX: number, + deltaY: number, + //deltaZ: number + ) => this.onMouseWheel(deltaY), + ); scene.input.on('pointerdown', () => this.onMouseDown()); scene.input.on('pointerup', () => this.onMouseUp()); scene.input.on('pointerupoutside', () => this.onMouseUp()); } - + private onMouseDown() { if (!this.active) { return; } // set global panning state when the panKey is down - if (this.panKey.isDown || this.scene.input.activePointer.middleButtonDown()) { + if ( + this.panKey.isDown || + this.scene.input.activePointer.middleButtonDown() + ) { Globals.panning = true; } else { Globals.panning = false; @@ -45,51 +57,54 @@ export class MapPan extends Phaser.GameObjects.GameObject implements PreUpdate{ this.isScrolling = true; const cam = this.scene.cameras.main; Vec2.assign(this.startMouse, this.scene.input.activePointer); - + this.startCam.x = cam.scrollX; this.startCam.y = cam.scrollY; } - + private onMouseUp() { this.isScrolling = false; } - + private onMouseWheel(delta: number) { if (!this.zoomKey.isDown) { return; } - + const cam = this.scene.cameras.main; - + let zoom = delta > 0 ? 0.8 : 1.25; zoom *= cam.zoom; if (zoom > 0.4 && zoom < 50) { - const pointer = this.scene.input.activePointer; - + const mouse = Vec2.createC(pointer.worldX, pointer.worldY); const oldX = mouse.x; const oldY = mouse.y; cam.zoom = zoom; - - // @ts-ignore + + // @ts-expect-error undocumented parameter cam.preRender(this.scene.scale.resolution); - cam.getWorldPoint(pointer.x, pointer.y, mouse); + cam.getWorldPoint(pointer.x, pointer.y, mouse as any); cam.scrollX += oldX - mouse.x; cam.scrollY += oldY - mouse.y; } } - - preUpdate() { + preUpdate() { const pointer = this.scene.input.activePointer; - + Globals.panning = this.panKey.isDown || pointer.middleButtonDown(); - if (Globals.panning && this.isScrolling && this.active && pointer.isDown) { + if ( + Globals.panning && + this.isScrolling && + this.active && + pointer.isDown + ) { const dx = pointer.x - this.startMouse.x; const dy = pointer.y - this.startMouse.y; - + const cam = this.scene.cameras.main; cam.scrollX = this.startCam.x - dx / cam.zoom; cam.scrollY = this.startCam.y - dy / cam.zoom; diff --git a/webapp/src/app/services/phaser/phaser-events.service.ts b/webapp/src/app/services/phaser/phaser-events.service.ts index 991249c4..e1a26715 100644 --- a/webapp/src/app/services/phaser/phaser-events.service.ts +++ b/webapp/src/app/services/phaser/phaser-events.service.ts @@ -3,9 +3,8 @@ import { Subject } from 'rxjs'; import { SelectedTile } from '../../models/tile-selector'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PhaserEventsService { - changeSelectedTiles = new Subject(); } diff --git a/webapp/src/app/services/phaser/sheet-parser.ts b/webapp/src/app/services/phaser/sheet-parser.ts index 17e9bf63..0cb25508 100644 --- a/webapp/src/app/services/phaser/sheet-parser.ts +++ b/webapp/src/app/services/phaser/sheet-parser.ts @@ -1,17 +1,13 @@ -import { Helper } from './helper'; import { Point, Point3 } from '../../models/cross-code-map'; import { Fix } from './entities/cc-entity'; -import { ScalablePropDef } from './entities/registry/scalable-prop'; import { CharacterSettings } from './entities/registry/npc'; +import { ScalablePropDef } from './entities/registry/scalable-prop'; +import { Helper } from './helper'; export interface ScalablePropSheet { DOCTYPE: string; - entries: { - [key: string]: ScalablePropDef; - }; - jsonTEMPLATES?: { - [key: string]: ScalablePropDef; - }; + entries: Record; + jsonTEMPLATES?: Record; } export interface PropSheet { @@ -20,9 +16,7 @@ export interface PropSheet { jsonTEMPLATES?: JsonTemplates; } -export interface JsonTemplates { - [key: string]: Anims | Anims[keyof Anims]; -} +export type JsonTemplates = Record; export interface PropDef { name?: string; @@ -91,10 +85,10 @@ export interface Anims extends IfThen { gfxOffset?: Partial; aboveZ?: number; offX?: number; - namedSheets?: { [key: string]: AnimSheet }; + namedSheets?: Record; framesSpriteOffset?: number[]; globalTiming?: boolean; - + // used in NPC DOCTYPE?: string; dirs?: number | string; @@ -122,7 +116,7 @@ export interface SubJsonInstance { export interface SubJsonParam { jsonPARAM: string; - + // can't find any usages of it, but code references it default?: any; } @@ -136,7 +130,6 @@ export interface Effect { name: string; } - export function isJsonInstance(obj: any): obj is SubJsonInstance { const key: keyof SubJsonInstance = 'jsonINSTANCE'; return obj && obj[key]; @@ -147,25 +140,38 @@ export function isJsonParam(obj: any): obj is SubJsonParam { return obj && obj[key]; } -export function prepareSheet(sheetDef: T): T { +export function prepareSheet< + T extends PropSheet | ScalablePropSheet | CharacterSettings, +>(sheetDef: T): T { const sheet = Helper.copy(sheetDef); return recSearchTemplateInstance(sheet, sheet.jsonTEMPLATES, {}); } export function prepareProp(propDef: PropDef, sheetDef: PropSheet): Anims { - return recSearchTemplateInstance(Helper.copy(propDef.anims), sheetDef.jsonTEMPLATES, {}); + return recSearchTemplateInstance( + Helper.copy(propDef.anims), + sheetDef.jsonTEMPLATES, + {}, + ); } -export function prepareScalableProp(propDef: ScalablePropDef, sheetDef: ScalablePropSheet): ScalablePropDef { - return recSearchTemplateInstance(Helper.copy(propDef), sheetDef.jsonTEMPLATES, {}); +export function prepareScalableProp( + propDef: ScalablePropDef, + sheetDef: ScalablePropSheet, +): ScalablePropDef { + return recSearchTemplateInstance( + Helper.copy(propDef), + sheetDef.jsonTEMPLATES, + {}, + ); } function recSearchTemplateInstance( json: Anims | Anims[keyof Anims], templates?: JsonTemplates, - tmpTemplates?: JsonTemplates + tmpTemplates?: JsonTemplates, ) { - if (!json || typeof (json) != 'object') { + if (!json || typeof json != 'object') { return json; } if (isJsonInstance(json)) { @@ -173,64 +179,80 @@ function recSearchTemplateInstance( } if (json instanceof Array) { for (let i = 0; i < json.length; i++) { - json[i] = recSearchTemplateInstance(json[i], templates, tmpTemplates); + json[i] = recSearchTemplateInstance( + json[i], + templates, + tmpTemplates, + ); } return json; } for (const key of Object.keys(json)) { - // @ts-ignore - json[key] = recSearchTemplateInstance(json[key], templates, tmpTemplates); - + //@ts-expect-error no index signature + json[key] = recSearchTemplateInstance( + //@ts-expect-error no index signature + json[key], + templates, + tmpTemplates, + ); } - + return json; } function resolveTemplateInstance( instanceData: SubJsonInstance, templates: JsonTemplates, - tmpTemplates: JsonTemplates + tmpTemplates: JsonTemplates, ) { const templateName = instanceData.jsonINSTANCE; const template = tmpTemplates[templateName] || templates[templateName]; if (!template) { - console.error('Could not find template \'' + templateName + '\''); + console.error("Could not find template '" + templateName + "'"); return; } - return recResolveTemplateInstance(template, instanceData, templates, tmpTemplates); + return recResolveTemplateInstance( + template, + instanceData, + templates, + tmpTemplates, + ); } function recResolveTemplateInstance( template: JsonTemplates[''], instanceData: any, templates: JsonTemplates, - tmpTemplates: JsonTemplates + tmpTemplates: JsonTemplates, ): any { - if (!template || typeof (template) != 'object') { + if (!template || typeof template != 'object') { return template; } - + if (isJsonInstance(template)) { const templateCopy = Helper.copy(template); return resolveTemplateInstance(templateCopy, templates, tmpTemplates); } - + if (isJsonParam(template)) { let value = instanceData[template['jsonPARAM']]; - + if (value === undefined || value === null) { if (template['default'] !== undefined) { value = template['default']; } else { - throw new Error('Could not find template parameters \'' + template['jsonPARAM'] + '\' '); + throw new Error( + "Could not find template parameters '" + + template['jsonPARAM'] + + "' ", + ); } } return recSearchTemplateInstance(value); } else { if (Array.isArray(template)) { const result = []; - for (let i = 0; i < template.length; ++i) { - let entry = template[i] as any; + for (let entry of template as any[]) { if (typeof entry !== 'number' && entry['jsonIF']) { if (instanceData[entry['jsonIF']] === undefined) { continue; @@ -242,7 +264,12 @@ function recResolveTemplateInstance( delete entry['jsonIF']; } } - const sub = recResolveTemplateInstance(entry, instanceData, templates, tmpTemplates); + const sub = recResolveTemplateInstance( + entry, + instanceData, + templates, + tmpTemplates, + ); result.push(sub); } return result; @@ -261,18 +288,22 @@ function recResolveTemplateInstance( delete entry['jsonIF']; } } - result[name] = recResolveTemplateInstance(entry, instanceData, templates, tmpTemplates); + result[name] = recResolveTemplateInstance( + entry, + instanceData, + templates, + tmpTemplates, + ); } return result; } } - export function flattenSUBs(obj: Anims, parent: Anims): Anims[] { const out: Anims[] = []; const merged = { ...parent, - ...obj + ...obj, }; if (Array.isArray(obj.SUB)) { for (const sub of obj.SUB) { @@ -281,7 +312,7 @@ export function flattenSUBs(obj: Anims, parent: Anims): Anims[] { } else { out.push({ ...parent, - ...obj + ...obj, }); } return out; diff --git a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts index 1b885e6b..58c53c9f 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts @@ -7,20 +7,16 @@ import { Globals } from '../../globals'; import Tile = Phaser.Tilemaps.Tile; export class CCMapLayer { - public details!: MapLayer; - + private layer!: Phaser.Tilemaps.TilemapLayer; private border!: Phaser.GameObjects.Rectangle; private container!: Phaser.GameObjects.Container; - + private index = 0; - - constructor( - private tilemap: Phaser.Tilemaps.Tilemap - ) { - } - + + constructor(private tilemap: Phaser.Tilemaps.Tilemap) {} + public async init(details: MapLayer) { // noinspection SuspiciousTypeOfGuard if (typeof details.distance === 'string') { @@ -36,60 +32,60 @@ export class CCMapLayer { if (details.data && details.data.length > 0) { customPutTilesAt(details.data, this.layer); } - await this.updateTileset(details.tilesetName!); - + await this.updateTileset(details.tilesetName); + const skip = 'Navigation Collision HeightMap'.split(' '); // const skip = 'Navigation Background HeightMap'.split(' '); - skip.forEach(type => { + skip.forEach((type) => { if (type === details.type) { this.visible = false; } }); } - + get visible(): boolean { return this.layer.visible; } - + set visible(val: boolean) { this.container.visible = val; this.container.active = val; this.layer.visible = val; this.layer.active = val; } - + get alpha(): number { return this.layer.alpha; } - + set alpha(val: number) { this.layer.alpha = val; } - + get x(): number { return this.container.x; } - + get y(): number { return this.container.y; } - + destroy() { this.container.destroy(true); this.layer?.destroy(true); } - + select(val: boolean) { if (val) { this.visible = true; } this.border.visible = val; } - + offsetLayer(offset: Point, borderTiles = false) { const data = this.layer.layer.data; const newData: number[][] = []; - + for (let y = 0; y < data.length; y++) { newData[y] = []; for (let x = 0; x < data[y].length; x++) { @@ -109,12 +105,12 @@ export class CCMapLayer { } customPutTilesAt(newData, this.layer); } - + resize(width: number, height: number) { const data = this.layer.layer.data; this.details.width = width; this.details.height = height; - + const newData: number[][] = []; for (let y = 0; y < height; y++) { newData[y] = []; @@ -126,35 +122,44 @@ export class CCMapLayer { const visible = this.layer.visible; this.makeLayer(undefined, newData); this.updateBorder(); - + this.visible = visible; } - + async updateTileset(tilesetname: string) { const details = this.details; details.tilesetName = tilesetname; - + await Helper.loadTexture(tilesetname, this.tilemap.scene); - - const newTileset = this.tilemap.addTilesetImage(tilesetname, undefined, undefined, undefined, undefined, undefined, 1); + + const newTileset = this.tilemap.addTilesetImage( + tilesetname, + undefined, + undefined, + undefined, + undefined, + undefined, + 1, + ); this.makeLayer(newTileset ?? []); - + this.updateLevel(); this.updateLighter(!!this.details.lighter); } - + updateLevel(level?: number | string) { - if (level === undefined) { - level = (this.details.levelName ?? this.details.level) as string | number; + level = (this.details.levelName ?? this.details.level) as + | string + | number; } - + //converts stringed numbers like "2" to be numeric. //leaves everything else unchanged. if (!isNaN(+level)) { level = +level; } - + if (typeof level === 'string') { this.details.levelName = level; switch (level) { @@ -174,39 +179,49 @@ export class CCMapLayer { } this.layer.depth = this.details.level * 10 + this.index * 0.0001; } - + updateIndex(index: number) { this.index = index; this.updateLevel(); } - + setOffset(x: number, y: number) { this.container.x = x; this.container.y = y; this.layer.x = x; this.layer.y = y; } - + updateLighter(lighter: boolean) { this.details.lighter = lighter; const blendMode = lighter ? BlendModes.ADD : BlendModes.NORMAL; this.layer.setBlendMode(blendMode); } - + getPhaserLayer(): Phaser.Tilemaps.TilemapLayer { return this.layer; } - - private makeLayer(tileset?: string | string[] | Phaser.Tilemaps.Tileset, tiles?: Tile[][] | number[][]) { + + private makeLayer( + tileset?: string | string[] | Phaser.Tilemaps.Tileset, + tiles?: Tile[][] | number[][], + ) { const oldLayer = this.layer as typeof this.layer | undefined; - + if (!tileset) { tileset = oldLayer?.tileset[0]?.name ?? []; } if (!tiles) { tiles = oldLayer?.layer?.data; } - this.layer = this.tilemap.createBlankLayer(this.details.name + Math.random(), tileset, 0, 0, this.details.width, this.details.height)!; + this.layer = this.tilemap.createBlankLayer( + this.details.name + Math.random(), + tileset, + 0, + 0, + this.details.width, + this.details.height, + )!; if (tiles) { customPutTilesAt(tiles, this.layer); } @@ -217,12 +232,12 @@ export class CCMapLayer { oldLayer.destroy(true); } } - + private updateBorder() { const s = Globals.TILE_SIZE; - + const borderSize = 2; - + this.border.setPosition(-borderSize * 0.5, -borderSize * 0.5); this.border.setSize( this.details.width * s + borderSize, @@ -231,7 +246,7 @@ export class CCMapLayer { this.border.setStrokeStyle(borderSize, 0xfc4445, 1); this.border.setOrigin(0, 0); } - + exportLayer(): MapLayer { const out: MapLayer = Object.assign({}, this.details); if (out.levelName) { @@ -242,7 +257,7 @@ export class CCMapLayer { this.extractLayerData(out); return out; } - + private extractLayerData(layer: MapLayer): void { for (const tile of this.layer.getTilesWithin()) { if (!layer.data[tile.y]) { @@ -251,11 +266,10 @@ export class CCMapLayer { layer.data[tile.y][tile.x] = tile.index; } } - + setPhaserLayer(layer: Phaser.Tilemaps.TilemapLayer) { - const oldLayer = this.layer as typeof this.layer | undefined; - + this.layer = layer; this.layer.alpha = oldLayer?.alpha ?? 1; this.details.width = this.layer.width / Globals.TILE_SIZE; diff --git a/webapp/src/app/services/phaser/tilemap/cc-map.ts b/webapp/src/app/services/phaser/tilemap/cc-map.ts index 5d8dc343..d6c5d8dc 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map.ts @@ -1,5 +1,10 @@ import { Subscription } from 'rxjs'; -import { Attributes, CrossCodeMap, MapLayer, Point } from '../../../models/cross-code-map'; +import { + Attributes, + CrossCodeMap, + MapLayer, + Point, +} from '../../../models/cross-code-map'; import { Globals } from '../../globals'; import { EntityManager } from '../entities/entity-manager'; import { CCMapLayer } from './cc-map-layer'; @@ -20,78 +25,92 @@ export class CCMap { mapHeight = 0; masterLevel = 0; layers: CCMapLayer[] = []; - attributes: Attributes = {}; - screen: Point = {x: 0, y: 0}; - + attributes: Attributes = {} as any; + screen: Point = { x: 0, y: 0 }; + private lastMapId = 1; private tileMap?: Phaser.Tilemaps.Tilemap; - + private subs: Subscription[] = []; - + filename = ''; path?: string; - + private inputLayers?: MapLayer[]; - + constructor( private game: Phaser.Game, private scene: Phaser.Scene, - public entityManager: EntityManager + public entityManager: EntityManager, ) { const stateHistory = Globals.stateHistoryService; - this.subs.push(stateHistory.selectedState.subscribe(async container => { - if (!container || !container.state) { - return; - } - - const makeLayerKey = (layer: CCMapLayer) => { - return `${layer.details.name}\n${layer.details.levelName}\n${layer.details.level}`; - }; - - const selectedLayer = Globals.mapLoaderService.selectedLayer; - const oldLayers = this.layers.map(v => ({ - key: makeLayerKey(v), - visible: v.visible, - selected: v === selectedLayer.getValue() - })); - - await this.loadMap(JSON.parse(container.state.json), true); - - for (const layer of this.layers) { - const key = makeLayerKey(layer); - const oldLayer = oldLayers.find(v => v.key === key); - if (!oldLayer) { - continue; + this.subs.push( + stateHistory.selectedState.subscribe(async (container) => { + if (!container || !container.state) { + return; } - layer.visible = oldLayer.visible; - if (oldLayer.selected) { - selectedLayer.next(layer); + + const makeLayerKey = (layer: CCMapLayer) => { + return `${layer.details.name}\n${layer.details.levelName}\n${layer.details.level}`; + }; + + const selectedLayer = Globals.mapLoaderService.selectedLayer; + const oldLayers = this.layers.map((v) => ({ + key: makeLayerKey(v), + visible: v.visible, + selected: v === selectedLayer.getValue(), + })); + + await this.loadMap(JSON.parse(container.state.json), true); + + for (const layer of this.layers) { + const key = makeLayerKey(layer); + const oldLayer = oldLayers.find((v) => v.key === key); + if (!oldLayer) { + continue; + } + layer.visible = oldLayer.visible; + if (oldLayer.selected) { + selectedLayer.next(layer); + } } - } - })); - - this.subs.push(Globals.globalEventsService.offsetMap.subscribe(offset => this.offsetMap(offset))); - this.subs.push(Globals.globalEventsService.offsetEntities.subscribe(offset => this.offsetEntities(offset))); - this.subs.push(Globals.globalEventsService.resizeMap.subscribe(size => this.resize(size.x, size.y))); + }), + ); + + this.subs.push( + Globals.globalEventsService.offsetMap.subscribe((offset) => + this.offsetMap(offset), + ), + ); + this.subs.push( + Globals.globalEventsService.offsetEntities.subscribe((offset) => + this.offsetEntities(offset), + ), + ); + this.subs.push( + Globals.globalEventsService.resizeMap.subscribe((size) => + this.resize(size.x, size.y), + ), + ); } - + destroy() { for (const sub of this.subs) { sub.unsubscribe(); } this.subs = []; } - + async loadMap(map: CrossCodeMap, skipInit = false) { const tileMap = this.scene.make.tilemap({ width: map.mapWidth, height: map.mapHeight, tileHeight: Globals.TILE_SIZE, - tileWidth: Globals.TILE_SIZE + tileWidth: Globals.TILE_SIZE, }); - + this.tileMap = tileMap; - + this.name = map.name; this.levels = map.levels; this.mapWidth = map.mapWidth; @@ -101,14 +120,14 @@ export class CCMap { this.screen = map.screen; this.filename = map.filename || 'untitled'; this.path = map.path; - + this.inputLayers = map.layer; - + // cleanup everything before loading new map - this.layers.forEach(layer => layer.destroy()); - + this.layers.forEach((layer) => layer.destroy()); + this.layers = []; - + // generate Map Layers if (this.inputLayers) { for (const layer of this.inputLayers) { @@ -116,12 +135,12 @@ export class CCMap { await ccLayer.init(layer); this.layers.push(ccLayer); } - + this.updateLayerIndices(); - + this.inputLayers = undefined; } - + this.lastMapId = 1; for (const entity of map.entities) { const mapId = entity.settings.mapId ?? 0; @@ -129,27 +148,27 @@ export class CCMap { this.lastMapId = mapId; } } - + // generate entities await this.entityManager.initialize(map, this); - + if (!skipInit) { Globals.stateHistoryService.init({ name: 'load', icon: 'insert_drive_file', - json: JSON.stringify(this.exportMap()) + json: JSON.stringify(this.exportMap()), }); } - + Globals.mapLoaderService.tileMap.next(this); Globals.mapLoaderService.selectedLayer.next(this.layers[0]); } - + private resize(width: number, height: number) { this.mapWidth = width; this.mapHeight = height; - - this.layers.forEach(layer => { + + this.layers.forEach((layer) => { // only update layers with distance: 1 // Parallax Layers shouldn't be updated because they usually have different dimensions than the map if (layer.details.distance === 1) { @@ -157,52 +176,52 @@ export class CCMap { } }); } - + offsetMap(offset: Point, borderTiles = false) { - this.layers.forEach(layer => layer.offsetLayer(offset, borderTiles)); + this.layers.forEach((layer) => layer.offsetLayer(offset, borderTiles)); } - + offsetEntities(offset: Point) { for (const entity of this.entityManager.entities) { entity.addPosition(offset.x, offset.y); } } - + addLayer(layer: CCMapLayer) { this.layers.push(layer); this.updateLayerIndices(); } - + removeLayer(layer: CCMapLayer) { const index = this.layers.indexOf(layer); this.layers.splice(index, 1); layer.destroy(); } - + updateLayerIndices() { for (let i = 0; i < this.layers.length; i++) { this.layers[i].updateIndex(i); } } - + public getTilemap() { return this.tileMap; } - + public getUniqueMapid() { return ++this.lastMapId; } - + exportMap(): CrossCodeMap { - const out: CrossCodeMap = {}; - + const out: CrossCodeMap = {} as any; + for (const level of this.levels) { const number = Number(level.height); if (!Number.isNaN(number)) { level.height = number; } } - + out.name = this.name; out.levels = this.levels; out.mapWidth = this.mapWidth; @@ -210,14 +229,14 @@ export class CCMap { out.masterLevel = this.masterLevel; out.attributes = this.attributes; out.screen = this.screen; - + out.path = this.path; out.filename = this.filename; - + out.entities = this.entityManager.exportEntities(); out.layer = []; - this.layers.forEach(l => out.layer.push(l.exportLayer())); - + this.layers.forEach((l) => out.layer.push(l.exportLayer())); + return out; } } diff --git a/webapp/src/app/services/phaser/tilemap/fill.ts b/webapp/src/app/services/phaser/tilemap/fill.ts index 32eab53d..3f5ba49a 100644 --- a/webapp/src/app/services/phaser/tilemap/fill.ts +++ b/webapp/src/app/services/phaser/tilemap/fill.ts @@ -12,7 +12,7 @@ export class Filler { if (newTile === prev) { return; } - + let toCheck: Point[] = [p]; while (toCheck.length > 0) { const currP = toCheck.pop()!; @@ -23,23 +23,23 @@ export class Filler { } } } - + private static getNeighbours(p: Point, layer: CCMapLayer): Point[] { const out: Point[] = []; - + if (p.x > 0) { - out.push({x: p.x - 1, y: p.y}); + out.push({ x: p.x - 1, y: p.y }); } if (p.x < layer.details.width - 1) { - out.push({x: p.x + 1, y: p.y}); + out.push({ x: p.x + 1, y: p.y }); } if (p.y > 0) { - out.push({x: p.x, y: p.y - 1}); + out.push({ x: p.x, y: p.y - 1 }); } if (p.y < layer.details.height - 1) { - out.push({x: p.x, y: p.y + 1}); + out.push({ x: p.x, y: p.y + 1 }); } - + return out; } } diff --git a/webapp/src/app/services/phaser/tilemap/layer-helper.ts b/webapp/src/app/services/phaser/tilemap/layer-helper.ts index 51b6cfb3..110563cf 100644 --- a/webapp/src/app/services/phaser/tilemap/layer-helper.ts +++ b/webapp/src/app/services/phaser/tilemap/layer-helper.ts @@ -3,11 +3,13 @@ import IsInLayerBounds = Phaser.Tilemaps.Components.IsInLayerBounds; import Tile = Phaser.Tilemaps.Tile; -export function customPutTilesAt(tiles: (number | null | undefined)[][] | Tile[][], layer: Phaser.Tilemaps.TilemapLayer) { - +export function customPutTilesAt( + tiles: (number | null | undefined)[][] | Tile[][], + layer: Phaser.Tilemaps.TilemapLayer, +) { const height = tiles.length; const width = tiles[0].length; - + for (let ty = 0; ty < height; ty++) { for (let tx = 0; tx < width; tx++) { const tile = tiles[ty][tx] ?? 0; @@ -17,12 +19,16 @@ export function customPutTilesAt(tiles: (number | null | undefined)[][] | Tile[] } } -export function customPutTileAt(tile: number, tileX: number, tileY: number, layer: Phaser.Tilemaps.LayerData) { - +export function customPutTileAt( + tile: number, + tileX: number, + tileY: number, + layer: Phaser.Tilemaps.LayerData, +) { if (!IsInLayerBounds(tileX, tileY, layer)) { return null; } - + if (layer.data[tileY][tileX] === null) { layer.data[tileY][tileX] = new Tile( layer, @@ -32,16 +38,16 @@ export function customPutTileAt(tile: number, tileX: number, tileY: number, laye layer.tileWidth, layer.tileHeight, layer.baseTileWidth, - layer.baseTileHeight + layer.baseTileHeight, ); } else { layer.data[tileY][tileX].index = tile; } - + const newTile = layer.data[tileY][tileX]; - + newTile.width = layer.tileWidth; newTile.height = layer.tileHeight; - + return newTile; } diff --git a/webapp/src/app/services/phaser/tilemap/points-in-line.ts b/webapp/src/app/services/phaser/tilemap/points-in-line.ts index 1d7a6f6d..721bb330 100644 --- a/webapp/src/app/services/phaser/tilemap/points-in-line.ts +++ b/webapp/src/app/services/phaser/tilemap/points-in-line.ts @@ -6,16 +6,16 @@ export function pointsInLine(start: Point, end: Point): Point[] { let y0 = start.y; const x1 = end.x; const y1 = end.y; - + const out = []; - + const dx = Math.abs(x1 - x0); const sx = x0 < x1 ? 1 : -1; const dy = -Math.abs(y1 - y0); const sy = y0 < y1 ? 1 : -1; let err = dx + dy; while (true) { - out.push({x: x0, y: y0}); + out.push({ x: x0, y: y0 }); if (x0 === x1 && y0 === y1) { break; } @@ -29,6 +29,6 @@ export function pointsInLine(start: Point, end: Point): Point[] { y0 += sy; } } - + return out; } diff --git a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts index f53e5b60..71c7a8c8 100644 --- a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts +++ b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts @@ -12,57 +12,66 @@ import { customPutTileAt } from './layer-helper'; import { BaseTileDrawer } from '../BaseTileDrawer'; export class TileDrawer extends BaseObject { - private selectedTiles: SelectedTile[] = []; private layer?: CCMapLayer; - + private baseDrawer!: BaseTileDrawer; - + private renderLayersTransparent = false; - + private container!: Phaser.GameObjects.Container; - + private transparentKey!: Phaser.Input.Keyboard.Key; private visibilityKey!: Phaser.Input.Keyboard.Key; private shiftKey!: Phaser.Input.Keyboard.Key; private fillKey!: Phaser.Input.Keyboard.Key; - private lastDraw: Point = {x: -1, y: -1}; - + private lastDraw: Point = { x: -1, y: -1 }; + private dirty = false; - + constructor(scene: Phaser.Scene) { super(scene, 'tileDrawer'); } - - + protected override init() { - - this.fillKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.F, false); - this.transparentKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.R, false); - this.visibilityKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.V, false); - this.shiftKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false); - + this.fillKey = this.scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.F, + false, + ); + this.transparentKey = this.scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.R, + false, + ); + this.visibilityKey = this.scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.V, + false, + ); + this.shiftKey = this.scene.input.keyboard!.addKey( + Phaser.Input.Keyboard.KeyCodes.SHIFT, + false, + ); + this.container = this.scene.add.container(0, 0); this.container.depth = 10000; - + this.baseDrawer = new BaseTileDrawer(this.scene, false, this.container); this.baseDrawer.resetSelectedTiles(); this.scene.add.existing(this.baseDrawer); } - + private async selectLayer(selectedLayer?: CCMapLayer) { this.layer = selectedLayer; await this.baseDrawer.setLayer(selectedLayer); - + this.setLayerAlpha(); - + if (!selectedLayer || !selectedLayer.details.tilesetName) { this.container.visible = false; return; } this.container.visible = true; } - + preUpdate(): void { // hide cursor when no map loaded if (!this.layer) { @@ -70,116 +79,157 @@ export class TileDrawer extends BaseObject { return; } const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); - + const p = Helper.worldToTile( + pointer.worldX - this.layer.x, + pointer.worldY - this.layer.y, + ); + // draw tiles // trigger only when mouse is over canvas element (the renderer), avoids triggering when interacting with ui - if (pointer.leftButtonDown() && pointer.downElement?.nodeName === 'CANVAS' && this.layer) { - const finalPos = {x: 0, y: 0}; - const startPos = {x: 0, y: 0}; - + if ( + pointer.leftButtonDown() && + pointer.downElement?.nodeName === 'CANVAS' && + this.layer + ) { + const finalPos = { x: 0, y: 0 }; + const startPos = { x: 0, y: 0 }; + // skip drawing if last frame was the same if (this.lastDraw.x === p.x && this.lastDraw.y === p.y) { return; } - + // skip drawing if panning key is down if (Globals.panning) { return; } - + this.dirty = true; for (const tile of this.selectedTiles) { - finalPos.x = p.x + tile.offset.x; finalPos.y = p.y + tile.offset.y; - + startPos.x = this.lastDraw.x + tile.offset.x; startPos.y = this.lastDraw.y + tile.offset.y; - - const points = this.lastDraw.x < 0 ? [finalPos] : pointsInLine(startPos, finalPos); + + const points = + this.lastDraw.x < 0 + ? [finalPos] + : pointsInLine(startPos, finalPos); for (const point of points) { if (Helper.isInBounds(this.layer, point)) { const phaserLayer = this.layer.getPhaserLayer(); if (!phaserLayer) { return; } - - customPutTileAt(tile.id, point.x, point.y, phaserLayer.layer); - + + customPutTileAt( + tile.id, + point.x, + point.y, + phaserLayer.layer, + ); + if (!this.shiftKey.isDown) { - Globals.autotileService.drawTile(this.layer, point.x, point.y, tile.id); + Globals.autotileService.drawTile( + this.layer, + point.x, + point.y, + tile.id, + ); } } } } Vec2.assign(this.lastDraw, p); - } } - + protected deactivate() { this.container.visible = false; this.baseDrawer.setActive(false); } - + protected activate() { - this.addSubscription(Globals.mapLoaderService.selectedLayer.subscribe(layer => this.selectLayer(layer))); - this.addSubscription(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => this.selectedTiles = tiles)); + this.addSubscription( + Globals.mapLoaderService.selectedLayer.subscribe((layer) => + this.selectLayer(layer), + ), + ); + this.addSubscription( + Globals.phaserEventsService.changeSelectedTiles.subscribe( + (tiles) => (this.selectedTiles = tiles), + ), + ); this.baseDrawer.setActive(true); - + const fill = () => { if (!Helper.isInputFocused()) { this.fill(); } }; - this.addKeybinding({event: 'up', fun: fill, emitter: this.fillKey}); - - + this.addKeybinding({ event: 'up', fun: fill, emitter: this.fillKey }); + const leftUp = (pointer: Phaser.Input.Pointer) => { if (!pointer.leftButtonReleased()) { return; } - + if (!this.layer) { return; } - + this.lastDraw.x = -1; - + if (!this.dirty) { return; } this.dirty = false; - + Globals.stateHistoryService.saveState({ name: 'Tile Drawer', icon: 'create', }); }; - this.addKeybinding({event: 'pointerup', fun: leftUp, emitter: this.scene.input}); - this.addKeybinding({event: 'pointerupoutside', fun: leftUp, emitter: this.scene.input}); - + this.addKeybinding({ + event: 'pointerup', + fun: leftUp, + emitter: this.scene.input, + }); + this.addKeybinding({ + event: 'pointerupoutside', + fun: leftUp, + emitter: this.scene.input, + }); + const transparent = () => { if (!Helper.isInputFocused()) { this.renderLayersTransparent = !this.renderLayersTransparent; this.setLayerAlpha(); } }; - this.addKeybinding({event: 'up', fun: transparent, emitter: this.transparentKey}); - + this.addKeybinding({ + event: 'up', + fun: transparent, + emitter: this.transparentKey, + }); + const visible = () => { if (!Helper.isInputFocused()) { Globals.globalEventsService.toggleVisibility.next(); } }; - this.addKeybinding({event: 'up', fun: visible, emitter: this.visibilityKey}); + this.addKeybinding({ + event: 'up', + fun: visible, + emitter: this.visibilityKey, + }); } - + private setLayerAlpha() { const map = Globals.map; if (map) { - map.layers.forEach(layer => { + map.layers.forEach((layer) => { layer.alpha = this.renderLayersTransparent ? 0.5 : 1; }); if (this.layer) { @@ -187,22 +237,24 @@ export class TileDrawer extends BaseObject { } } } - + private fill() { if (!this.layer) { return; } - + const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); - + const p = Helper.worldToTile( + pointer.worldX - this.layer.x, + pointer.worldY - this.layer.y, + ); + if (this.selectedTiles.length > 0) { Filler.fill(this.layer, this.selectedTiles[0].id, p); Globals.stateHistoryService.saveState({ name: 'fill', - icon: 'format_color_fill' + icon: 'format_color_fill', }); } - } } diff --git a/webapp/src/app/services/phaser/vec2.ts b/webapp/src/app/services/phaser/vec2.ts index ef4d7ce0..59eb351c 100644 --- a/webapp/src/app/services/phaser/vec2.ts +++ b/webapp/src/app/services/phaser/vec2.ts @@ -3,40 +3,39 @@ import { Point } from '../../models/cross-code-map'; export class Vec2 { - public static create(otherVec?: Point) { - const res = {}; - res.x = (otherVec && otherVec.x || 0); - res.y = (otherVec && otherVec.y || 0); + const res = {} as Point; + res.x = (otherVec && otherVec.x) || 0; + res.y = (otherVec && otherVec.y) || 0; return res; } - + public static createC(x?: number, y?: number) { - const res = {}; - res.x = (x || 0); - res.y = (y || 0); + const res = {} as Point; + res.x = x || 0; + res.y = y || 0; return res; } - + public static assign(v1: Point, v2: Point) { - v1.x = (v2.x || 0); - v1.y = (v2.y || 0); + v1.x = v2.x || 0; + v1.y = v2.y || 0; return v1; } - + public static assignC(v: Point, x?: number, y?: number) { - v.x = (x || 0); - v.y = (y || 0); + v.x = x || 0; + v.y = y || 0; return v; } - + public static add(v1: Point, v2: Point, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) + (v2.x || 0); res.y = (v1.y || 0) + (v2.y || 0); return res; } - + public static addC(v1: Point, x?: number, y?: number, copy?: boolean) { const res: any = copy || false ? {} : v1; y = y === undefined || y === null ? x : y; @@ -44,14 +43,14 @@ export class Vec2 { res.y = (v1.y || 0) + (y || 0); return res; } - + public static sub(v1: Point, v2: Point, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) - (v2.x || 0); res.y = (v1.y || 0) - (v2.y || 0); return res; } - + public static subC(v1: Point, x: number, y: number, copy?: boolean) { const res: any = copy ? {} : v1; y = y === undefined || y === null ? x : y; @@ -59,14 +58,14 @@ export class Vec2 { res.y = (v1.y || 0) - (y || 0); return res; } - + public static mul(v1: Point, v2: Point, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) * (v2.x || 0); res.y = (v1.y || 0) * (v2.y || 0); return res; } - + public static mulC(v1: Point, x?: number, y?: number, copy?: boolean) { const res: any = copy || false ? {} : v1; y = y === undefined || y === null ? x : y; @@ -74,21 +73,21 @@ export class Vec2 { res.y = (v1.y || 0) * (y || 0); return res; } - + public static mulF(v1: Point, f: number, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) * (f || 0); res.y = (v1.y || 0) * (f || 0); return res; } - + public static div(v1: Point, v2: Point, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) / (v2.x || 0); res.y = (v1.y || 0) / (v2.y || 0); return res; } - + public static divC(v1: Point, x?: number, y?: number, copy?: boolean) { const res: any = copy || false ? {} : v1; y = y === undefined || y === null ? x : y; @@ -96,24 +95,31 @@ export class Vec2 { res.y = (v1.y || 0) / (y || 0); return res; } - + public static dot(v1: Point, v2: Point) { return (v1.x || 0) * (v2.x || 0) + (v1.y || 0) * (v2.y || 0); } - + public static dotR(v1: Point, v2: Point) { return -(v1.y || 0) * (v2.x || 0) + (v1.x || 0) * (v2.y || 0); } - + public static vlength(v: Point, newLength?: number, copy?: boolean) { - const oldLength = Math.sqrt((v.x || 0) * (v.x || 0) + (v.y || 0) * (v.y || 0)); + const oldLength = Math.sqrt( + (v.x || 0) * (v.x || 0) + (v.y || 0) * (v.y || 0), + ); if (newLength) { - return Vec2.mulC(v, oldLength ? newLength / oldLength : 1, undefined, copy); + return Vec2.mulC( + v, + oldLength ? newLength / oldLength : 1, + undefined, + copy, + ); } else { return oldLength; } } - + public static limit(v: Point, min: number, max: number, copy?: boolean) { const length = Vec2.vlength(v); if (length > max) { @@ -124,11 +130,11 @@ export class Vec2 { return copy || false ? Vec2.create(v) : v; } } - + public static normalize(v: Point, copy?: boolean) { return Vec2.vlength(v, 1, copy); } - + public static clockangle(v: Point) { let result = Math.acos(-(v.y || 0) / Vec2.vlength(v)); if (v.x < 0) { @@ -136,12 +142,14 @@ export class Vec2 { } return result || 0; } - + public static angle(v1: Point, v2: Point) { - const result = Math.acos(Vec2.dot(v1, v2) / (Vec2.vlength(v1) * Vec2.vlength(v2))); + const result = Math.acos( + Vec2.dot(v1, v2) / (Vec2.vlength(v1) * Vec2.vlength(v2)), + ); return result || 0; } - + public static rotate(v: Point, angle: number, copy?: boolean) { const res: any = copy || false ? {} : v; const x = v.x || 0; @@ -149,46 +157,46 @@ export class Vec2 { res.y = Math.sin(-angle) * x + Math.cos(angle) * (v.y || 0); return res; } - + public static rotate90CW(v: Point, copy?: boolean) { const res: any = copy || false ? {} : v; - const x = (v.x || 0); - res.x = (v.y || 0); + const x = v.x || 0; + res.x = v.y || 0; res.y = -x; return res; } - + public static rotate90CCW(v: Point, copy?: boolean) { const res: any = copy || false ? {} : v; - const x = (v.x || 0); + const x = v.x || 0; res.x = -(v.y || 0); res.y = x; return res; } - + public static flip(v: Point, copy?: boolean) { const res: any = copy || false ? {} : v; res.x = -v.x; res.y = -v.y; return res; } - + public static equal(v1: Point, v2: Point) { return v1.x === v2.x && v1.y === v2.y; } - + public static distance(v1: Point, v2: Point) { - const x = ((v1.x - v2.x) || 0); - const y = ((v1.y - v2.y) || 0); + const x = v1.x - v2.x || 0; + const y = v1.y - v2.y || 0; return Math.sqrt(x * x + y * y); } - + public static distance2(v1: Point, v2: Point) { - const x = ((v1.x - v2.x) || 0); - const y = ((v1.y - v2.y) || 0); + const x = v1.x - v2.x || 0; + const y = v1.y - v2.y || 0; return x * x + y * y; } - + public static lerp(v1: Point, v2: Point, i: number, copy?: boolean) { const res: any = copy || false ? {} : v1; res.x = (v1.x || 0) * (1 - i) + (v2.x || 0) * i; diff --git a/webapp/src/app/services/save.service.ts b/webapp/src/app/services/save.service.ts index c8dbd03b..947fb515 100644 --- a/webapp/src/app/services/save.service.ts +++ b/webapp/src/app/services/save.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { EventManager } from '@angular/platform-browser'; import { GlobalEventsService } from './global-events.service'; @@ -8,35 +8,39 @@ import { Helper } from './phaser/helper'; import { CCMap } from './phaser/tilemap/cc-map'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SaveService { + private http = inject(HttpClientService); + private snackbar = inject(MatSnackBar); + private readonly eventsService = inject(GlobalEventsService); - constructor( - private http: HttpClientService, - private snackbar: MatSnackBar, - mapLoader: MapLoaderService, - eventManager: EventManager, - private readonly eventsService: GlobalEventsService, - ) { - eventManager.addEventListener(document as any, 'keydown', (event: KeyboardEvent) => { - if (Helper.isInputFocused()) { - return; - } + constructor() { + const mapLoader = inject(MapLoaderService); + const eventManager = inject(EventManager); - if (event.ctrlKey && event.key.toLowerCase() === 's') { - event.preventDefault(); - const map = mapLoader.tileMap.getValue(); - if (!map) { + eventManager.addEventListener( + document as any, + 'keydown', + (event: KeyboardEvent) => { + if (Helper.isInputFocused()) { return; } - if (event.shiftKey) { - this.saveMapAs(map); - } else { - this.saveMap(map); + + if (event.ctrlKey && event.key.toLowerCase() === 's') { + event.preventDefault(); + const map = mapLoader.tileMap.getValue(); + if (!map) { + return; + } + if (event.shiftKey) { + this.saveMapAs(map); + } else { + this.saveMap(map); + } } - } - }); + }, + ); } saveMap(map: CCMap) { @@ -45,19 +49,26 @@ export class SaveService { return this.saveMapAs(map); } this.http.saveFile(map.path, this.generateMapJson(map)).subscribe({ - next: msg => { + next: (msg) => { console.log(msg); this.eventsService.hasUnsavedChanges.next(false); - this.snackbar.open('successfully saved map', 'ok', { duration: 3000 }); - }, error: err => { + this.snackbar.open('successfully saved map', 'ok', { + duration: 3000, + }); + }, + error: (err) => { console.error(err); - this.snackbar.open('failed to save map', 'ok', {panelClass: 'snackbar-error'}); - } + this.snackbar.open('failed to save map', 'ok', { + panelClass: 'snackbar-error', + }); + }, }); } saveMapAs(map: CCMap) { - const file = new Blob([this.generateMapJson(map)], { type: 'application/json' }); + const file = new Blob([this.generateMapJson(map)], { + type: 'application/json', + }); const a = document.createElement('a'), url = URL.createObjectURL(file); a.href = url; diff --git a/webapp/src/app/services/search-filter.service.ts b/webapp/src/app/services/search-filter.service.ts index d91159a5..73f2c610 100644 --- a/webapp/src/app/services/search-filter.service.ts +++ b/webapp/src/app/services/search-filter.service.ts @@ -5,28 +5,31 @@ import { Injectable } from '@angular/core'; }) export class SearchFilterService { NEUTRAL_CHAR_REGEX = /[-_\s]*/g; - - private escapeRegExp(text: string) { //https://stackoverflow.com/a/6969486 + + private escapeRegExp(text: string) { + //https://stackoverflow.com/a/6969486 return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } - + private innerTest(tested: string, searched: RegExp) { const result = tested.match(searched) != null; return result; } - + createSearcherRegex(searched: string, global = false) { //Prevents from returning /[-_\s]*/g for strings like '_', which would always match and cause problems with highlighting. if (searched.replace(this.NEUTRAL_CHAR_REGEX, '') === '') { return /[-_\s]+/g; } - + const characters = searched.split(this.NEUTRAL_CHAR_REGEX); - const escapedCharacters = characters.map(token => this.escapeRegExp(token)); + const escapedCharacters = characters.map((token) => + this.escapeRegExp(token), + ); const regex = escapedCharacters.join(this.NEUTRAL_CHAR_REGEX.source); return new RegExp(regex, global ? 'gi' : 'i'); } - + test(tested: string, searched?: string | null) { searched = searched?.trim(); if (!searched) { @@ -35,18 +38,22 @@ export class SearchFilterService { return this.innerTest(tested, this.createSearcherRegex(searched)); } } - + filterOptions(options: readonly string[], searched?: string | null) { searched = searched?.trim(); if (!searched) { return [...options]; //By returning a copy it's always safe to modify the result. } else { const regex = this.createSearcherRegex(searched); - const filtered = options.filter(tested => this.innerTest(tested, regex)); - + const filtered = options.filter((tested) => + this.innerTest(tested, regex), + ); + // move exact match to first position const lowercase = searched.toLowerCase(); - const exactIndex = filtered.findIndex(v => v.toLowerCase() === lowercase); + const exactIndex = filtered.findIndex( + (v) => v.toLowerCase() === lowercase, + ); if (exactIndex > 0) { const exact = filtered.splice(exactIndex, 1)[0]; filtered.unshift(exact); diff --git a/webapp/src/app/services/settings.service.ts b/webapp/src/app/services/settings.service.ts index 36fa95a7..447c5d37 100644 --- a/webapp/src/app/services/settings.service.ts +++ b/webapp/src/app/services/settings.service.ts @@ -7,29 +7,37 @@ export interface AppSettings { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SettingsService { - private settings: AppSettings = { - wrapEventEditorLines: this.loadBooleanOrDefault('wrapEventEditorLines', true), - includeVanillaMaps: this.loadBooleanOrDefault('includeVanillaMaps', false), + wrapEventEditorLines: this.loadBooleanOrDefault( + 'wrapEventEditorLines', + true, + ), + includeVanillaMaps: this.loadBooleanOrDefault( + 'includeVanillaMaps', + false, + ), selectionBoxDark: this.loadBooleanOrDefault('selectionBoxDark', true), }; - + getSettings(): Readonly { return this.settings; } - - private loadBooleanOrDefault(key: keyof AppSettings, defaultValue: boolean): boolean { + + private loadBooleanOrDefault( + key: keyof AppSettings, + defaultValue: boolean, + ): boolean { const loadedValue = localStorage.getItem(key); - return loadedValue === null ? defaultValue : (loadedValue === 'true'); + return loadedValue === null ? defaultValue : loadedValue === 'true'; } - + public updateSettings(newSettings: Partial) { this.settings = { ...this.settings, - ...newSettings + ...newSettings, }; for (const [key, value] of Object.entries(this.settings)) { localStorage.setItem(key, (value as boolean).toString()); diff --git a/webapp/src/app/services/shared-service.ts b/webapp/src/app/services/shared-service.ts index ec288bc0..c0c561e8 100644 --- a/webapp/src/app/services/shared-service.ts +++ b/webapp/src/app/services/shared-service.ts @@ -1,6 +1,6 @@ export interface SharedService { - saveModSelect(mod: string): Promise; - getSelectedMod(): string; - - relaunch(): void; + saveModSelect(mod: string): Promise; + getSelectedMod(): string; + + relaunch(): void; } diff --git a/webapp/src/app/tests/entities.spec.ts b/webapp/src/app/tests/entities.spec.ts index cf4da94a..2ce87a54 100644 --- a/webapp/src/app/tests/entities.spec.ts +++ b/webapp/src/app/tests/entities.spec.ts @@ -1,4 +1,7 @@ -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { + provideHttpClient, + withInterceptorsFromDi, +} from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppModule } from '../app.module'; @@ -12,85 +15,93 @@ import { MapLoaderService } from '../services/map-loader.service'; import { TestHelper } from './test-helper'; class SimpleServiceMock { - init() { - } + init() {} } describe('Entities', () => { let component: PhaserComponent; let fixture: ComponentFixture; - - beforeEach(() => TestBed.configureTestingModule({ - declarations: [PhaserComponent], - imports: [AppModule], - providers: [ - {provide: AutotileService, useValue: new SimpleServiceMock()}, - {provide: HeightMapService, useValue: new SimpleServiceMock()}, - StateHistoryService, - provideHttpClient(withInterceptorsFromDi()) - ] - }).compileComponents()); - + + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [PhaserComponent], + imports: [AppModule], + providers: [ + { provide: AutotileService, useValue: new SimpleServiceMock() }, + { + provide: HeightMapService, + useValue: new SimpleServiceMock(), + }, + StateHistoryService, + provideHttpClient(withInterceptorsFromDi()), + ], + }).compileComponents(), + ); + beforeEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - + beforeEach(() => { fixture = TestBed.createComponent(PhaserComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - + it('make new npc and export map', async () => { const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); const res = await TestHelper.loadMap(service, http, 'autumn/entrance'); const map = res.ccmap; const exported1 = map.exportMap(); - + const npc = makeNewNpc(); const e = await map.entityManager.generateNewEntity(npc); expect(e).toBeDefined(); const exported2 = map.exportMap(); - - expect(exported2.entities.length - exported1.entities.length).toBe(1, 'exported map should have 1 additional entity'); + + expect(exported2.entities.length - exported1.entities.length).toBe( + 1, + 'exported map should have 1 additional entity', + ); }); - - + it('entities should have a unique mapId (instanceof Number)', async () => { const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); const res = await TestHelper.loadMap(service, http, 'autumn/entrance'); const map = res.ccmap; - + await map.entityManager.generateNewEntity(makeNewNpc()); await map.entityManager.generateNewEntity(makeNewNpc()); await map.entityManager.generateNewEntity(makeNewNpc()); const exported = map.exportMap(); checkMapId(exported); }); - + it('copy paste entity', async () => { const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); const res = await TestHelper.loadMap(service, http, 'autumn/entrance'); const map = res.ccmap; - + map.entityManager.setActive(true); - + const entity = await map.entityManager.generateNewEntity(makeNewNpc()); const exported1 = map.exportMap(); - + map.entityManager.selectEntity(entity, false); - map.entityManager.copy(); + await map.entityManager.copy(); await map.entityManager.paste(); - + const exported2 = map.exportMap(); - - expect(exported2.entities.length - exported1.entities.length).toBe(1, 'copy paste should add 1 entity'); + + expect(exported2.entities.length - exported1.entities.length).toBe( + 1, + 'copy paste should add 1 entity', + ); checkMapId(exported2); }); - }); function makeNewNpc() { @@ -99,18 +110,21 @@ function makeNewNpc() { y: 221, type: 'NPC', level: 0, - settings: {} + settings: {}, }; return npc; } function checkMapId(exported: CrossCodeMap) { - const ids = exported.entities.map(e => e.settings.mapId); + const ids = exported.entities.map((e) => e.settings.mapId); ids.sort((a, b) => (a ?? -9) - (b ?? -9)); console.log(ids); for (const id of ids) { expect(id).toBeInstanceOf(Number); } - - expect(new Set(ids).size).toBe(exported.entities.length, 'mapId should be unique for every entity'); + + expect(new Set(ids).size).toBe( + exported.entities.length, + 'mapId should be unique for every entity', + ); } diff --git a/webapp/src/app/tests/layers.spec.ts b/webapp/src/app/tests/layers.spec.ts index df9900fe..4a0da9cc 100644 --- a/webapp/src/app/tests/layers.spec.ts +++ b/webapp/src/app/tests/layers.spec.ts @@ -1,4 +1,7 @@ -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { + provideHttpClient, + withInterceptorsFromDi, +} from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { AppModule } from '../app.module'; @@ -14,58 +17,66 @@ import { MapLoaderService } from '../services/map-loader.service'; import { TestHelper } from './test-helper'; class SimpleServiceMock { - init() { - } + init() {} } describe('Layers', () => { let component: PhaserComponent; let fixture: ComponentFixture; - - beforeEach(() => TestBed.configureTestingModule({ - declarations: [PhaserComponent, LayersComponent], - imports: [NoopAnimationsModule, AppModule, MaterialModule], - providers: [ - {provide: AutotileService, useValue: new SimpleServiceMock()}, - {provide: HeightMapService, useValue: new SimpleServiceMock()}, - StateHistoryService, - provideHttpClient(withInterceptorsFromDi()) - ] - }).compileComponents()); - + + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [PhaserComponent, LayersComponent], + imports: [NoopAnimationsModule, AppModule, MaterialModule], + providers: [ + { provide: AutotileService, useValue: new SimpleServiceMock() }, + { + provide: HeightMapService, + useValue: new SimpleServiceMock(), + }, + StateHistoryService, + provideHttpClient(withInterceptorsFromDi()), + ], + }).compileComponents(), + ); + beforeEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - + beforeEach(() => { fixture = TestBed.createComponent(PhaserComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - + it('add new layer with all tiles index 0', async () => { const fixture = TestBed.createComponent(LayersComponent); const comp = fixture.componentInstance; fixture.detectChanges(); - + const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); const res = await TestHelper.loadMap(service, http, 'autumn/entrance'); const map = res.ccmap; const export1 = map.exportMap(); - + await comp.addNewLayer(); - + const export2 = map.exportMap(); const data = export2.layer[export2.layer.length - 1].data; - - expect(export2.layer.length - export1.layer.length).toBe(1, 'should add new layer'); - + + expect(export2.layer.length - export1.layer.length).toBe( + 1, + 'should add new layer', + ); + const ids = new Set(data.flat()); console.log(Array.from(ids)); expect(ids.size).toBe(1); - expect(ids.values().next().value).toBe(0, 'should initialize all tiles with 0'); - + expect(ids.values().next().value).toBe( + 0, + 'should initialize all tiles with 0', + ); }); - }); diff --git a/webapp/src/app/tests/map-loading.spec.ts b/webapp/src/app/tests/map-loading.spec.ts index 5af82a14..eff83d90 100644 --- a/webapp/src/app/tests/map-loading.spec.ts +++ b/webapp/src/app/tests/map-loading.spec.ts @@ -1,4 +1,7 @@ -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { + provideHttpClient, + withInterceptorsFromDi, +} from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { firstValueFrom } from 'rxjs'; import { AppModule } from '../app.module'; @@ -12,59 +15,66 @@ import { MapLoaderService } from '../services/map-loader.service'; import { TestHelper } from './test-helper'; class SimpleServiceMock { - init() { - } + init() {} } // TODO: fix map loading, order of entities doesn't matter describe('Map Loading', () => { let component: PhaserComponent; let fixture: ComponentFixture; - - beforeEach(() => TestBed.configureTestingModule({ - declarations: [PhaserComponent], - imports: [AppModule], - providers: [ - {provide: AutotileService, useValue: new SimpleServiceMock()}, - {provide: HeightMapService, useValue: new SimpleServiceMock()}, - StateHistoryService, - provideHttpClient(withInterceptorsFromDi()) - ] - }).compileComponents()); - + + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [PhaserComponent], + imports: [AppModule], + providers: [ + { provide: AutotileService, useValue: new SimpleServiceMock() }, + { + provide: HeightMapService, + useValue: new SimpleServiceMock(), + }, + StateHistoryService, + provideHttpClient(withInterceptorsFromDi()), + ], + }).compileComponents(), + ); + beforeEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - + beforeEach(() => { fixture = TestBed.createComponent(PhaserComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - + it('should create phaser component', () => { expect(component).toBeTruthy(); }); - + it('should export same map as imported (autumn/entrance)', async () => { const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); const output = await loadMap(service, http, 'autumn/entrance'); expect(output.exported).toEqual(output.imported); }); - + it('should export same map as imported (lots of maps)', async () => { - const service: MapLoaderService = TestBed.inject(MapLoaderService); const http: HttpClientService = TestBed.inject(HttpClientService); - + const paths = await firstValueFrom(http.getMaps()); - - const autumn = paths.filter(p => p.startsWith('autumn.')).slice(0, 5); - const arid = paths.filter(p => p.startsWith('arid.')).slice(0, 5); - const rhombus = paths.filter(p => p.startsWith('rhombus-sqr.')).slice(0, 5); - const shock = paths.filter(p => p.startsWith('shock-dng.')).slice(0, 5); - + + const autumn = paths.filter((p) => p.startsWith('autumn.')).slice(0, 5); + const arid = paths.filter((p) => p.startsWith('arid.')).slice(0, 5); + const rhombus = paths + .filter((p) => p.startsWith('rhombus-sqr.')) + .slice(0, 5); + const shock = paths + .filter((p) => p.startsWith('shock-dng.')) + .slice(0, 5); + const toTest = [...autumn, ...arid, ...rhombus, ...shock]; expect(toTest.length).toBeGreaterThan(10); for (const mapName of toTest) { @@ -74,11 +84,14 @@ describe('Map Loading', () => { }); }); -async function loadMap(service: MapLoaderService, http: HttpClientService, mapName: string): Promise<{ imported: any, exported: any }> { +async function loadMap( + service: MapLoaderService, + http: HttpClientService, + mapName: string, +): Promise<{ imported: any; exported: any }> { const res = await TestHelper.loadMap(service, http, mapName); const exported = res.ccmap.exportMap(); delete exported.path; delete exported.filename; - return {imported: res.imported, exported: exported}; - + return { imported: res.imported, exported: exported }; } diff --git a/webapp/src/app/tests/test-helper.ts b/webapp/src/app/tests/test-helper.ts index d9be329c..c6aa8d66 100644 --- a/webapp/src/app/tests/test-helper.ts +++ b/webapp/src/app/tests/test-helper.ts @@ -2,34 +2,46 @@ import { Subscription } from 'rxjs'; import { CrossCodeMap } from '../models/cross-code-map'; import { HttpClientService } from '../services/http-client.service'; import { MapLoaderService } from '../services/map-loader.service'; -import { BasePath, FileExtension, PathResolver } from '../services/path-resolver'; +import { + BasePath, + FileExtension, + PathResolver, +} from '../services/path-resolver'; import { CCMap } from '../services/phaser/tilemap/cc-map'; export class TestHelper { // TODO: shouldn't be used because it relies on backend and original crosscode maps. Instead make custom maps for testing - public static async loadMap(service: MapLoaderService, http: HttpClientService, mapName: string): Promise<{ imported: CrossCodeMap, ccmap: CCMap }> { - return new Promise(((res) => { + public static async loadMap( + service: MapLoaderService, + http: HttpClientService, + mapName: string, + ): Promise<{ imported: CrossCodeMap; ccmap: CCMap }> { + return new Promise((res) => { let imported: CrossCodeMap; let sub: Subscription; // eslint-disable-next-line prefer-const - sub = service.tileMap.subscribe(map => { + sub = service.tileMap.subscribe((map) => { if (!map) { return; } - + // TODO: sometimes undefined sub!.unsubscribe(); - res({imported: imported, ccmap: map}); + res({ imported: imported, ccmap: map }); }); - + console.log('load by name: ' + mapName); - - const path = PathResolver.convertToPath(BasePath.MAPS, mapName, FileExtension.JSON); - - http.getAssetsFile(path).subscribe(map => { + + const path = PathResolver.convertToPath( + BasePath.MAPS, + mapName, + FileExtension.JSON, + ); + + http.getAssetsFile(path).subscribe((map) => { imported = JSON.parse(JSON.stringify(map)); service.loadRawMap(map, mapName, path); }); - })); + }); } } diff --git a/webapp/src/environments/environment.prod.ts b/webapp/src/environments/environment.prod.ts index ef040fc8..3cf93ef1 100644 --- a/webapp/src/environments/environment.prod.ts +++ b/webapp/src/environments/environment.prod.ts @@ -1,4 +1,6 @@ +import pkg from '../../package.json'; + export const environment = { production: true, - version: require('../../package.json').version + version: pkg.version, }; diff --git a/webapp/src/environments/environment.ts b/webapp/src/environments/environment.ts index 883fc9eb..b39406d1 100644 --- a/webapp/src/environments/environment.ts +++ b/webapp/src/environments/environment.ts @@ -3,7 +3,9 @@ // `ng build --env=prod` then `environment.prod.ts` will be used instead. // The list of which env maps to which file can be found in `.angular-cli.json`. +import pkg from '../../package.json'; + export const environment = { production: false, - version: require('../../package.json').version + version: pkg.version, }; diff --git a/webapp/src/main.ts b/webapp/src/main.ts index 25b36ae5..811c5026 100644 --- a/webapp/src/main.ts +++ b/webapp/src/main.ts @@ -12,8 +12,11 @@ if (environment.production) { enableProdMode(); } -// @ts-ignore -if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) { +if ( + typeof process !== 'undefined' && + typeof process.versions === 'object' && + !!process.versions.electron +) { Globals.isElectron = true; } console.log('is electron: ', Globals.isElectron); @@ -21,5 +24,5 @@ console.log('is electron: ', Globals.isElectron); (async () => { await ElectronService.init(); await BrowserService.init(); - platformBrowserDynamic().bootstrapModule(AppModule); -})(); + await platformBrowserDynamic().bootstrapModule(AppModule); +})().catch((err) => console.error(err)); diff --git a/webapp/src/polyfills.ts b/webapp/src/polyfills.ts deleted file mode 100644 index 9058db7e..00000000 --- a/webapp/src/polyfills.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/*************************************************************************************************** - * Zone JS is required by Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ - -/** - * Date, currency, decimal and percent pipes. - * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 - */ -// import 'intl'; // Run `npm install --save intl`. -/** - * Need to import at least one locale-data with intl. - */ -// import 'intl/locale-data/jsonp/en'; - -(window as any).global = window; -// Phaser 3 dist build attaches API to the global window object. -import 'phaser/dist/phaser'; - diff --git a/webapp/src/test.ts b/webapp/src/test.ts index 0b99b20b..7b807aac 100644 --- a/webapp/src/test.ts +++ b/webapp/src/test.ts @@ -4,7 +4,7 @@ import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, - platformBrowserDynamicTesting + platformBrowserDynamicTesting, } from '@angular/platform-browser-dynamic/testing'; declare const require: any; @@ -12,7 +12,7 @@ declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting() + platformBrowserDynamicTesting(), ); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); diff --git a/webapp/src/typings.d.ts b/webapp/src/typings.d.ts index 771c6bec..cfd7aebd 100644 --- a/webapp/src/typings.d.ts +++ b/webapp/src/typings.d.ts @@ -2,5 +2,3 @@ declare module '*.json' { const value: any; export default value; } - -declare interface ActiveXObject {} diff --git a/webapp/tailwind.config.js b/webapp/tailwind.config.js index 75867539..e4a6f796 100644 --- a/webapp/tailwind.config.js +++ b/webapp/tailwind.config.js @@ -1,11 +1,8 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - "./src/**/*.{html,ts}", - ], - theme: { - extend: {}, - }, - plugins: [], -} - + content: ['./src/**/*.{html,ts}'], + theme: { + extend: {}, + }, + plugins: [], +};